Multidimensional Array Sorting/Chaining Based on Value PHP - php

I have an array below format:
$data = [
'2018-04-26' => [
[
'op' => 3,
'cl' => 4
],
[
'op' => 3,
'cl' => 2
],
[
'op' => 4,
'cl' => 3
]
]
];
I want to make it sort as
$data['2018-04-26'] = [
[
'op' => 3,
'cl' => 4
],
[
'op' => 4,
'cl' => 3
],
[
'op' => 3,
'cl' => 2
]
];
How will I sort based on OP, CL.
OP of current array is equal to previous array CL. or
CL of current array is equal to next array OP.
We can start any where but let start accordingly index 0-n.
There is no chance for multiple solution.
If same op/cl appeared we can put it anywhere.
I have tried using usort() but how do I put the logic.
function cmpare($a, $b){
//the logic
return 0;
}
usort($data['2018-04-26'], 'cmpare');

This is the php code:
$data = [];
$data['2018-04-26'][] = [
'op' => 3,
'cl' => 4
];
$data['2018-04-26'][] = [
'op' => 4,
'cl' => 3
];
$data['2018-04-26'][] = [
'op' => 3,
'cl' => 2
];
usort($data['2018-04-26'], function($a, $b){
return ($a['op'] == $b["cl"]) ? 1 : 0;
});

Related

How to group PHP arrays by key before sorting by another key?

I have an array that contains the key counted and placement which I am trying to group by before I sort. The array should be first sorted by counted and then, for each duplicate counted, should then sort by placement.
$array = [
[
'id' => 1,
'placement' => 8,
'counted' => 3
'user' => ['name' => 'foo'],
],
[
'id' => 2,
'placement' => 5,
'counted' => 3
'user' => ['name' => 'bar'],
],
[
'id' => 3,
'placement' => 1,
'counted' => 2
'user' => ['name' => 'foobar'],
]
];
My expected output here would be:
$array = [
[
'id' => 2,
'placement' => 5,
'counted' => 3
'user' => ['name' => 'bar'],
],
[
'id' => 1,
'placement' => 8,
'counted' => 3
'user' => ['name' => 'foo'],
],
[
'id' => 3,
'placement' => 1,
'counted' => 2
'user' => ['name' => 'foobar'],
]
];
I have tried to usort to achieve this:
usort($array, fn($a, $b) => ((int)$a['placement'] <=> (int)$b['counted']) * -1);
But this gives me an unexpected result. Everything I try seems to not work, any ideas would be appreciated.
Since you prefer using usort so this is my answser
$array = [
[
'id' => 1,
'placement' => 8,
'counted' => 3,
'user' => ['name' => 'foo'],
],
[
'id' => 2,
'placement' => 5,
'counted' => 3,
'user' => ['name' => 'bar'],
],
[
'id' => 3,
'placement' => 1,
'counted' => 2,
'user' => ['name' => 'foobar'],
]
];
usort($array, function ($a, $b) {
if ($a['counted'] < $b['counted']) {
return 1;
}
if ($a['counted'] === $b['counted'] && $a['placement'] > $b['placement']) {
return 1;
}
});
If you don’t care about efficiency, you can write like this
collect($array)
->sortByDesc('counted')
->groupBy('counted')
->map(function ($group) {
return $group->sortBy('placement');
})
->flatten(1)
->toArray()

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

Finding the lowest value for specific key in a deeply nested array

Given an array with the following structure:
$orders = [
0 => [
'volume' => 1926,
'lowPrice' => 1000.00,
'date' => 2016-12-29 00:45:23
],
1 => [
'volume' => 145,
'lowPrice' => 1009.99,
'date' => 2016-12-31 14:23:51
],
2 => [
'volume' => 19,
'lowPrice' => 985.99,
'date' => 2016-12-21 09:37:13
],
3 => [
'volume' => 2984,
'lowPrice' => 749.99,
'date' => 2017-01-01 19:37:22
],
// ...
]
I'm looking for the most performant way to find the lowest value for lowPrice. The size of $orders will more than likely be larger than 500 items. For the given array, the result would be as follows:
$lowestPrice = $this->findLowestPrice($orders);
echo $lowestPrice; // 749.99
Extract an array of lowPrice and find the min():
echo min(array_column($orders, 'lowPrice'));
Since those appear to be floats, you'll probably want number_format() or money_format().
If you're only interested in the value of the lowest price (not the index in the array or something) you can use array_reduce:
$orders = [
0 => [
'volume' => 1926,
'lowPrice' => 1000.00,
'date' => "2016-12-29 00:45:23"
],
1 => [
'volume' => 145,
'lowPrice' => 1009.99,
'date' => "2016-12-31 14:23:51"
],
2 => [
'volume' => 19,
'lowPrice' => 985.99,
'date' => "2016-12-21 09:37:13"
],
3 => [
'volume' => 2984,
'lowPrice' => 749.99,
'date' => "2017-01-01 19:37:22"
],
];
$lowestPrice = array_reduce(
$orders,
function($c, $v)
{
if ($c === null)
return $v["lowPrice"];
return $v["lowPrice"] < $c ? $v["lowPrice"] : $c;
}
);

