phparraysmultidimensional-arraygroupingmerging-data

Group 2d array data by one column, and group, filter, sort comma-separated values in other columns per group


I have some PHP variable sets from which I'm making a multidimensional array. Now, in that array, I want to check for a particular key ([font]) for duplicates.

If duplicates found, the corresponding and respective values for [lang] and [weight] should merge.

Here is what I've tried so far (this unsets/removes the duplicate value from the array):

// Font [0]
$font_1 = "Poppins";
$font_1_l = "Hindi, English";
$font_1_w = "700, 700i";

// Font [1]
$font_2 = "Lora";
$font_2_l = "Vietnamese, Japanese";
$font_2_w = "200, 300, 400, 400i";

// Font [2]
$font_3 = "Noto Sans";
$font_3_l = "Punjabi, Latin, Hindi";
$font_3_w = "200, 200i, 300, 300i, 400, 500";

// Font [3]
$font_4 = "Lora";
$font_4_l = "Greek, Roman, Vietnamese";
$font_4_w = "400, 400i, 500, 500b";

// Array of all the values
$font_f = array( array( 'font' => $font_1, 'lang' => $font_1_l, 'weight' => $font_1_w ), array( 'font' => $font_2, 'lang' => $font_2_l, 'weight' => $font_2_w ), array( 'font' => $font_3, 'lang' => $font_3_l, 'weight' => $font_3_w ), array( 'font' => $font_4, 'lang' => $font_4_l, 'weight' => $font_4_w ) ); 

// Printing the array for testing
echo "<pre>";
print_r( array_map("unserialize", array_unique(array_map("serialize", $font_f))) );

// Removing duplicates
$font_f_copy = $font_f; // Copy of $font_f for modification
$fonts = array(); // To get unique fonts

for( $i=0; $i<count($font_f); $i++ ) {
  if ( in_array( $font_f[$i]['font'], $fonts ) ) {
    unset($font_f_copy[$i]);
  }
  else {
    $fonts[] = $font_f[$i]['font'];
  }
}

// Printing $font_f_copy for testing
print_r($font_f_copy);

Output:

Array
(
    [0] => Array
        (
            [font] => Poppins
            [lang] => Hindi, English
            [weight] => 700, 700i
        )

    [1] => Array
        (
            [font] => Lora
            [lang] => Vietnamese, Japanese
            [weight] => 200, 300, 400, 400i
        )

    [2] => Array
        (
            [font] => Noto Sans
            [lang] => Punjabi, Latin, Hindi
            [weight] => 200, 200i, 300, 300i, 400, 500
        )

    [3] => Array
        (
            [font] => Lora
            [lang] => Greek, Roman, Vietnamese
            [weight] => 400, 400i, 500, 500b
        )

)
Array
(
    [0] => Array
        (
            [font] => Poppins
            [lang] => Hindi, English
            [weight] => 700, 700i
        )

    [1] => Array
        (
            [font] => Lora
            [lang] => Vietnamese, Japanese
            [weight] => 200, 300, 400, 400i
        )

    [2] => Array
        (
            [font] => Noto Sans
            [lang] => Punjabi, Latin, Hindi
            [weight] => 200, 200i, 300, 300i, 400, 500
        )

)

As you can see in the code above, Font [1] and Font [3] will have the same value for [font] i.e. Lora, so the [lang] and [weight] for Font [1] should merge with [lang] and [weight] for Font [3] respectively.

I'm wondering how to proceed to achieve that.


Solution

  • Here is a real workhorse: https://3v4l.org/qBRjb

    Starting from $font_f this will:

    1. Concatenate lang and weight values on duplicate fonts in each subarray.
    2. Remove duplicate values in the lang and weight CSV strings.
    3. Lexicographically (not naturally) sort ASC the unique lang and weight values.
    4. Sort the subarrays by their key / font name.
    5. Print the grouped data without the temporary parent keys.
    $font_f = [
        ['font' => 'Poppins', 'lang' => 'Hindi, English', 'weight' => '700, 700i'],
        ['font' => 'Lora', 'lang' => 'Vietnamese, Japanese', 'weight' => '200, 300, 400, 400i'],
        ['font' => 'Noto Sans', 'lang' => 'Punjabi, Latin, Hindi', 'weight' => '200, 200i, 300, 300i, 400, 500'],
        ['font' => 'Lora', 'lang' => 'Greek, Roman, Vietnamese', 'weight' => '400, 400i, 500, 500b'],
    ];
    
    $result = [];
    foreach ($font_f as $row) {
        $f = $row['font'];
        if (!isset($result[$f])) {
            $result[$f] = $row;
            continue;
        }
        // append, split, merge, de-duplicate, sort, join
        foreach(["lang", "weight"] as $col) {
            $result[$f][$col] .= ', ' . $row[$col];
            $result[$f][$col] = array_unique(explode(', ', trim($result[$f][$col], ', ')));
            sort($result[$f][$col]);
            $result[$f][$col] = implode(', ', $result[$f][$col]);
        }
    }
    ksort($result);                    // sort rows by font name
    var_export(array_values($result)); // print without temporary grouping keys
    

    Output:

    Array(
        [0] => Array(
            [font] => Lora
            [lang] => Greek, Japanese, Roman, Vietnamese
            [weight] => 200, 300, 400, 400i, 500, 500b
        ),    
        [1] => Array(
            [font] => Noto Sans
            [lang] => Hindi, Latin, Punjabi
            [weight] => 200, 200i, 300, 300i, 400, 500
        ),
        [2] => Array(
            [font] => Poppins
            [lang] => English, Hindi
            [weight] => 700, 700i
        )
    )