-2

What's the simplest way and uses less computing power to check if a multidimensional array has some equal values in the sub keys and, if is in this way, make the sum of that?

Look at the input. It's something like this:

$array[] = ['number' => 1, 'values' => ['a' => 1, 'b' => 2]];
$array[] = ['number' => 2, 'values' => ['a' => 4, 'b' => 1]];
$array[] = ['number' => 2, 'values' => ['a' => 1, 'b' => 4]]; // same "number" as the previous so get the sum between "a" of the previous and "a" of this, the same for "b"
$array[] = ['number' => 2, 'values' => ['a' => 1, 'b' => 1]]; // same "number" as the previous so get the sum between "a" of the previous and "a" of this, the same for "b"
$array[] = ['number' => 3, 'values' => ['a' => 2, 'b' => 4]];

So the output should be

$array[0] : ['number' => 1, 'values' => ['a' => 1, 'b' => 2]];
$array[1] : ['number' => 2, 'values' => ['a' => 6, 'b' => 6]]; //a = 4 + 1 + 1     //b = 1 + 4 + 1
$array[2] : ['number' => 3, 'values' => ['a' => 2, 'b' => 4]];

To tell the truth I have trouble doing it even with simple foreach.

My brain can only go so far and no further.

$array_output = [];
foreach ($array as $k => $v){
    foreach ($array as $kk => $vv){
        if ($v['number'] == $vv['number']) {
            // ????
        }
    }
}

Anyone help me?


Another question in the same question

How to do the same thing for
$array[] = ['number' => 1, 'a' => 1, 'b' => 2];
$array[] = ['number' => 2, 'a' => 4, 'b' => 1];
$array[] = ['number' => 2, 'a' => 1, 'b' => 4];
$array[] = ['number' => 2, 'a' => 1, 'b' => 1];
$array[] = ['number' => 3, 'a' => 2, 'b' => 4];

so how to get the sum for same variable number for a and b variables?

0

4 Answers 4

2

Using array functions. The idea is to build an array where the "number" is the key with array_reduce and then to remove these keys with array_values. This way you know easily if a number has been already encountered and if you have to sum or to define a new key. array_values is finally used to remove these keys.

$result = array_values(
    array_reduce(
        array: $array,
        
        callback: function ($carry, $item) {
            
            [$number, $values] = [$item['number'], $item['values']];
            
            if (array_key_exists(key: $number, array: $carry)) {
                foreach ($values as $key => $value) {
                    $carry[$number]['values'][$key] += $value;
                }
            }
            else {
                $carry[$number] = $item;
            }
            
            return $carry;
        },
        
        initial: []
    )
);

print_r($result);

demo

Note that this code is written for PHP >= 8.0. To make it compatible with older versions, remove the parameter labels in function calls (callback:, key:, array:, etc.).

1
  • @ Casimir et Hippolyte It's so complicated that I'll have to watch it several times to understand it. In any case, do another iteration with array_value, which uses more computational power. Thanks for your contribute. Bye. Commented Dec 11, 2024 at 6:49
1
$seen = [];
$i = 0;
foreach ($array as $data) {
    $number = $data['number'];
    $a = $data['values']['a'];
    $b = $data['values']['b'];

    if (isset($seen[$number])) {
        $output[$seen[$number]]['values']['a'] += $a;
        $output[$seen[$number]]['values']['b'] += $b;
    } else {
        $output[] = $data;
        $seen[$number] = $i;
        $i++;
    }
    //$i++;
}

checking result

print_r($output);

gives me this

Array ( [0] => Array ( [number] => 1 [values] => Array ( [a] => 1 [b] => 2 ) ) [1] => Array ( [number] => 2 [values] => Array ( [a] => 6 [b] => 6 ) ) [2] => Array ( [number] => 3 [values] => Array ( [a] => 2 [b] => 4 ) ) )

edit: went back and saw C.H's comment about a wrong increment placement, code now corrected.

Just to add, to answer the follow up question you can remove all mentions of ['values'] to use the same logic for the simpler second array example.

