Skip to content
Advertisement

Combine object values in a single array by matching object fields in PHP

I’ve been digging around and trying to solve this issue the cleanest way possible, but haven’t quite found the right approach.

I have an array of objects like so:

$myArray = [
    {field: "Diameter", measurement: 15, count: 4},
    {field: "Diameter", measurement: 16, count: 1},
    {field: "Diameter", measurement: 17, count: 15},
    {field: "Width", measurement: 7, count: 12},
    {field: "Width", measurement: 8, count: 8},
    {field: "Brands", measurement: "blah", count: 1},
    {field: "Brands", measurement: "doubleBlah", count: 3},
    {field: "Brands", measurement: "blah", count: 1},
    {field: "Brands", measurement: "doubleBlah", count: 3},
    {field: "Brands", measurement: "blah", count: 12}
    ]

and I need to combine the objects that have duplicate measurement fields and combine the counts like this:

$myBetterArray = [
    {field: "Diameter", measurement: 15, count: 4},
    {field: "Diameter", measurement: 16, count: 1},
    {field: "Diameter", measurement: 17, count: 15},
    {field: "Width", measurement: 7, count: 12},
    {field: "Width", measurement: 8, count: 8},
    {field: "Brands", measurement: "blah", count: 14},
    {field: "Brands", measurement: "doubleBlah", count: 6},
    ]

can it be done with something like array_map or something equally clean and not too verbose? Any help would be much appreciated.

Advertisement

Answer

Based on the keys being “hidden” like that, the data format known, and the way the question is written(‘the cleanest way possible’, ‘not too verbose’) I’d simply go for foreach():

$myArray = '[
{"field": "Diameter", "measurement": 15, "count": 4},
{"field": "Diameter", "measurement": 16, "count": 1},
{"field": "Diameter", "measurement": 17, "count": 15},
{"field": "Width", "measurement": 7, "count": 12},
{"field": "Width", "measurement": 8, "count": 8},
{"field": "Brands", "measurement": "blah", "count": 1},
{"field": "Brands", "measurement": "doubleBlah", "count": 3},
{"field": "Brands", "measurement": "blah", "count": 1},
{"field": "Brands", "measurement": "doubleBlah", "count": 3},
{"field": "Brands", "measurement": "blah", "count": 12}
]';
$myArray = json_decode($myArray);

// Sort the data
$_im = [];
foreach($myArray as $item) {
    $_im[$item->field][$item->measurement] = $item->count + ($_im[$item->field][$item->measurement] ?? 0);
}

// Output it in the desired format
$res = [];
foreach($_im as $field => $fdata) {
    foreach($fdata as $measurement => $count) {
        $res []= (object) compact('field', 'measurement', 'count');
    }
}

print_r($res);

Process the data in an intermediary array where you can use the keys(if you don’t do it that way searching for a given pair of keys later would be more complex), and then another foreach to put the result together in the desired format.

This should be enough for 2 levels. Also, it might be nice to put the code in a function to keep $_im from polluting the namespace.

(object) [] is a simple way to cast an array into an object(stdClass), since that seems to be the format of the data in the question.

?? 0 creates a start value for the case when the current $_im[$item->field][$item->measurement] isn’t there yet (i.e. each time there’s a new field,measurement pair). Shorter to write than using isset().

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement