Reputation: 614
I need to sum a column of values in one array grouped by another column and map those results to another array based on a specific column value.
Sample Input:
$milestones = [
[
'id' => '1578453e53090b',
'milestone' => 'Wireframe',
'assigned_to' => 9
],
[
'id' => '1578453e530975',
'milestone' => 'Development'
],
[
'id' => '1578453e530943',
'milestone' => 'Design',
'assigned_to' => 2
]
];
$tasks = [
[
'id' => '15786dc59333f2',
'status' => 'Open',
'progress' => 5,
'milestone' => '1578453e53090b'
],
[
'id' => '15786dc669d451',
'status' => 'Open',
'progress' => 10,
'milestone' => '1578453e53090b'
],
[
'id' => '15786dc7ccbea3',
'status' => 'Open',
'progress' => 20,
'milestone' => '1578453e530943'
]
];
I need to get the sum of the progress
values in the $tasks
array grouped by milestone
then store/append those totals to the $milestones
array.
Desired Output:
[
[
'id' => '1578453e53090b',
'milestone' => 'Wireframe',
'assigned_to' => 9,
'total' => 15
],
[
'id' => '1578453e530975',
'milestone' => 'Development'
],
[
'id' => '1578453e530943',
'milestone' => 'Design',
'assigned_to' => 2,
'total' => 20
]
]
Upvotes: 0
Views: 62
Reputation: 47874
Using a nested loop approach is unnecessarily expensive -- it is simply doing too many unnecessary cycles while checking for matches in the inner loop.
Instead, perform one loop over the $milestones
array and declare a unique reference to each row. Then perform one loop over the $tasks
array to add each new encounter's progress
value to its mapped/referenced row.
Code: (Demo)
foreach ($milestones as &$row) {
$ref[$row['id']] = &$row;
}
foreach ($tasks as ['milestone' => $m, 'progress' => $p]) {
$ref[$m]['total'] = ($ref[$m]['total'] ?? 0) + $p;
}
var_export($milestones);
This approach is assumed to be safe because ALL task rows have a milestone
value which correlates to milestone row id
s and ALL id
values in the milestones array are unique.
Upvotes: 0
Reputation: 40886
Array mapping should do the trick:
//will add 'progress' key to each milestone
$tabulate_progress = function($milestone) use ($tasks) {
$milestone['progress'] = 0; //start with 0 progress
foreach($tasks as $task):
//add to progress if we find a matching task
if($task['milestone']===$milestone['id'])
$milestone['progress']+=$task['progress'];
endforeach;
return $milestone;
};
$milestones = array_map($tabulate_progress,$milestones);
Upvotes: 2
Reputation: 41885
One of the ways to do this is to just use foreach
. Use the first foreach
for the milestones
getting the ID that will match the second foreach
for tasks
in milestone
.
After that, use a simple $total
container to be used continually andatch them using a simple if
.
foreach($milestones as &$m) {
// ^ add reference to make changes
$total = 0; // initialize total holder
foreach($tasks as $task) {
if($task['milestone'] === $m['id']) { // if it matches
$total += $task['progress']; // add
}
}
$m['total'] = $total; // after its done, add another key pair being total and its value
}
Upvotes: 1