Rahul
Rahul

Reputation: 842

PHP: Merging adjacent values if duplicates found in a multidimensional array

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.

Upvotes: 2

Views: 293

Answers (2)

mickmackusa
mickmackusa

Reputation: 47991

Here is a real workhorse:

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. Sort ASC the unique lang and weight values.
  4. Sort the subarrays by their key / font name.
  5. Replace the associative subarray keys with numeric indices.

Here is the code:

$array=[];
foreach($font_f as $a){
    $array[$a["font"]]["font"]=$a["font"];                      // declare or lazy overwrite if duplicate
    foreach(array("lang","weight") as $col){                    // cycle through the concat-able elements
        if(isset($array[$a["font"]][$col])){
            $temp_str=$array[$a["font"]][$col].", ".$a[$col];   // concat
        }else{
            $temp_str=$a[$col];                                 // declare
        }
        $temp_arr=array_unique(explode(', ',$temp_str));        // split & remove duplicates
        sort($temp_arr);                                        // sort
        $array[$a["font"]][$col]=implode(', ',$temp_arr);       // rejoin
    }
}
ksort($array);                    // sort subarrays by font name
$array=array_values($array);      // replace temporary associative keys with indices

echo "<pre>";
print_r($array);
echo "</pre>";

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
    )
)

Upvotes: 1

apokryfos
apokryfos

Reputation: 40690

Instead of the for loop I'd personally I'd do something like:

$result = [];
foreach ($font_f as $f) {
     if (isset($result[$f["font"]])) {
         $result[$f["font"]] = [ 
               "font" => $f["font"], 
               "lang" => $f["lang"]." ,".$result[$f["font"]]["lang"],
               "weight" => $f["weight"]." ,".$result[$f["font"]]["weight"]
         ]; 
     } else {
        $result[$f["font"]] = $f;
     }
}

Upvotes: 2

Related Questions