28

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.

Community
  • 1
  • 1
Džuris
  • 2,115
  • 3
  • 27
  • 55

5 Answers5

33

I guess faster and more memory-efficient way is to use slice() method:

$collection->slice($n, 1);
Alexey Mezenin
  • 158,981
  • 26
  • 290
  • 279
  • This seems nice, but I am not sure about the behaviour if `$n > $collection->count()`. It appears that `slice()` uses PHPs `array_slice()` and I can't find info on behaviour if `$n` exceeds the size of array. Maybe it's unspecified and unreliable :| – Džuris Nov 06 '16 at 13:39
  • It will just return an empty collection. You even can do easy check with `->isEmpty()` or `empty($result)`. – Alexey Mezenin Nov 06 '16 at 13:50
  • 2
    Mkay, then `$collection->slice($n, 1)->fiirst()` will return nth item if it exists and `null` otherwise. Seems great :) – Džuris Nov 08 '16 at 15:34
  • Two issues with this answer to consider: a) this returns a collection of one, so `$collection->slice($n, 1)->first()` must be used; b) `$n` is zero indexed, so will return the `$n+1` element from the collection. So for this example use: `$collection->slice($n-1, 1)->first()` – Jason Aug 16 '21 at 09:50
11

You can try it using values() function as:

$collection->values()->get($n);
Amit Gupta
  • 17,072
  • 4
  • 41
  • 53
  • This seems the best solution so far, can't think of an example where it fails :) – Džuris Nov 06 '16 at 13:22
  • 3
    It's working solution but it will duplicate whole collection and may kill an app, if collection is big by itself (collection of articles with relations or something similar). – Alexey Mezenin Nov 06 '16 at 13:46
8

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
Muhammad Syauqy
  • 401
  • 4
  • 7
7

you may use offsetGet since Collection class implements ArrayAccess

$lines->offsetGet($nth);
Zymawy
  • 1,511
  • 16
  • 15
1

Maybe not the best option, but, you can get item from array inside collection

$collection->all()[0] 
tylik
  • 1,018
  • 1
  • 11
  • 21