<?php $rows[] = [ "widget_id" => "widget1", "size" => "large", "item" => [ "item_id" => "item1", "shape" => "circle", "paint" => [ "paint_id" => "paint1", "colour" => "red", ] ] ]; # Exactly the same as above, except the "paint" child array is different $rows[] = [ "widget_id" => "widget1", "size" => "large", "item" => [ "item_id" => "item1", "shape" => "circle", "paint" => [ "paint_id" => "paint2", "colour" => "green", ] ] ]; # Same children ("item" and "paint") as the first row, but different parents ("widget_id" is different) $rows[] = [ "widget_id" => "widget2", "size" => "medium", "item" => [ "item_id" => "item1", "shape" => "circle", "paint" => [ "paint_id" => "paint1", "colour" => "red", ] ] ]; class ComplexMerge{ /** * Checks to see whether an array has sequential numerical keys (only), * starting from 0 to n, where n is the array count minus one. * * @link https://codereview.stackexchange.com/questions/201/is-numeric-array-is-missing/204 * * @param $arr * * @return bool */ private static function isNumericArray($arr) { if(!is_array($arr)){ return false; } return array_keys($arr) === range(0, (count($arr) - 1)); } /** * Given an array, separate out * array values that themselves are arrays * and those that are not. * * @param array $array * * @return array[] */ private static function separateOutArrayValues(array $array): array { $valuesThatAreArrays = []; $valuesThatAreNotArrays = []; foreach($array as $key => $val){ if(is_array($val)){ $valuesThatAreArrays[$key] = $val; } else { $valuesThatAreNotArrays[$key] = $val; } } return [$valuesThatAreArrays, $valuesThatAreNotArrays]; } /** * Groups row keys together that have the same non-array values. * If every row is already unique, returns NULL. * * @param $array * * @return array|null */ private static function groupRowKeysWithSameNonArrayValues($array): ?array { foreach($array as $key => $row){ # Separate out the values that are arrays and those that are not [$a, $v] = self::separateOutArrayValues($row); # Serialise the values that are not arrays and create a unique ID from them $uniqueRowId = md5(serialize($v)); # Store all the original array keys under the unique ID $deduplicatedArray[$uniqueRowId][] = $key; } # If every row is unique, there are no more rows to combine, and our work is done if(!$a && count($array) == count($deduplicatedArray)){ return NULL; } return $deduplicatedArray; } private static function mergeRows(array $array): array { # Get the grouped row keys if(!$groupedRowKeys = self::groupRowKeysWithSameNonArrayValues($array)){ //If there are no more rows to merge return $array; } foreach($groupedRowKeys as $uniqueRowId => $keys){ foreach($keys as $id => $key){ # Separate out the values that are arrays and those that are not [$valuesThatAreArrays, $valuesThatAreNotArrays] = self::separateOutArrayValues($array[$key]); //We're using the key from the grouped row keys array, but using it on the original array # If this is the first row from the group, throw in the non-array values if(!$id){ $unique[$uniqueRowId] = $valuesThatAreNotArrays; } # For each of the values that are arrays include them back in foreach($valuesThatAreArrays as $k => $childArray){ $unique[$uniqueRowId][$k][] = $childArray; //Wrap them in a numerical key array so that only children and siblings are have the same parent-child relationship } } } # Go deeper foreach($unique as $key => $val){ foreach($val as $k => $valuesThatAreNotArrays){ if(self::isNumericArray($valuesThatAreNotArrays)){ $unique[$key][$k] = self::mergeRows($unique[$key][$k]); } } } # No need to include the unique row IDs return array_values($unique); } public static function normalise($array): ?array { $array = self::mergeRows($array); return $array; } } $array = ComplexMerge::normalise($rows); var_export($array);
You have javascript disabled. You will not be able to edit any code.