I am going to interpret this question very literally and provide the exact described output structure.
Temporary compound keys allow the very swift lookup of previously encountered brand
- model
pairs. It is important that a delimiting character (that is not used in either value) is used to separate each value in the compound string so that there are no accidental "data collisions".
If a given "brand-model" combination occurs only once, the original row structure is pushed into the result array. Otherwise, the "size" data is converted into an indexed array and subsequent unique size values are pushed into the subarray.
Classic foreach()
: (Demo) (with array_unique to remove potential duplicate sizes)
$result = [];
foreach ($array as $row) {
$compositeKey = $row['brand'] . '_' . $row['model'];
if (!isset($result[$compositeKey])) {
$result[$compositeKey] = $row;
} else {
$result[$compositeKey]['size'] = array_merge(
(array)$result[$compositeKey]['size'],
[$row['size']]
);
}
}
var_export($result);
Functional programming with array_reduce()
: (Demo)
var_export(
array_values(
array_reduce(
$array,
function ($carry, $row) {
$compositeKey = $row['brand'] . '_' . $row['model'];
if (!isset($carry[$compositeKey])) {
$carry[$compositeKey] = $row;
} else {
$carry[$compositeKey]['size'] = array_merge(
(array)$carry[$compositeKey]['size'],
[$row['size']]
);
}
return $carry;
}
)
)
);
If I am being honest, I would create a consistent data structure for my output and size would ALWAYS be a subarray. Here's how to modify the above snippet to cast the size element as an array on the first encounter and push all subsequently encountered size values into that group's subarray: (Demo)
$result = [];
foreach ($array as $row) {
$compositeKey = $row['brand'] . '_' . $row['model'];
if (!isset($result[$compositeKey])) {
$row['size'] = (array)$row['size'];
$result[$compositeKey] = $row;
} else {
$result[$compositeKey]['size'][] = $row['size'];
}
}
var_export(array_values($result));