Laravel sum up values from multidimensional array collection [closed] - php

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 14 days ago.
Improve this question
I have a collection like
$collection = collect([
[
["studentId" => 3, "marks" => 10],
["studentId" => 7, "marks" => 15]
],
[
["studentId" => 3, "marks" => 20],
["studentId" => 7, "marks" => 15]
],
[
["studentId" => 3, "marks" => 10],
["studentId" => 7, "marks" => 20]
],
]);
I want to sum up marks for specific studentId and map the results like
[
["studentId" => 3, "marks" => 40],
["studentId" => 7, "marks" => 50],
]
Thanks 🙏

This is untested but you can probably do something like...
$collection = collect([
[
['studentId' => 3, 'marks' => 10],
['studentId' => 7, 'marks' => 15]
],
[
['studentId' => 3, 'marks' => 20],
['studentId' => 7, 'marks' => 15]
],
[
['studentId' => 3, 'marks' => 10],
['studentId' => 7, 'marks' => 20]
],
]);
$collection = $collection
->flatten(1)
->groupBy('studentId')
->transform(function($subItems, $studentId) {
return [
'studentId' => $studentId,
'marks' => $subItems->sum('marks')
];
})
->values();
What this does is...
1. Flatten the array
First we flatten the collection by 1 depth using the flatten method. Resulting structure...
[
['studentId' => 3, 'marks' => 10],
['studentId' => 7, 'marks' => 15],
['studentId' => 3, 'marks' => 20],
['studentId' => 7, 'marks' => 15],
['studentId' => 3, 'marks' => 10],
['studentId' => 7, 'marks' => 20]
]
2. Group values by
Next we group all the values by the studentId using the groupBy method. Result...
[
3 => [
['studentId' => 3, 'marks' => 10],
['studentId' => 3, 'marks' => 20],
['studentId' => 3, 'marks' => 10]
],
7 => [
['studentId' => 7, 'marks' => 15],
['studentId' => 7, 'marks' => 15],
['studentId' => 7, 'marks' => 20]
]
]
3. Sum the marks
Now we calculate the groups of marks with the transform method. Result...
[
3 => ['studentId' => 3, 'marks' => 40],
7 => ['studentId' => 7, 'marks' => 50]
]
4. Reset keys (optional)
You'll notice in the last result that the top level array keys are the same as the studentId values this is left over from the groupBy method. We can reset these with the values method. Result...
[
['studentId' => 3, 'marks' => 40],
['studentId' => 7, 'marks' => 50]
]

Related

How to sort specific value and create new array object from existing with PHP?

I have unsort array object by month as below.
$source = [
['key' => 1,'case_id' => 1, 'month' => 6],
['key' => 2,'case_id' => 1, 'month' => 7],
['key' => 3,'case_id' => 2, 'month' => 7],
['key' => 4,'case_id' => 2, 'month' => 8],
['key' => 5,'case_id' => 3, 'month' => 2],
['key' => 6,'case_id' => 3, 'month' => 4],
['key' => 7,'case_id' => 4, 'month' => 13],
['key' => 8,'case_id' => 4, 'month' => 14]
];
I would like to sort by month value with PHP and create new array object as below prefer.
$newsource = [
['key' => 5,'case_id' => 3, 'month' => 2],
['key' => 6,'case_id' => 3, 'month' => 4],
['key' => 1,'case_id' => 1, 'month' => 6],
['key' => 2,'case_id' => 1, 'month' => 7],
['key' => 3,'case_id' => 2, 'month' => 7],
['key' => 4,'case_id' => 2, 'month' => 8],
['key' => 7,'case_id' => 4, 'month' => 13],
['key' => 8,'case_id' => 4, 'month' => 14]
];
Any advice or guidance on this would be greatly appreciated, Thanks.
Hope this helps you---
$source = [
['key' => 1,'case_id' => 1, 'month' => 6],
['key' => 2,'case_id' => 1, 'month' => 7],
['key' => 3,'case_id' => 2, 'month' => 7],
['key' => 4,'case_id' => 2, 'month' => 8],
['key' => 5,'case_id' => 3, 'month' => 2],
['key' => 6,'case_id' => 3, 'month' => 4],
['key' => 7,'case_id' => 4, 'month' => 13],
['key' => 8,'case_id' => 4, 'month' => 14]
];
uasort($source, function($a, $b){
return $a['month'] > $b['month'];
});
print_r($source);

