1

I recently encounter a couple of cases where it makes me wonder if there is any way to get internal index, or if not, to generate index efficiently when using LINQ.

Consider the following case:

List<int> list = new List() { 7, 4, 5, 1, 7, 8 };

In C#, if we are to return the indexes of "7" on the above array using LINQ, we cannot simply using IndexOf/LastIndexOf - for it will only return the first/last result.

This will not work:

var result = list.Where(x => x == 7).Select(y => list.IndexOf(y));

We have several workarounds for doing it, however.

For instance, one way of doing it could be this (which I typically do):

int index = 0;
var result = from x in list
        let indexNow = index++
        where x == 7
        select indexNow;

It will introduce additional parameter indexNow by using let.

And also another way:

var result = Enumerable.Range(0, list.Count).Where(x => list.ElementAt(x) == 7);

It will generate another set of items by using Enumerable.Range and pick the result from it.

Now, I am wondering if there is any alternative, simpler way of doing it, such as:

  1. if there is any (built-in) way to get the internal index of the IEnumerable without declaring something with let or
  2. to generate the index for the IEnumerable using something other than Enumerable.Range (perhaps something like new? Which I am not too familiar how to do it), or
  3. anything else which could shorten the code but still getting the indexes of the IEnumerable.
Ian
  • 30,182
  • 19
  • 69
  • 107
  • 4
    `var result = list.Select((x, idx) => idx);` where idx is the index of each element in the IEnumerable - apologies if I'm misunderstanding. – TVOHM Feb 04 '16 at 10:41
  • @TVOHM Thanks for the comment you most probably are right... It is one of the overloads which I never realize till now.. – Ian Feb 04 '16 at 10:54

1 Answers1

2

From IEnumerable.Select with index, How to get index using LINQ?, and so on: IEnumerable<T>.Select() has an overload that provides an index.

Using this, you can:

  1. Project into an anonymous type (or a Tuple<int, T> if you want to expose it to other methods, anonymous types are local) that contains the index and the value.
  2. Filter those results for the value you're looking for.
  3. Project only the index.

So the code will look like this:

var result = list.Select((v, i) => new { Index = i, Value = v })
                 .Where(i => i.Value == 7)
                 .Select(i => i.Index);

Now result will contain an enumerable containing all indexes.

Note that this will only work for source collection types that guarantee insertion order and provide a numeric indexer, such as List<T>. When you use this for a Dictionary<TKey, TValue> or HashSet<T>, the resulting indexes will not be usable to index into the dictionary.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • This is new to me. I didn't know that Select has overload which provide index. But I heard that IEnumerable is not necessarily ordered? Yet we still could get the index by doing this? – Ian Feb 04 '16 at 10:46
  • "That depends on the spurce collection." as in..? Some collection cannot be ordered such (with the solution you provide) and thus cannot be indexed but some can? How do we know those which can be indexed and which can't? – Ian Feb 04 '16 at 10:50
  • 1
    ok, thanks for the clarification and input. It is indeed useful for me. – Ian Feb 04 '16 at 10:56