Home > Net >  Sum same data collection laravel
Sum same data collection laravel

Time:09-16

I have data collection like this

illuminate\support\collection

items: array(3)
    0: array(5)
        user_id: '1'
        mode: 'doci'
        channel: 1
        total: 1
        addition: "[{"cash":0}]"
    1: array(5)
        user_id: '1'
        mode: 'doci'
        channel: 1
        total: 1
        addition: "[{"cash":0}]"
    2: array(5)
        user_id: '1'
        mode: 'doci'
        channel: 1
        total: 1
        addition: "[{"cash":10}]"

as you can see index 0 and 1 are same. And I wanna sum total of them because the data are same.

and I have trying this

$collection = collect([]);

$data->each(function ($item) use ($collection) {
    $target = $collection->where('user_id', $item['user_id'])
        ->where('mode', $item['mode'])
        ->where('channel', $item['channel'])
        ->where('addition', $item['addition']);

  if ($target->count() == 0) {
    $collection->push($item);
  } else {
    $target->first()['total']  = $item['total'];
  }
});
    
$data = $collection;

and resulting this

illuminate\support\collection

items: array(3)
    0: array(5)
        user_id: '1'
        mode: 'doci'
        channel: 1
        total: 1    -> expected result is 2 but that still 1
        addition: "[{"cash":0}]"
    2: array(5)
        user_id: '1'
        mode: 'doci'
        channel: 1
        total: 1
        addition: "[{"cash":10}]"

Expected result on index 0 and key total is 2, but existing resulting 1 What I missed?

CodePudding user response:

Since you want to group all items with the same values (except "total" I assume) you can do something like:

$result = $data->groupBy(function ($item) {
   return $item['user_id'].'-'.$item['mode'].'-'.$item['channel'].'-'.$item['addition'];
})->map(function ($items) {
    return $items->reduce(function ($acc, $item) {
        return array_merge($acc, $item, [ 'total' => ($acc['total']??0)   ($item['total']??0) ]); 
    }, []);
})->values();

This should group by all values except total, then reduce each group down to a single item with summed totals.

CodePudding user response:

The method of a Collection returns a new Collection instance, so $target is a new instance of $collection.

This line won't modify the original value of $collection.

$target->first()['total']  = $item['total'];

Considering the feature of the Collection, it's maybe a good way to use an array, in which the key is unique and the value of total is accumulated. Below is the code:

        $uniqueArray = [];
        $keyArray = ['user_id', 'mode', 'channel', 'addition']; // make sure the value of the key is string !!!
        $data->each(function ($item) use (&$uniqueArray, $keyArray) {
            //build uniuqe index 
            $index = '';
            foreach($keyArray as $key) {
                $index .= $item[$key];
            }
            if (!isset($uniqueArray[$index])) {
                $uniqueArray[$index] = $item;
            }else {
                $uniqueArray[$index]['total']  = $item['total'];
            }
        });
        $data = collect($uniqueArray);

  • Related