3
  • @ lemon8de 🍋🍋 Beautiful solution with $seen array. And gives back the correct output too. But I would also be interested to know if there is any function other than foreach that allows you to do the same thing to iterate the array. For now the green tick is yours (in case no other better answer comes). Tnx 😇😘 Commented Dec 10, 2024 at 7:50
  • @ lemon8de And it's beautiful to know if there are a variable number of variables other than just "a" and "b" what would your code looks like. Because for just "a" and "b" it is a specific case. Commented Dec 10, 2024 at 8:08
  • @ Casimir et Hippolyte great correction I didn't realize that. TNX 👋👋😘 Commented Dec 11, 2024 at 6:44
1

So that you don't need to keep track of the indexes of data pushed into the result array nor need to re-index the result, push reference variables into the result array to represent each group.

Assuming all rows will have the same columns in the values subarray, you can unconditionally sum in a nested loop whenever a group is encountered more than once. (Demo)

$result = [];
foreach ($array as $row) {
    if (!isset($ref[$row['number']])) {
        $ref[$row['number']] = $row;
        $result[] =& $ref[$row['number']];
    } else {
        foreach ($row['values'] as $k => $v) {
            $ref[$row['number']]['values'][$k] += $v;
        }
    }
}
var_export($result);

This advice resembles many of my earlier answers on the topic of grouping and summing array data. My posts tagged with arrays + grouping + sum

2
  • 1
    It's a good idea, but if I where you, I will add unset($ref) at the end to break the link with $result. Commented Dec 11, 2024 at 22:41
  • 1
    I tend not to call unset() on reference variables unless I know that it is necessary. Commented Dec 12, 2024 at 12:55
-1

Since everyone is writing, look at this other way that's a possible solution for the second part of the question.

It does a little more math but it works great.

$array[] = ['number' => 1, 'a' => 1, 'b' => 2];
$array[] = ['number' => 2, 'a' => 4, 'b' => 1];
$array[] = ['number' => 2, 'a' => 1, 'b' => 4];
$array[] = ['number' => 2, 'a' => 1, 'b' => 1];
$array[] = ['number' => 3, 'a' => 2, 'b' => 4];

$seen = [];
$output = [];
foreach ($array as $e) {
    $number = $e['number'];
    if (isset($number)) {
        if (!in_array($number,$seen)) {
            $seen[] = $number;
            $output[$number]['number'] = $number;
            foreach($array as $ee) {
                if ($number == $ee['number']) {
                    if (isset($output[$number]['a'])) {
                        $output[$number]['a'] = $output[$number]['a'] + $ee['a'];
                    } else {
                        $output[$number]['a'] = $ee['a'];
                    }
                    if (isset($output[$number]['b'])) {
                        $output[$number]['b'] = $output[$number]['b'] + $ee['b'];
                    } else {
                        $output[$number]['b'] = $ee['b'];
                    }
                }
            }
        }
    }
}
//here the output key is on `$number`
$output = array_values($output);
//here the output key it's in numeric incremental order . (Nothing changes in this case)
print_r($output);

Does not include the case of many variables other than $a and $b, but only sums the specified ones and groups by $number

Here a DEMO. Look at it.

I hope this helps.

Small update - General Case

If you need to sum each key except number replace the inner foreach like this

            foreach($array as $ee) {
                if ($number == $ee['number']) {
                    foreach ($ee as $k => $v) {
                        if ($k != 'number'){
                            if (isset($output[$number][$k])) {
                                $output[$number][$k] = $output[$number][$k] + $ee[$k];
                            } else {
                                $output[$number][$k] = $ee[$k];
                            }
                        }
                    }
                }
            }

Complete other working DEMO

4
  • 2
    Why put the smelly @ in front of in_array()¿ Commented Dec 12, 2024 at 12:53
  • @ mickmackusa I almost always put an @ in front of functions to avoid Warining errors. This time it was not necessary. The array $seen that comes to the function in_array is declared and $number has a check with isset. I have no more "smelly" @ in the code. Bye 👨‍💻 Commented Dec 12, 2024 at 14:32
  • Why do you test isset($number) just after defining it? Commented Dec 12, 2024 at 21:10
  • @Casimir et Hippolyte is related to my case which is more complex than the case in the example. isset($number) was tested because it cannot be defined a number but another variable (actually the real variable of the case I was dealing with is not called "number" and can also have another key for the value, so this operation is performed only in the case where there is number). Specifically you can very well remove it. However I did not say anywhere that number is defined. The example is only for three elements and the variables can be more than a and b. Thanks for your contribution. Bye 😘👋 Commented Dec 12, 2024 at 23:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.