How to make new array as a group by array using common key-value pair of the input array in PHP

I have a PHP multidimensional array which have one key-value pair same for some records i.e. group_image_id
Input array -
$arr =
[
[0] =>['id' => 1, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ],
[1] =>['id' => 2, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ],
[2] =>['id' => 3, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ],
[3] =>['id' => 4, 'group_image_id' => 5, 'website_id' => 28, 'media_alt' => 'pqr' ],
[4] =>['id' => 5, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ],
[5] =>['id' => 6, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ],
[6] =>['id' => 7, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ],
]
Output array -
$new_arr =
[
[4] =>
[
['id' => 1, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ],
['id' => 2, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ],
['id' => 3, 'group_image_id' => 4, 'website_id' => 28, 'media_alt' => 'abc' ]
],
[5] =>
[
['id' => 4, 'group_image_id' => 5, 'website_id' => 28, 'media_alt' => 'pqr' ]
],
[6] =>
[
['id' => 5, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ],
['id' => 6, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ],
['id' => 7, 'group_image_id' => 6, 'website_id' => 28, 'media_alt' => 'xyz' ]
]
]
I want to convert it into a new array with a group by group_image_id.
Any built-in function exists for this or how it can make it successful.
Using foreach loop I can do it. BUT I am looking for builtin functions if any.
I tried the below solution and it is working fine for me, still, I would love to know if you know any built-in functions for the same.
$new_arr = [];
foreach($arr as $key=>$val){
foreach($val as $k=>$v){
if($k == 'group_image_id'){
$new_arr[$v][] = $val;
}
}
}
As far as I know there is no equivalent for this kind of functionality. Your approach is fairly simple and if I had to do something like that, this is probably what I would do. I don't know about your environment and setup, but If you have the chance to query the data you could do that in the db rather than in php itself.
Nonetheless, you can improve your code by removing the unnecessary inner for-loop. You just need the group-key, you don't need the other keys & values. Like this:
Solution
$buffer = [];
foreach($arr as $val) {
$buffer[$val['group_image_id']][] = $val;
}
print_r($buffer);
This is a simple solution and probably the best.
Anyway, there are alternatives (but they are probably worse) ...
Alternative I
array_walk just goes through the array like a foreach - you can access $key and $value inside the lambda function. You need to pass the buffer by reference to the lambda, otherwise it will create a local variable $buffer inside the function. You could also write global $buffer; on the first line of the function instead of the use(&$buffer).
$buffer = [];
array_walk($arr, function($value, $key) use (&$buffer) {
$buffer[$value['group_image_id']][] = $value;
});
print_r($buffer);
Alternative II
array_column lets you fetch a single key-value pair from a nested array. Keep in mind, that this does multiple for-loops i.e. array_column internally + your for loop.
$groupIds = array_column($arr, 'group_image_id'); // => [4, 4, 4, 5, 6, 6, 6]
$buffer = [];
foreach($groupIds as $key => $val) {
$buffer[$val][] = $arr[$key];
}
print_r($buffer);
Other than that I cannot think of any half viable alternatives.

How to group and sum subarray items in PHP Laravel

