get Biggest collection in a collection of collection - laravel

I have a Collection of collection.
I would like to get the biggest collection inside the collection.
I wrote a function that works well, but I'm pretty sure it can be done much quicker:
private function getMaxFightersByEntity($userGroups): int
{
$max = 0;
foreach ($userGroups as $userGroup) { // $userGroup is another Collection
if (count($userGroup) > $max) {
$max = count($userGroup);
}
}
return $max;
}
I'm quite sure there is a better way managing collection, but don't really know it.
Anyone has a better solution???

You can sort the collection by the count of the inner collections, and then just take the first item (largest group).
// sortByDesc: sort the groups by their size, largest first
// first: get the first item in the result: the largest group
// count: get the size of the largest group
return $userGroups
->sortByDesc(function ($group) {
return $group->count();
})
->first()
->count();
It won't be "quicker" than your current solution in execution time, but it is written to take advantage of the functions provided by collections.

Related

How to sort a collection in Laravel

I doing sortBy function to sort but i can't use it as sorted by For.
$this->journey->journey_item = $this->journey->journey_items->sortBy('activation_date');
dump($journey_items); //// sorted list
for($i = 0;$i < Count($this->journey->journey_item);$i++){
dump("#".$i." ".$this->journey->journey_item[$i]->activation_date);
}
When it entered in For, it becomes as unsorted.
Where am i doing wrong ?
What can i do ?
Also, I have created a new collection and tried to equalize it, again it didn't
Try with foreach. when you use collection sort it'll sort your array not key.. as I see you're using key [$i].
Use foreach
foreach($this->journey->journey_item as $journey_item){
dump($journey_item);
}
Or you use as below.
$this->journey->journey_item = $this->journey->journey_items->sortBy(function ($journey_item) {
return $journey_item->activation_date;
});

Laravel get the position/rank of a row in collection

I have a leaderboards and leaderboard_scores table.
leaderboard_scores has columns: id, leaderboard_id, player_id, score.
In Leaderboard model, I defined a hasMany relationship with LeaderboardScore:
public function scores()
{
return $this->hasMany('App\LeaderboardScore')->orderBy('score', 'desc');
}
Now I want to get the ranking of a player in a particular leaderboard.
So in controller, I do a loop to find the position given a player_id:
$score = $leaderboard->scores;
$scoreCount = $scores->count();
$myScore = $scores->where('player_id', $request->query('player_id'));
if ($myScore) { // If got my score
$myRank = 1;
for ($i = 0; $i < $scoreCount; $i++) {
if ($scores[$i]->player_id == $request->query('player_id')) {
$myRank = $i + 1;
break;
}
}
// Output or do something with $myRank
}
It works fine, but I am worried about the performance, when I have hundred thousand of players and they constantly getting their ranking. The for loop seems not a good option.
Should I use raw database query? Or any better idea?
Here some ideas
Instead of calculating ranks each time you get request, then loop through all data. you can just store players rank either in database or cache. You only need to calculate them when new player added/deleted or score changed.
Check For Caching:
https://laravel.com/docs/5.4/redis
if you still want to calculate it everytime you can use mysql rank function which is more optimized than a primitive loop
Mysql Rank : http://www.folkstalk.com/2013/03/grouped-rank-function-mysql-sql-query.html

Merging multiple objects which uses same id

