Search code examples
phparraysmultidimensional-arraysumgrouping

Group rows of a multidimensional array by column value and sum another column in each group


This my array example

[
    [
        '_id' => '5f76b1788ee23077dccd1a2c',
        'product' => ['_id'=>'5d0391a4a72ffe76b8fcc610'],
        'count'=> 1
    ],
    [
        '_id' => '5f76b6288ee2300700cd1a3a',
        'product' => ['_id'=>'5d0391b6a72ffe76b8fcc611'],
        'count'=> 1
    ],
    [
        '_id' => '5f76d2488ee23083d3cd1a4a',
        'product' => ['_id'=>'5d0391b6a72ffe76b8fcc611'],
        'count'=> 1
    ]
]

And I want to group if product id value is the same, like this:

[
    [
        '_id' => '5f76b1788ee23077dccd1a2c',
        'product' => ['_id'=>'5d0391a4a72ffe76b8fcc610'],
        'count'=> 1
    ],
    [
        '_id' => '5f76b6288ee2300700cd1a3a',
        'product' => ['_id'=>'5d0391b6a72ffe76b8fcc611'],
        'count'=> 2
    ],
]

Solution

  • It makes no sense to retain the first level ids since you are arbitrarily trashing some of the first level ids (damaging the relationships) during the merging process.

    Instead I recommend that you only isolate the data that is accurately related.

    If this output does not serve your needs, then I'll ask for further question clarification.

    By assigning temporary keys to your output array, the output array also acts as a lookup array by which you can swiftly check for uniqueness. The "null coalescing operator" (??) sets a fallback value of 0 when an id is encountered for the first time -- this prevents generating any warnings regarding undeclared keys.

    Code: (Demo)

    $array = [
        ['_id' => '5f76b1788ee23077dccd1a2c', 'product' => ['_id'=>'5d0391a4a72ffe76b8fcc610'], 'count'=> 1],
        ['_id' => '5f76b6288ee2300700cd1a3a', 'product' => ['_id'=>'5d0391b6a72ffe76b8fcc611'], 'count'=> 1],
        ['_id' => '5f76d2488ee23083d3cd1a4a', 'product' => ['_id'=>'5d0391b6a72ffe76b8fcc611'], 'count'=> 1]
    ];
    
    $productCounts = [];
    foreach ($array as $item) {
        $productId = $item['product']['_id'];
        $productCounts[$productId] = ($productCounts[$productId] ?? 0) + $item['count'];
    }
    
    var_export($productCounts);
    

    Output:

    array (
      '5d0391a4a72ffe76b8fcc610' => 1,
      '5d0391b6a72ffe76b8fcc611' => 2,
    )
    

    If you insist of the desired output in your question, then it can be as simple and efficient as this...

    Code: (Demo)

    $result = [];
    foreach ($array as $item) {
        $productId = $item['product']['_id'];
        if (!isset($result[$productId])) {
            $result[$productId] = $item;
        } else {
            $result[$productId]['count'] += $item['count'];
        }
    }
    
    var_export(array_values($result));
    

    Output:

    array (
      0 => 
      array (
        '_id' => '5f76b1788ee23077dccd1a2c',
        'product' => 
        array (
          '_id' => '5d0391a4a72ffe76b8fcc610',
        ),
        'count' => 1,
      ),
      1 => 
      array (
        '_id' => '5f76b6288ee2300700cd1a3a',
        'product' => 
        array (
          '_id' => '5d0391b6a72ffe76b8fcc611',
        ),
        'count' => 2,
      ),
    )