I have N arrays. with n grade_items subarrays.
just like this.
array:2 [
0 => array:10 [
"id" => 9
"course_id" => 6
"semester_id" => 2
"name" => "Assignment"
"total_score" => 10
"grade_items" => array:1 [
0 => array:7 [
"id" => 5
"gradelist_id" => 9
"student_course_id" => 11
"score" => 8
"created_at" => "2020-04-21T03:31:20.000000Z"
"updated_at" => "2020-04-21T20:04:10.000000Z"
]
]
]
1 => array:10 [
"id" => 10
"course_id" => 6
"semester_id" => 2
"name" => "Pop Quiz"
"total_score" => 20
"grade_items" => array:1 [
0 => array:7 [
"id" => 6
"gradelist_id" => 10
"student_course_id" => 11
"score" => null
"created_at" => "2020-04-22T00:11:17.000000Z"
"updated_at" => "2020-04-22T00:11:17.000000Z"
]
]
]
]
I am trying to add each grade_item subarray from each array where the student_course_id is the same. Where there is only one grade_item and no other one with the same student_course_id, then it returns just that one value instead of a sum.
I have gone through this thread
But it just messed up the logic in my head further. I've been at this for weeks.
When I add the scores from each grade_item, i want to put that value into another model say "result_model" that would look like:
result_item [
"id" => 1,
"student_course_id" => 11,
"score" => 15 //total of grade_items from all arrays where the student_course_id's were the same
];
Help!
So basically you want to regroup the current information to receive the sum of grades. It seems the the information comes form a databases, so why don't you GROUP BY and sum on database level?
Anyway. Here's an approach. Start by keeping a map of student_course_id => score. First it will be empty: $map = [];
Then start iterating through the whole structure e.g. foreach ($data as $row. For each row, you need to check all the corresponding grade_items e.g. foreach ($row['grade_items'] as $gradeItem). Now you need to check whether the student_course_id from the grade item is present into the mapping.
If it's not present, create it with starting value of zero e.g.
if (!key_exists($gradeItem['student_course_id'], $map)) {
$map[$gradeItem['student_course_id']] = 0;
}
Once you ensure that the student_course_id is present, you can just add to the previous value the current score => $map[$gradeItem['student_course_id']] += $gradeItem['score'].
Here is a sample data i used
$array = [
[
'id' => 9,
'course_id' => 6,
'semester_id' => 2,
'name' => 'Assignment',
'total_score' => 10,
'grade_items' => [
[
'id' => 5,
'gradelist_id' => 9,
'student_course_id' => 11,
'score' => 8,
'created_at' => '2020-04-21T03:31:20.000000Z',
'updated_at' => '2020-04-21T20:04:10.000000Z',
],
[
'id' => 5,
'gradelist_id' => 9,
'student_course_id' => 15,
'score' => 15,
'created_at' => '2020-04-21T03:31:20.000000Z',
'updated_at' => '2020-04-21T20:04:10.000000Z',
]
]
],
[
'id' => 10,
'course_id' => 6,
'semester_id' => 2,
'name' => 'Pop Quiz',
'total_score' => 20,
'grade_items' => [
[
'id' => 6,
'gradelist_id' => 10,
'student_course_id' => 11,
'score' => 21,
'created_at' => '2020-04-22T00:11:17.000000Z',
'updated_at' => '2020-04-22T00:11:17.000000Z',
],
[
'id' => 6,
'gradelist_id' => 10,
'student_course_id' => 23,
'score' => 15,
'created_at' => '2020-04-22T00:11:17.000000Z',
'updated_at' => '2020-04-22T00:11:17.000000Z',
]
]
]
];
and here is the code;
$id = 0;
return collect($array)
->flatMap(function ($item){
return $item['grade_items'];
})
->groupBy('student_course_id')
->transform(function ($subItems, $courseId) use (&$id) {
$id++;
return [
'id' => $id,
'student_course_id' => $courseId,
'score' => $subItems->sum('score')
];
})
->values()
->toArray();
here is the result;
[
[
'id' => 1,
'student_course_id' => 11,
'score' => 29,
],
[
'id' => 2,
'student_course_id' => 15,
'score' => 15,
],
[
'id' => 3,
'student_course_id' => 23,
'score' => 15,
]
]
Use the sum() function. You can loop through the array, and do any checks you need, like if it is not Null etc, and pluck() it and then sum() it.
May I suggest recursivity approach?
<?php
function rec_sum_grades(&$array_grades, &$sum = 0){
$sum += $array_grades['total_score'];
if(!empty($array_grades['grade_items'])){
$this->rec_sum_grades($array_grades['grade_items'], $sum);
}
}
rec_sum_grades($array_grades, $sum);
echo $sum;
?>

PHP Branching Recursion With Repeating Children

I have a flat structure that I need to convert to a nested tree-like structure. This is different from other Stack Overflow questions because the children can repeat (i.e. can have the same questionId and parentId). I have attempted to solve this with branching recursion with no luck.
Input (flat array):
[
[
'questionId' => 1,
'name' => 'albumName',
'parentId' => 0,
'answer' => "Name of album",
],
[
'questionId' => 2,
'name' => 'albumGenre',
'parentId' => 0,
'answer' => "album genre",
],
[
'questionId' => 3,
'name' => 'trackStart',
'parentId' => 0,
],
[
'questionId' => 4,
'name' => 'trackName',
'parentId' => 3,
'answer' => "Track One",
],
[
'questionId' => 5,
'name' => 'trackEnd',
'parentId' => 3,
],
[
'questionId' => 3,
'name' => 'trackStart',
'parentId' => 0,
],
[
'questionId' => 4,
'name' => 'trackName',
'parentId' => 3,
'answer' => "Track Two",
],
[
'questionId' => 6,
'name' => 'artistStart',
'parentId' => 3,
],
[
'questionId' => 7,
'name' => 'artistName',
'parentId' => 6,
'answer' => "Artist Name",
],
[
'questionId' => 8,
'name' => 'artistEnd',
'parentId' => 6,
],
[
'questionId' => 5,
'name' => 'trackEnd',
'parentId' => 3,
],
[
'questionId' => 9,
'name' => 'albumDate',
'parentId' => 0,
'answer' => "album Date",
]
]
Desired Output (nested array):
[
'albumName' => 'Album Name',
'albumGenre' => 'Album Genre',
'trackStart' => [
[
'trackName' => 'Track One'
],
[
'trackName' => 'Track Two',
'artistStart' => [
[
'artistName' => 'Artist Name'
]
]
]
],
'albumDate' => 'album Date'
]
You can solve this using Reference Pointers:
$newArray = array();
$pointer[] = &$newArray;
foreach($arr as $ar) {
if(stristr($ar['name'], "start")) { // Start
$pointer[] = &$pointer[count($pointer)-1][$ar['name']][];
} else if(stristr($ar['name'], "end")) { // End
array_pop($pointer);
} else {
$pointer[count($pointer)-1][$ar['name']] = $ar['answer'];
}
}
To make it faster you can use stripos($ar['name'], "start") !== false;

Laravel Collection pulling all values realted to a item or key

I have a collection which looks like this:
[{"name":"John","apples":8,"pears":4,"oranges":6,"bananas":5},
{"name":"Jane","apples":3,"pears":4,"oranges":2,"bananas":3},
{"name":"Joe","apples":86,"pears":76,"oranges":79,"bananas":77},
{"name":"Janet","apples":3,"pears":16,"oranges":13,"bananas":15}]
What I need to get is a collection or array like below:
[{"name":"John","data":[ 8, 4, 6, 5]},
{"name":"Jane","data":[ 3, 4, 2, 3]},
{"name":"Joe","data":[ 86, 76, 79, 77]},
{"name":"Janet","data":[ 3, 16, 13, 15]}]
Quick and easy solution:
$newCollection = collect([
["name" => "John", "apples" => 8, "pears" => 4, "oranges" => 6, "bananas" => 5],
["name" => "Jane", "apples" => 3, "pears" => 4, "oranges" => 2, "bananas" => 3],
["name" => "Joe", "apples" => 86, "pears" => 76, "oranges" => 79, "bananas" => 77],
["name" => "Janet", "apples" => 3, "pears" => 16, "oranges" => 13, "bananas" => 15],
])->map(function ($row) {
return [
'name' => $row['name'],
'data' => array_values(array_slice($row, 1))
];
});
Keep in mind this will disregard all sequential keys after the first.

Categories