I'm struggling to get a groupby on a collection to work - I'm not getting the concept just yet.
I'm pulling a collection of results from a table for a player the eloquent collection will have data like this:
['player_id'=>1, 'opposition_id'=>10, 'result'=>'won', 'points'=>2],
['player_id'=>1, 'opposition_id'=>11, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>12, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>10, 'result'=>'won', 'points'=>2],
['player_id'=>1, 'opposition_id'=>11, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>10, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>12, 'result'=>'won', 'points'=>2],
I want to be able to groupBy('opposition_id') and then give me a count of results in total, total won, total lost and sum of points to end up with a collection like this:
['opposition_id'=>10, 'results'=>3, 'won'=>2, 'lost'=>1, 'points'=>4],
['opposition_id'=>11, 'results'=>2, 'won'=>0, 'lost'=>2, 'points'=>0],
['opposition_id'=>10, 'results'=>2, 'won'=>1, 'lost'=>1, 'points'=>2]
I'm trying to avoid going back to the database to do this as I already have the results from previous activity.
How can I do this using Laravel collection methods, So far all I have is:
$stats = $results->groupBy('opposition_id');
I've looked at map() but do not yet understand that method to work through a solution
Can anyone point me in the right direction please.
Happy to go back to the database if needed but assumed I could do this with the collection I already have rather than create another query. Solutions I've found on here all appear to be providing a solution in the query
Thank you
Take a look here, working code with explanation in comments.
// make a collection
$c = collect(
[
['player_id' => 1, 'opposition_id' => 10, 'result' => 'won', 'points' => 2],
['player_id' => 1, 'opposition_id' => 11, 'result' => 'lost', 'points' => 0],
['player_id' => 1, 'opposition_id' => 12, 'result' => 'lost', 'points' => 0],
['player_id' => 1, 'opposition_id' => 10, 'result' => 'won', 'points' => 2],
['player_id' => 1, 'opposition_id' => 11, 'result' => 'lost', 'points' => 0],
['player_id' => 1, 'opposition_id' => 10, 'result' => 'lost', 'points' => 0],
['player_id' => 1, 'opposition_id' => 12, 'result' => 'won', 'points' => 2]
]
);
// this only splits the rows into groups without any thing else.
// $groups will be a collection, it's keys are 'opposition_id' and it's values collections of rows with the same opposition_id.
$groups = $c->groupBy('opposition_id');
// we will use map to cumulate each group of rows into single row.
// $group is a collection of rows that has the same opposition_id.
$groupwithcount = $groups->map(function ($group) {
return [
'opposition_id' => $group->first()['opposition_id'], // opposition_id is constant inside the same group, so just take the first or whatever.
'points' => $group->sum('points'),
'won' => $group->where('result', 'won')->count(),
'lost' => $group->where('result', 'lost')->count(),
];
});
// if you don't like to take the first opposition_id you can use mapWithKeys:
$groupwithcount = $groups->mapWithKeys(function ($group, $key) {
return [
$key =>
[
'opposition_id' => $key, // $key is what we grouped by, it'll be constant by each group of rows
'points' => $group->sum('points'),
'won' => $group->where('result', 'won')->count(),
'lost' => $group->where('result', 'lost')->count(),
]
];
});
// here $groupwithcount will give you objects/arrays keyed by opposition_id:
[
10 => ["opposition_id" => 10,"points" => 4,"won" => 2,"lost" => 1]
11 => ["opposition_id" => 11,"points" => 0,"won" => 0,"lost" => 2]
12 => ["opposition_id" => 12,"points" => 2,"won" => 1,"lost" => 1]
]
// if you use $groupwithcount->values() it'll reset the keys to 0 based sequence as usual:
[
0 => ["opposition_id" => 10,"points" => 4,"won" => 2,"lost" => 1]
1 => ["opposition_id" => 11,"points" => 0,"won" => 0,"lost" => 2]
2 => ["opposition_id" => 12,"points" => 2,"won" => 1,"lost" => 1]
]
Related
Good Afternoon,
Hello, i am new to coding and Laravel and working on home project as a self leaner. i stuck at array sum where i want to make the sum of values in array and add the same sum at the end of tjat array itself.
array:7 [▼
"2022-12-04" => array:9 [▼
"startdate" => "2022-12-04"
"Stotalbmilk" => "29.00"
"Stotala2milk" => "22.50"
"Stotaljmilk" => "20.00"
"Stotalmilk" => "71.50"
"Dtotalbmilk" => "40.00"
"Dtotala2milk" => "0.00"
"Dtotaljmilk" => "0.00"
"Dtotalmilk" => "40.00"
]
in the above array i am to add "TOTAL" at he bottom which value will be addition of 2 values ( "Stotalmilk" and "Dtotalmilk" ). Expected array will be
array:7 [▼
"2022-12-04" => array:9 [▼
"startdate" => "2022-12-04"
"Stotalbmilk" => "29.00"
"Stotala2milk" => "22.50"
"Stotaljmilk" => "20.00"
**"Stotalmilk" => "71.50"**
"Dtotalbmilk" => "40.00"
"Dtotala2milk" => "0.00"
"Dtotaljmilk" => "0.00"
**"Dtotalmilk" => "40.00"**
**"TOTAL" => "111.50"**
]
hope i properly explain my question and sorry for poor english.
Thanks in advance
So if your array have many dates with data, you can do something like:
$result = [];
foreach ($array as $date => $amounts) {
$total = (float) $amounts['Stotalmilk'] + (float) $amounts['Dtotalmilk'];
$amounts['TOTAL'] = number_format($total, 2, '.');
$result[$date] = $amounts;
}
In Laravel 9, there's syncWithPivotValues, which can sync several records with passed pivot values. Is there such thing for attach method? Basically I want to perform attach to several records with the same pivot values, such as:
// attach `role` 1, 2, 3 to `$user`; with `active` attribute set to `true`
$user->roles()->attach([1, 2, 3], ['active' => true]);
You can do something like this:
$user->roles()->attach([
1 => ['active' => true],
2 => ['active' => true],
3 => ['active' => true],
]);
Resource: https://laravel.com/docs/9.x/eloquent-relationships#updating-many-to-many-relationships
If you don't want to repeat ['active' => true] all the time, you can use array_fill_keys like this: array_fill_keys([1, 2, 3], ['active' => true]) so your code will look like:
$user->roles()->attach(array_fill_keys([1, 2, 3], ['active' => true]));
Resource: https://www.php.net/manual/en/function.array-fill-keys.php
just a simple question
In this example, what does => do? and how to read it?
is it read by "is to" or "is equal"
$array = array(
'color' => 'red',
'shape' => 'round',
'radius' => '10',
'diameter' => '20'
);
Well, its not is to or is equal but the sign => is an assignment operator to assign values in array's indexes.
For example:
$x[0] = 10;
$x[1] = 20;
$x[2] = 30;
$x[3] = 40;
Or
$x = (0 => 10,
1 => 20,
2 => 30,
3 => 40,
);
=> separates key and value pairs in PHP array. It is not related to Codeigniter. See more details about PHP array from this link.
I am getting this exception: Undefined offset: 2 when inserting a multidimensional array in the database. This is my loop at which the error is pointing at:
$order_details = [];
for($i= 0; $i < count($product); $i++){
$order_details[] = [
'order_id' => $orders->id,
'product_id' => $product['product_id'][$i],
'units'=>4,
'quantity' => $product['quantity'][$i],
'unit_price' => $product['price'][$i],
'product_discount_id'=>0,
'amount' => $product['amount'][$i],
];
}
When I echo my array seems to look fine:
Array ( [product_id] => Array ( [0] => 7 [1] => 1 ) [quantity] => Array ( [0] => 2 [1] => 1 ) [price] => Array ( [0] => 200.00 [1] => 700.00 ) [amount] => Array ( [0] => 400 [1] => 700 ) )
The undefined offset value alternates depending on how many items from the shopping cart are passed to the loop. For example if there are 3 items the error is set to Undefined offset:3
Its like I cannot find out the real mole. I have searched the web I have not managed any way out.
Any assistance Kindly. what May I doing wrong?
The errors you are getting are about not having an element with index of count($product).
You should just use:
for($i= 0; $i <count($product)-1; $i++)
This way you loop will be fine.
That's kind of an odd array structure. You'd have a much easier time working with the data if you rearranged it like this so you have an item per product:
$products = [
[
'product_id' => 7,
'quantity' => 2,
'price' => 200.00,
'amount' => 400
],
[
'product_id' => 1,
'quantity' => 1,
'price' => 700.00,
'amount' => 700
]
];
Then you can just foreach over the products.
The fix for your problem though is this for($i= 0; $i <= count($product); $i++)
Say for example I've got a collection like this:
[{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2}...]
How can I change the value of ALL the magic_numbers in the collection to be the same value (e.g. 8)?
I'm sure it's using something like map or collect but I can't seem to do it at the moment and return me the whole collection with the changes, just one or the other...
Just use .each:
a = [{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2} ]
a.each { |x| x['magic_number'] = 8 }
# a is now [{"magic_number"=>8, "name"=>"Ganesh"}, {"magic_number"=>8, "name"=>"Comrade"}]
The argument to the block is a reference to the original elements so you can change them as desired. Note that this changes a in-place which I think is what you're after.
This works:
x = [{"name" => "Ganesh", "magic_number" => 7}, {"name" => "Comrade", "magic_number" => 2}]
x.map{|i| i["magic_number"] = 0; i }
=> [{"magic_number"=>0, "name"=>"Ganesh"}, {"magic_number"=>0, "name"=>"Comrade"}]