Recursive count of children in nested arrays + changing values by reference

I know this is basic recursion but I get stuck anyway :(
I need to count how many elements each element has below it (children, grandchildren,...) and write that value into original array.
My example array:
$table = [
1 => [
'id' => 1,
'children_count' => 0
],
2 => [
'id' => 2,
'children_count' => 0,
'children' => [
3 => [
'id' => 3,
'children_count' => 0,
'children' => [
4 => [
'id' => 4,
'children_count' => 0,
'children' => [
5 => [
'id' => 5,
'children_count' => 0
],
6 => [
'id' => 6,
'children_count' => 0
]
]
]
]
]
]
]
];
My recursion code:
function count_children(&$array){
foreach($array['children'] as &$child){
if(isset($child['children'])){
$array['children_count'] += count_children($child);
}
else return 1;
}
}
Call for recursion:
//call for each root element
foreach($table as &$element){
if(isset($element['children'])) count_children($element);
}
Expected output:
$table = [
1 => [
'id' => 1,
'children_count' => 0
],
2 => [
'id' => 2,
'children_count' => 4,
'children' => [
3 => [
'id' => 3,
'children_count' => 3,
'children' => [
4 => [
'id' => 4,
'children_count' => 2,
'children' => [
5 => [
'id' => 5,
'children_count' => 0
],
6 => [
'id' => 6,
'children_count' => 0
]
]
]
]
]
]
]
];
Where did I got it wrong?
My function does something, element 3 gets value 1, but thats about it.
Here is the ideone link: http://ideone.com/LOnl3G
function count_children(&$table){
$count1 = 0;
foreach($table as &$array) {
$count = 0;
if (isset($array['children'])) {
$count += count($array['children']);
$count += count_children($array['children']);
}
$array['children_count'] = $count;
$count1 += $count;
}
return $count1;
}
count_children($table);
print_r($table);

PHP Merge by values in same array

So I have this array in PHP.
$arr = [
[ 'sections' => [1], 'id' => 1 ],
[ 'sections' => [2], 'id' => 1 ],
[ 'sections' => [3], 'id' => NULL ],
[ 'sections' => [4], 'id' => 4 ],
[ 'sections' => [5], 'id' => 4 ],
[ 'sections' => [6], 'id' => 4 ]
];
I want to merge on 'id' and get something like
$arr = [
[ 'sections' => [1, 2], 'id' => 1 ],
[ 'sections' => [3], 'id' => NULL ],
[ 'sections' => [4, 5, 6], 'id' => 4 ]
];
Just struggling to get my head around this one. Any Ideas
I've created this quick function that might work for you
<?php
// Your array
$arr = array(
array( 'elem1' => 1, 'elem2' => 1 ),
array( 'elem1' => 2, 'elem2' => 1 ),
array( 'elem1' => 3, 'elem2' => NULL ),
array( 'elem1' => 4, 'elem2' => 4 ),
array( 'elem1' => 5, 'elem2' => 4 ),
array( 'elem1' => 6, 'elem2' => 4 )
);
print_r($arr);
function mergeBy($arr, $elem2 = 'elem2') {
$result = array();
foreach ($arr as $item) {
if (empty($result[$item[$elem2]])) {
// for new items (elem2), just add it in with index of elem2's value to start
$result[$item[$elem2]] = $item;
} else {
// for non-new items (elem2) merge any other values (elem1)
foreach ($item as $key => $val) {
if ($key != $elem2) {
// cast elem1's as arrays, just incase you were lazy like me in the declaration of the array
$result[$item[$elem2]][$key] = $result[$item[$elem2]][$key] = array_merge((array)$result[$item[$elem2]][$key],(array)$val);
}
}
}
}
// strip out the keys so that you dont have the elem2's values all over the place
return array_values($result);
}
print_r(mergeBy($arr));
?>
Hopefully it'll work for more than 2 elements, and you can choose what to sort on also....

Categories