get the first, the second, the third element of collection - laravel

I have a collection like that:
Collection {#750 ▼
#items: array:18 [▼
18 => User {#691 ▶}
19 => User {#696 ▶}
20 => User {#701 ▶}
]
}
Where 18, 19, 20 should vary
I tried to call with
$collection->get(0);
$collection->get(1);
$collection->get(2);
But obviously, it doesn't work
I found a workaround with shift, that return first element and remove it from the collection,
$el1 = $collection->shift();
$el2 = $collection->shift();
$el3 = $collection->shift();
But in this case, my original collection is destroyed.
Any idea how I should do it?

You can use slice() method:
$collection->slice(0, 3)
The slice method returns a slice of the collection starting at the given index. If you would like to limit the size of the returned slice, pass the desired size as the second argument.

You can use values() as:
$collection = $collection->values();
then you can use it as:
$collection->get(0);
$collection->get(1);
$collection->get(2);

You can either loop over them:
foreach ($collection as $element) {
// use $element
}
Or you can reset the keys to be sequentially indexed:
$collection = $collection->values();
$element1 = $collection->get(0);
$element2 = $collection->get(1);
$element3 = $collection->get(2);

There are many ways you can access thoes values. For example, you can get keys by calling:
$keys = $collection->keys()
Then call by the ordered keys you wanted:
if (isset($keys[0])) $collection->get($keys[0]);

Related

Put specific item on top without sorting others in laravel collection

I have an ordered laravel collection and i need too put element with id = 20 on top, without sorting other elements. Is it possible to do with sortBy?
You can try to use filter method
// Say $originalCollection is the response from the large request, with data from the database
$modifiedCollection = $originalCollection->filter(fn($item) => $item->id === 20)
->concat($originalCollection->filter(fn($item) => $item->id !== 20));
Or to be more intuitive you can use filter and reject methods
$modifiedCollection = $originalCollection->filter(fn($item) => $item->id === 20)
->concat($originalCollection->reject(fn($item) => $item->id === 20));
The $modifiedCollection will have record with id = 20 at the top and rest of the records will remain in the same order as in $originalCollection
if you want to put a specific item at the top of the array, simply add it separately.
$type = ['20' => 'Select Type'] + $your_sorted_array ;
Example:
$country = ['1' => 'Andorra'] + Countries::orderby('nicename')->pluck('name', 'id')->toArray();
Edit 1:Given new information, the on way you could "manually" do this is by using a combination of unset and unshift AFTER the array is built from the collection.
$key_value = $country[20];
unset($country[20]);
array_unshift($country, $key_value );
if your collection is not very large you can use combination of keyBy, pull and prepend methods
$originalCollection = Model::hereYourBigQuery()->get()->keyBy('id');
/*
now collection will look like this
{
'id1' => objectWithId1,
'id2' => objectWithId2,
...
20 => objectWithId20,
...
}
*/
// pull takes off element by its key
$toMakeFirst = $originalCollection->pull(20);
// prepend adding item into begining of the collection
// note that prepend will reindex collection so its keys will be set by default
$originalCollection->prepend($toMakeFirst);
upd:
if you want to stick with sort there is a way
$collection = Model::yourBigQuery()->get();
$sorted = $collection->sort(function($a, $b){return $a->id == 20 ? -1 : 1;})->values();
as said in docs method sort can take closure as argument and utilizes php uasort under the hood

Laravel collection opposite of diff

i have 2 collections:
$collection = collect([1,2,3,4]);
$collection2 = collect([1,5,6,4]);
with:
$collection->diff($collection2);
i get [2,3]
so what i want is to have the opposite value, [1,4]
Is there a way or method in collection to make this?
You can use duplicates method after mergging into single collection
$collection1 = collect([1,2,3,4]);
$collection2 = collect([1,5,6,4]);
$duplicates = $collection1
->merge($collection2)
->duplicates()
->values();
Will give you
Illuminate\Support\Collection {#1151
all: [
1,
4,
],
}
laravel collection intersect
intersect method removes any values from the original collection that are not present in the given array or collection.
$collection = collect([1,2,3,4]);
$collection2 = collect([1,5,6,4]);
$intersect = $collection->intersect($collection2);
$intersect->all();

PHP array_map() always change my first index value to zero value

I have a controller in Laravel
This is my collection
$milestones = $this->getmilestones();
dump($milestones);
and the value is
array:3 [▼
0 => "["109"
1 => "110"
2 => "111"]"
]
And I tried this code based on the answer here.
So, I have code like this
array_unshift($milestones, $milestones[0]);
unset($milestones[0]);
dump($milestones);
and the value is (index was changed)
array:3 [▼
1 => "["109"
2 => "110"
3 => "111"]"
]
So, after unshifting the collections, I tried to use array_map to convert array of strings to array of integers.
$milestones = array_map('intval', $milestones);
dump($milestones);
But, I still got the same value. The first index returns 0 like this
array:3 [▼
1 => 0
2 => 110
3 => 111
]
What should I do?
Try this one
array_splice($milestone, 0, 1);
dump($milestone);
Ah, finally I got the results that I wanted. I try to remove square brackets and double quote. Because milestones is collection. So my code is
$milestones = str_replace(array('[', ']', '"'),'',$milestones);
Thank you all for your help
Use array_values this should re-index your array the way you need it:
$milestones = array_values($milestones);
If $milestones is a collection:
$milestones = $milestones->values();
The method values() will call array_values on your items defined in your collection instance.
Source: http://php.net/manual/en/function.array-values.php

