phparraysmultidimensional-arraysumgrouping

Group rows of a 2d array by a column and sum numeric columns within groups


I have $row which outputs the below. Array 0 - 4 do not change per user but the last items in each array need to be added together.

array (
  0 => '2',
  1 => 'Joe',
  2 => 'Bloggs',
  3 => 'joe.bloggs@mail.com',
  4 => '1,2',
  5 => 1,
  6 => 0,
  7 => 1,
)array (
  0 => '2',
  1 => 'Joe',
  2 => 'Bloggs',
  3 => 'joe.bloggs@mail.com',
  4 => '1,2',
  5 => 0,
  6 => 1,
  7 => 1,
)array (
  0 => '1',
  1 => 'Jane',
  2 => 'Doe',
  3 => 'jane.doe@mail.com',
  4 => '1,4',
  5 => 1,
  6 => 0,
  7 => 1,
)array (
  0 => '1',
  1 => 'Jane',
  2 => 'Doe',
  3 => 'jane.doe@mail.com',
  4 => '1,4',
  5 => 0,
  6 => 0,
  7 => 0,
)

I need to combine them so they are like this:

Array
(
    [0] => 2
    [1] => Joe
    [2] => Bloggs
    [3] => joe.bloggs@mail.com
    [4] => 1,2
    [5] => 1
    [6] => 1
    [7] => 2
)
Array
(
    [0] => 1
    [1] => Jane
    [2] => Doe
    [3] => jane.doe@mail.com
    [4] => 1,4
    [5] => 1
    [6] => 0
    [7] => 1
)

My code so far is:

$combined = array();
foreach ($row as $item) {
    if (!array_key_exists($item[0], $combined)) {
        $combined[$item[0]] = $item;
    } else {
        
        array_push($combined, $item);
    }   
}

But this is not doing what I expect.


Solution

  • You were very close. You simply needed to add the values.

    /**
     * @param array $combined       the combined array
     * @param array $item           a single row of data (8-element array)
     *
     * @returns array               the updated combined array
     */
    function combineArray(array $combined, array $item) {
        // This is how we know whether the element exists or not
        $key = $item[0];
        if (!array_key_exists($key, $combined)) {
            // This is a NEW item, so just add it to the combined array.
            $combined[$key] = $item;
        } else {
            // This already exists. Modify the required columns.
            $combined[$key][5] += $item[5];
            $combined[$key][6] += $item[6];
            $combined[$key][7] += $item[7];
            /*
               You could also do this automatically from the type of variable, instead of specifying 5, 6 and 7:
               foreach ($item as $i => $value) {
                   if (in_array(gettype($value), array('integer', 'float'))) {
                       $combined[$key][$i] += $value;
                   }
               }
            */
        }   
    
        return $combined;
    }
    
    $combined = array();
    
    foreach ($row as $item) {
        $combined = combineArray($combined, $item);
    }
    // Now convert to "true" array. This is VERY IMPORTANT if you want to output
    // it to, say, JSON, where [ 0 => 'a', 1 => 'b' ] and [ 0 => 'a', 2 => 'b' ]
    // are two different KINDS of object (the first an array, the second a dict)
    
    $combined = array_values($combined);
    

    or also (to show the call with a single row):

    $item = array (
      0 => '2',
      1 => 'Joe',
      2 => 'Bloggs',
      3 => 'joe.bloggs@mail.com',
      4 => '1,2',
      5 => 1,
      6 => 0,
      7 => 1,
    );
    $combined = combineArray($combined, $item);
    

    The loop version works as expected with the following data:

    $row = array(
    array (
      0 => '2',
      1 => 'Joe',
      2 => 'Bloggs',
      3 => 'joe.bloggs@mail.com',
      4 => '1,2',
      5 => 1,
      6 => 0,
      7 => 1,
    ),array (
      0 => '2',
      1 => 'Joe',
      2 => 'Bloggs',
      3 => 'joe.bloggs@mail.com',
      4 => '1,2',
      5 => 0,
      6 => 1,
      7 => 1,
    ),array (
      0 => '1',
      1 => 'Jane',
      2 => 'Doe',
      3 => 'jane.doe@mail.com',
      4 => '1,4',
      5 => 1,
      6 => 0,
      7 => 1,
    ),array (
      0 => '1',
      1 => 'Jane',
      2 => 'Doe',
      3 => 'jane.doe@mail.com',
      4 => '1,4',
      5 => 0,
      6 => 0,
      7 => 0,
    ));
    

    and outputs:

    Array
    (
        [0] => Array
            (
                [0] => 2
                [1] => Joe
                [2] => Bloggs
                [3] => joe.bloggs@mail.com
                [4] => 1,2
                [5] => 1
                [6] => 1
                [7] => 2
            )
    
        [1] => Array
            (
                [0] => 1
                [1] => Jane
                [2] => Doe
                [3] => jane.doe@mail.com
                [4] => 1,4
                [5] => 1
                [6] => 0
                [7] => 1
            )
    
    )