7

I have IEnumerable list of customerList and index. Now I want to get item of IEnumerable based on index.

For example, if index = 3, it should provide me 3rd item of the IEnumerable .

Please guide.

IEnumerable<Customer> customerList = new Customer[] 
{ 
     new Customer { Name = "test1", Id = 999 }, 
     new Customer { Name = "test2", Id = 915 }, 
     new Customer { Name = "test8", Id = 986 },
     new Customer { Name = "test9", Id = 988 },
     new Customer { Name = "test4", Id = 997 },
     new Customer { Name = "test5", Id = 920 },
};

int currentIndex = 3;   //want to get object Name = "test8", Id = 986
Nanji Mange
  • 2,155
  • 4
  • 29
  • 63
  • 8
    *Don't* use `IEnumerable` here. Use `IList` – Panagiotis Kanavos Nov 02 '18 at 14:41
  • You would have to do `customerList.ToList()[2];`. Remember that indexes start at 0, not 1. – Icemanind Nov 02 '18 at 14:43
  • 2
    @Icemanind: `customerList.ToList()` may create an *enourmous* list (or even throw `OutOfMemory` exception) when we want just a *2-nd* item – Dmitry Bychenko Nov 02 '18 at 14:43
  • @Icemanind there's no reason to use `ToList()` when the array is *already* an IList. Just cast it to the correct type. Better yet, *don't* use `IEnumerable` in the first place – Panagiotis Kanavos Nov 02 '18 at 14:47
  • I don't see how [_"How to get the index of an element in an IEnumerable"_](https://stackoverflow.com/questions/1290603/how-to-get-the-index-of-an-element-in-an-ienumerable) can be a duplicate of [_"How to get the item from an IEnumerable using its index"_](https://stackoverflow.com/questions/53120635/how-to-get-item-from-ienumerable-collection-using-its-index-in-c). That are two different things and the answers are also not related. – Tim Schmelter Nov 02 '18 at 16:02
  • There is really something wrong with stackoverflow voting question, this question has right answer, if someone votes down no body know why ?? i often see voting down was because of some misunderstanding, or some time OP fix the problem but still it stays voted down that does not make sense. – sairfan Apr 17 '19 at 15:38

4 Answers4

27

For example, if index = 3, it should provide me 3rd item of the IEnumerable

You know that indexes are zero based in .NET? However, you can use ElementAt:

Customer c = customerList.ElementAt(currentIndex); // 4th

Use ElementAtOrDefault to prevent an exception if there are not enough items, then you get null:

Customer c = customerList.ElementAtOrDefault(currentIndex); // 4th or null

These methods are optimized in a way that they use the IList<T> indexer. So in your example there is actually an Customer[] which implements that interface, hence it will use the indexer.

If the sequence does not implement IList<T> it will be enumerated to find the item at this index.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
3

Don't use IEnumerable if you want index-based access. Use IList instead :

IList<Customer> customerList = new Customer[] 
{ 
    new Customer { Name = "test1", Id = 999 }, 
    new Customer { Name = "test2", Id = 915 }, 
    new Customer { Name = "test8", Id = 986 },
    new Customer { Name = "test9", Id = 988 },
    new Customer { Name = "test4", Id = 997 },
    new Customer { Name = "test5", Id = 920 },
};

With IEnumerable, the only option is to use the ElementAt() extension method.

IEnumerable<T> guarantees only that a collection can be enumerated. It makes no other promises about accessing elements. ElementAt allows access to a specific element by enumerating that collection one item at a time until the required element is reached. That can be expensive.

Luckily, ElementAt checks to see whether the argument is an IList and uses index-based access if possible.

That doesn't mean you should use IEnumerable<T> though. It will confuse maintainers (including you) that don't know what the actual instance behind the variable is. It will also cause performance issues if something that isn't an IList is ever assigned to that variable

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
1

This can be achieved using ElementAt method.

If your IEnumerable is not a materialized collection, but a generated sequence, calling ElementAt method multiple times will cause the sequence to be generated multiple times. This is usually not desired, as it unnecessarily consumes resources.

Some of the answers above suggest using IList instead of IEnumerable. Yes, definitely it'll make accessing by index less cumbersome, but using IList has a nasty side effect it makes the collection mutable. I'd use IReadOnlyList instead.

IReadOnlyList<Customer> customerList = new Customer[]
{
    new Customer { Name = "test1", Id = 999 },
    new Customer { Name = "test2", Id = 915 },
    new Customer { Name = "test8", Id = 986 },
    new Customer { Name = "test9", Id = 988 },
    new Customer { Name = "test4", Id = 997 },
    new Customer { Name = "test5", Id = 920 },
};

int currentIndex = 3;   //want to get object Name = "test8", Id = 986

var result = customerList[currentIndex];
customerList.Add(new Customer()); // doesn't compile
customerList[currentIndex] = new Customer(); // doesn't compile
Andrzej Gis
  • 13,706
  • 14
  • 86
  • 130
0

IEnumerable is a 'streaming' data type, so think of it like a stream instead of an array.

var yourCustomer = customerList.Skip(2).First()

However if you wanted a more array like syntax IList may be a better abstraction for your use case.

WhiteleyJ
  • 1,393
  • 1
  • 22
  • 29
  • _"IEnumerable is a 'streaming' data type"_ No, that's not said anywhere. It's just the base interface of everything that is enumerable. Your implementation would just prevent optimizations and ensure that the sequence is enumerated – Tim Schmelter Nov 02 '18 at 14:50
  • That's a bad idea. `IEnumerable` is *not* a streaming data type. Accessing elements is already available through `ElementAt` which does what the combination of `Skip.First` do *as a worst case scenario*. If the collection is actually an IList, `ElementAt` will use indexed access. – Panagiotis Kanavos Nov 02 '18 at 14:54
  • Note I say IEnumerable is a streaming type because the interface has one function, GetEnumerator. GetEnumerator returns an IEnumerator that can only MoveNext and Reset. Everything else is a static extension method. See: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerator-1?view=netframework-4.7.2 – WhiteleyJ Nov 06 '18 at 14:53