How to access the nth item in a Laravel collection?

I guess I am breaking all the rules by deliberately making a duplicate question...
The other question has an accepted answer. It obviously solved the askers problem, but it did not answer the title question.
Let's start from the beginning - the first() method is implemented approximately like this:
foreach ($collection as $item)
return $item;
It is obviously more robust than taking $collection[0] or using other suggested methods.
There might be no item with index 0 or index 15 even if there are 20 items in the collection. To illustrate the problem, let's take this collection out of the docs:
$collection = collect([
['product_id' => 'prod-100', 'name' => 'desk'],
['product_id' => 'prod-200', 'name' => 'chair'],
]);
$keyed = $collection->keyBy('product_id');
Now, do we have any reliable (and preferably concise) way to access nth item of $keyed?
My own suggestion would be to do:
$nth = $keyed->take($n)->last();
But this will give the wrong item ($keyed->last()) whenever $n > $keyed->count(). How can we get the nth item if it exists and null if it doesn't just like first() behaves?
Edit
To clarify, let's consider this collection:
$col = collect([
2 => 'a',
5 => 'b',
6 => 'c',
7 => 'd']);
First item is $col->first(). How to get the second?
$col->nth(3) should return 'c' (or 'c' if 0-based, but that would be inconsistent with first()). $col[3] wouldn't work, it would just return an error.
$col->nth(7) should return null because there is no seventh item, there are only four of them. $col[7] wouldn't work, it would just return 'd'.
You could rephrase the question as "How to get nth item in the foreach order?" if it's more clear for some.
I guess faster and more memory-efficient way is to use slice() method:
$collection->slice($n, 1);
You can try it using values() function as:
$collection->values()->get($n);
Based on Alexey's answer, you can create a macro in AppServiceProvider (add it inside register method):
use Illuminate\Support\Collection;
Collection::macro('getNth', function ($n) {
return $this->slice($n, 1)->first();
});
and then, you can use this throughout your application:
$collection = ['apple', 'orange'];
$collection->getNth(0) // returns 'apple'
$collection->getNth(1) // returns 'orange'
$collection->getNth(2) // returns null
$collection->getNth(3) // returns null
you may use offsetGet since Collection class implements ArrayAccess
$lines->offsetGet($nth);
Maybe not the best option, but, you can get item from array inside collection
$collection->all()[0]

How to get an array of 2-tuples from Laravel query builder?

Basically, I want to do this:
$locals['companies'] = Company::orderBy('name')->get(['id','name'])->map(function($c) { return [$c->id, $c->name]; })->toArray();
But without such a verbose map function. Isn't there a get-like method that will return flat numeric arrays instead of objects?
To be clear, the output should look like this:
array:4 [
0 => array:2 [
0 => 4
1 => "My Company"
]
1 => array:2 [
0 => 14
1 => "Example Company"
]
2 => array:2 [
0 => 13
1 => "Best Company"
]
3 => array:2 [
0 => 12
1 => "Super Co"
]
]
This is what I mean by 2-tuples: two-element numeric arrays. I know they don't exist in PHP, but the concept is the same; each entry has a fixed length.
There is no function out of the box to do this, but Laravel's Collection is Macroable, so you can add your own function to it to do this.
For example, somewhere in your code (like the boot() method of your AppServiceProvider), you can add a new method to the Collection:
// add toIndexedArray method to collections
\Illuminate\Support\Collection::macro('toIndexedArray', function() {
return array_map('array_values', $this->toArray());
});
Now you can use this new method like any other normal Collection method, so your final code would be:
$locals['companies'] = Company::orderBy('name')->get(['id','name'])->toIndexedArray();
If this is something you need a lot, you can change the PDO fetch mode in config/database.php to PDO::FETCH_NUM. I'm assuming it's possible to change it on-the-fly as well, but the code probably won't look that great. I don't think there's a Laravel command to change it for a single query, I'm afraid.
Otherwise, since the array is multidimensional, I'm afraid you do need to map over them somehow, and Laravel collections don't work nicely with e.g. ->map('array_values') which would have been a lot cleaner.
You could wrap it in array_map('array_values', $array) but that seems silly.
At least you could make it a little shorter if you change ->map() to ->transform() - then you don't need to tack on the ->toArray() at the end.
Use pluck():
$locals['companies'] = Company::orderBy('name')->pluck('id', 'name')->toArray();
If you need a list for Form::select this will work:
$locals['companies'] = Company::orderBy('name')->pluck('name', 'id');
You can omit the map function and just do:
$locals['companies'] = Company::orderBy('name')->get(['id','name'])->toArray();

Resources