I'm trying to merge multiple objects (like Receipts, Reports, etc) with Collection->merge().
This is the code I used:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->merge($reports);
This is the result:
The above screenshot shows two elements, but the third element is missing because it has the same id (id: "1") as the first one. What I'm trying to achieve is to display all three of them as a collection.
EDIT:
I need the result to be objects (collection) because I also use the code on my view, where I check the class to determine what to display. Also, I use this function to sort the objects in the collection.
$collection->sort(function($a, $b)
{
$a = $a->created_at;
$b = $b->created_at;
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
});
I know that this is an old question, but I will still provide the answer just in case someone comes here from the search like I did.
If you try to merge two different eloquent collections into one and some objects happen to have the same id, one will overwrite the other. I dunno why it does that and if that's a bug or a feature - more research needed. To fix this just use push() method instead or rethink your approach to the problem to avoid that.
Example of a problem:
$cars = Car::all();
$bikes = Bike::all();
$vehicles = $cars->merge($bikes);
// if there is a car and a bike with the same id, one will overwrite the other
A possible solution:
$collection = collect();
$cars = Car::all();
$bikes = Bike::all();
foreach ($cars as $car)
$collection->push($car);
foreach ($bikes as $bike)
$collection->push($bike);
Source: https://medium.com/#tadaspaplauskas/quick-tip-laravel-eloquent-collections-merge-gotcha-moment-e2a56fc95889
I know i'm bumping a 4 years old thread but i came across this and none of the answers were what i was looking for; so, like #Tadas, i'll leave my answer for people who will come across this. After Looking at the laravel 5.5 documentation thoroughly i found that concat was the go-to method.
So, in the OP's case the correct solution would be:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->concat($reports);
This way every element in the Report collection will be appended to every element in the Receipts collection, event if some fields are identical.
Eventually you could shuffle it to get a more visual appealing result for e.g. a view:
$collection->shuffle();
Another way to go about it is to convert one of your collections to a base collection with toBase() method. You can find it in Illuminate\Support\Collection
Method definition:
/**
* Get a base Support collection instance from this collection.
*
* #return \Illuminate\Support\Collection
*/
public function toBase()
{
return new self($this);
}
Usage:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->toBase()->merge($reports);
You could put all collections in an array and use this. Depends on what you want to do with the collection.
$list = array();
$list = array_merge($list, Receipt::all()->toArray());
$list = array_merge($list, Report::all()->toArray());

What is the linq query that would replace my two foreach

I'm struggeling to figure out how to replace the two foreach below.
public void UpdateChangedRows(List<Row> changedRows)
{
// How to write linq to update the rows (example code using foreach)
foreach (Row row in table.Rows)
{
foreach (Row changedRow in changedRows)
{
if (row.RowId==changedRow.RowId)
{
row.Values = changedRow.Values;
}
}
}
}
I think there would be a linq way to perform the same operation. Thanks for any help.
Larsi
Well, LINQ is more about querying than updating, so I'll still separate it into a LINQ query and then a foreach loop to do the update:
var query = from row in table.Rows
join changedRow in changedRows on row.RowId = changeRow.RowId
select { row, changedRow };
foreach (var entry in query)
{
entry.row.Values = entry.changedRow.Values;
}
Note that this is less efficient in terms of memory, in that it loads all the changed rows into a dictionary (internally) so that it can quickly look up the changed rows based on the row ID - but it means the looping complexity is a lot smaller (O(n) + O(m) instead of O(n * m) if each row only matches a single changed row and vice versa).

What's the best way to manipulate and store huge ordered lists or hashes?

I have a a simple ordered list that could contain 1 million or more items. There are only a few actions that are done with this list:
lookup in a value exist
find the index for a value
find value for index
add a value
get number of items in the list
Once a value is added to the list, it never changes. I append items to the list, no insert or delete.
I need to manipulate this big list, and store it persistently. Right now I am using a database Int => String to represent the list, but I think there should me a more efficient way to do that.
I could use memcached, but I think 2 functions are missing:
persistent storage
find the index for a value
It appears that you also need a String -> Int mapping table.
In Perl the easiest way to do this is to tie a hash to a DBM File (see man perltie).
Sample code, untested, could almost certainly be improved:
use DB_File;
tie %value2index, 'DB_File', 'value2index';
tie %index2value, 'DB_File', 'index2value';
sub index_count() {
return scalar %value2index;
}
sub value_exists() {
my $value = shift;
return exists($value2index{$value});
}
sub append() {
my $value = shift;
if (!value_exits($value)) { # prevent duplicate insertions
my $index = index_count() + 1;
$value2index{$value} = $index;
$index2value{$index} = $value;
}
}
sub find_index() {
my $value = shift;
return $value2index{$value};
}
sub find_value() {
my $index = shift;
return $index2value{$index};
}
Don't use this in a multi-threaded environment, there are non-atomic operations here.
How big are your Items? How much memory do you mind using? Are you items unique?
You could probably get away with something like this:
my #list; # This keeps the ordered list
my %keyval; # This maps key to value
my %valkey; # This maps value to key
on each insert you would:
push #list, value;
$valkey{$value} = $#list;
$keyval{$#list} = $value;
And for each of your requirements:
#Existence of a value:
if(exists($valkey{$value}));
#Existence of an index;
if(exists($keyval{$index}));
#value for an index:
$keyval{$index};
#value for a key:
$valkey{$value};
#Size
$#list;

Resources