'Group rows by column and sum another column within groups [duplicate]

I have an array looking like:

array(480) {
  [0]=>
  array(2) {
    ["tag_id"]=>
    string(4) "6291"
    ["az"]=>
    int(5)
  }
  [1]=>
  array(2) {
    ["tag_id"]=>
    string(4) "6291"
    ["az"]=>
    int(4)
  }
  [2]=>
  array(2) {
    ["tag_id"]=>
    string(4) "6311"
    ["az"]=>
    int(4)
  }
  [3]=>
  array(2) {
    ["tag_id"]=>
    string(4) "6427"
    ["az"]=>
    int(4)
  }

How can I reduce the array, so that "az" sums all values of "az", if "Tag_ID" is identical? Expected Output:

array(479) {
      [0]=>
      array(2) {
        ["tag_id"]=>
        string(4) "6291"
        ["az"]=>
        int(9)
      }
      
      [1]=>
      array(2) {
        ["tag_id"]=>
        string(4) "6311"
        ["az"]=>
        int(4)
      }
      [2]=>
      array(2) {
        ["tag_id"]=>
        string(4) "6427"
        ["az"]=>
        int(4)
      }

I tried with array_reduce but can not figure the function to use in it. Thanks a lot!



Solution 1:[1]

You'll have to use array_walk() to modify the array. array_reduce() is to calculate a single value and not to change the array itself.

I would do something like this:

<?php

$array = [
    [
        'tag_id' => "6291",
        'az' => 5,
    ],
    [
        'tag_id' => "6291",
        'az' => 4,
    ],
    [
        'tag_id' => "6311",
        'az' => 4,
    ],
    [
        'tag_id' => "6427",
        'az' => 4,
    ]
];

$tag_id_indexes = []; // To store the index of the first tag_id found.

array_walk(
    $array,
    function ($sub_array, $index) use (&$array, &$tag_id_indexes) {
        // Store the index of the first tag_id found.
        if (!isset($tag_id_indexes[$sub_array['tag_id']])) {
            $tag_id_indexes[$sub_array['tag_id']] = $index;
        }
        else { // This tag_id already exists so we'll combine it.
            // Get the index of the previous tag_id.
            $first_tag_id_index = $tag_id_indexes[$sub_array['tag_id']];
            // Sum the az value.
            $array[$first_tag_id_index]['az'] += $sub_array['az'];
            // Remove this entry.
            unset($array[$index]);
        }
    }
);

print "The reduced array but with the original indexes:\n" . var_export($array, true) . "\n";

// If you want new indexes.
$array = array_values($array);

print "The reduced array with new indexes:\n" . var_export($array, true) . "\n";

You can test it here: https://onlinephp.io/c/58a11

This is the output:

The reduced array but with the original indexes:
array (
  0 => 
  array (
    'tag_id' => '6291',
    'az' => 9,
  ),
  2 => 
  array (
    'tag_id' => '6311',
    'az' => 4,
  ),
  3 => 
  array (
    'tag_id' => '6427',
    'az' => 4,
  ),
)
The reduced array with new indexes:
array (
  0 => 
  array (
    'tag_id' => '6291',
    'az' => 9,
  ),
  1 => 
  array (
    'tag_id' => '6311',
    'az' => 4,
  ),
  2 => 
  array (
    'tag_id' => '6427',
    'az' => 4,
  ),
)

Solution 2:[2]

You can use array_reduce in natural way:

$res = array_reduce(
    $array,
    function($ac, $el) {
        $ac[$el['tag_id']] += $el['az'];
        return $ac;
    },
    []
);

print_r($res);

It will build array with tag_id as key and az as value:

Warning: Undefined array key 6291

Warning: Undefined array key 6311

Warning: Undefined array key 6427
Array
(
    [6291] => 9
    [6311] => 4
    [6427] => 4
)

When you need to restore source array structure use array_map

$res = array_map(
    fn($k, $v) => ['tag_id' => $k, 'az' => $v],
    array_keys($res),
    $res
);

PHP array_reduce array_map

Solution 3:[3]

I've created a bit of sample code that in theory does what you need it to do as long as you know what tag_id may be, it uses array filter and checks if tag_id equals what you've inputed:

$arr = array(
    array("tag_id" => "6291", "az" => "int(5)"),
    array("tag_id" => "6291", "az" => "int(4)"),
    array("tag_id" => "6311", "az" => "int(4)"),
);

$results = array_filter($arr, function ($item) {
    if ($item["tag_id"] == "6291") {
         return true;   
    }
    return false; 
});
              
foreach ($results as $result) {
    echo "tag_id = " . $result["tag_id"];    
    echo "az = " . $result["az"] . PHP_EOL;
} 

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Patrick Janser
Solution 2 mickmackusa
Solution 3 geertjanknapen