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);