53

Working of foreach: As I know,

foreach is a loop which iterates through a collection or array one by one, starting from 0 index till the last item of the collection.

So, if I have n items in an array.

foreach (var item in arr)
{

}  

then, In, 1st iteration, item=arr[0];
then, in 2nd, item=arr[1];
.
.
.
in last (nth), item=arr[n-1];

Conclusion: from working it seems that at each iteration it knows that which value to be taken from array or it knows the index of the item to be taken from array.

Now my question: How can I get index of an item without using a new variable?

foreach (string item in mylist)
{
   if (item == "myitem")
   {
       // get index of item
       break;
   }
}
Javed Akram
  • 14,220
  • 23
  • 77
  • 113
  • The canonical question is *[How do you get the index of the current iteration of a foreach loop?](http://stackoverflow.com/questions/43021)*. – Peter Mortensen Jul 28 '15 at 12:29
  • 1
    Possible duplicate of *[How do you get the index of the current iteration of a foreach loop?](http://stackoverflow.com/questions/43021/how-do-you-get-the-index-of-the-current-iteration-of-a-foreach-loop)*. – Peter Mortensen Jul 28 '15 at 12:30

10 Answers10

92

It depends what you mean by "it". The iterator knows what index it's reached, yes - in the case of a List<T> or an array. But there's no general index within IEnumerator<T>. Whether it's iterating over an indexed collection or not is up to the implementation. Plenty of collections don't support direct indexing.

(In fact, foreach doesn't always use an iterator at all. If the compile-time type of the collection is an array, the compiler will iterate over it using array[0], array[1] etc. Likewise the collection can have a method called GetEnumerator() which returns a type with the appropriate members, but without any implementation of IEnumerable/IEnumerator in sight.)

Options for maintaining an index:

  • Use a for loop
  • Use a separate variable
  • Use a projection which projects each item to an index/value pair, e.g.

     foreach (var x in list.Select((value, index) => new { value, index }))
     {
         // Use x.value and x.index in here
     }
    
  • Use my SmartEnumerable class which is a little bit like the previous option

All but the first of these options will work whether or not the collection is naturally indexed.

Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
20

Use for instead of foreach. foreach doesn't expose its inner workings, it enumerates anything that is IEnumerable (which doesn't have to have an index at all).

for (int i=0; i<arr.Length; i++)
{
    ...
}

Besides, if what you're trying to do is find the index of a particular item in the list, you don't have to iterate it at all by yourself. Use Array.IndexOf(item) instead.

Yodan Tauber
  • 3,717
  • 2
  • 24
  • 48
13

Your understanding of foreach is incomplete.

It works with any type that exposes IEnumerable (or implements a GetEnumerable method) and uses the returned IEnumerator to iterate over the items in the collection.

How the Enumerator does this (using an index, yield statement or magic) is an implementation detail.

In order to achieve what you want, you should use a for loop:

for (int i = 0; i < mylist.Count; i++)
{
}

Note:

Getting the number of items in a list is slightly different depending on the type of list

For Collections: Use Count   [property]
For Arrays:      Use Length  [property]
For IEnumerable: Use Count() [Linq method]
Oded
  • 463,167
  • 92
  • 837
  • 979
  • `foreach` actually works with any type that has a `GetEnumerator` method (if that method then returns an object with an appropriate `Current` property and `MoveNext` method). The types don't actually need to implement `IEnumerable`/`IEnumerator`. – LukeH Dec 02 '10 at 17:17
11

Or even more simple if you don't want to use a lot of linq and for some reason don't want to use a for loop.

int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}
BlueVoodoo
  • 3,516
  • 4
  • 27
  • 37
3

Probably pointless, but...

foreach (var item in yourList.Select((Value, Index) => new { Value, Index }))
{
    Console.WriteLine("Value=" + item.Value + ", Index=" + item.Index);
}
LukeH
  • 242,140
  • 52
  • 350
  • 400
1

This is only true if you're iterating through an array; what if you were iterating through a different kind of collection that has no notion of accessing by index? In the array case, the easiest way to retain the index is to simply use a vanilla for loop.

Dan Bryant
  • 26,669
  • 3
  • 48
  • 98
1

Without Custom Foreach Version:

datas.Where((data, index) =>
{
    //Your Logic
    return false;
}).Any();

In some simple case,my way is using where + false + any.
It is fater a little than foreach + select((data,index)=>new{data,index}),and without custom Foreach method.

MyLogic:

  • use statement body run your logic.
  • because return false,new Enumrable data count is zero.
  • use Any() let yeild run.

Benchmark Test Code

[RPlotExporter, RankColumn]
public class BenchmarkTest
{
    public static IEnumerable<dynamic> TestDatas = Enumerable.Range(1, 10).Select((data, index) => $"item_no_{index}");

    [Benchmark]
    public static void ToArrayAndFor()
    {
        var datats = TestDatas.ToArray();
        for (int index = 0; index < datats.Length; index++)
        {
            var result = $"{datats[index]}{index}";
        }
    }

    [Benchmark]
    public static void IEnumrableAndForach()
    {
        var index = 0;
        foreach (var item in TestDatas)
        {
            index++;
            var result = $"{item}{index}";
        }
    }

    [Benchmark]
    public static void LinqSelectForach()
    {
        foreach (var item in TestDatas.Select((data, index) => new { index, data }))
        {
            var result = $"{item.data}{item.index}";
        }
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToList()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToList();
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToArray()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToArray();
    }

    [Benchmark]
    public static void LinqWhereStatementBodyAny()
    {
        TestDatas.Where((data, index) =>
        {
            var result = $"{data}{index}";
            return false;
        }).Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<BenchmarkTest>();

        System.Console.Read();
    }
}

Benchmark Result :

                         Method |     Mean |     Error |    StdDev | Rank |
------------------------------- |---------:|----------:|----------:|-----:|
                  ToArrayAndFor | 4.027 us | 0.0797 us | 0.1241 us |    4 |
            IEnumrableAndForach | 3.494 us | 0.0321 us | 0.0285 us |    1 |
               LinqSelectForach | 3.842 us | 0.0503 us | 0.0471 us |    3 |
  LinqSelectStatementBodyToList | 3.822 us | 0.0416 us | 0.0389 us |    3 |
 LinqSelectStatementBodyToArray | 3.857 us | 0.0764 us | 0.0785 us |    3 |
      LinqWhereStatementBodyAny | 3.643 us | 0.0693 us | 0.0712 us |    2 |
ITWeiHan
  • 5,074
  • 1
  • 8
  • 29
0

Not all collections have indexes. For instance, I can use a Dictionary with foreach (and iterate through all the keys and values), but I can't write get at individual elements using dictionary[0], dictionary[1] etc.

If I did want to iterate through a dictionary and keep track of an index, I'd have to use a separate variable that I incremented myself.

Tim Robinson
  • 50,349
  • 9
  • 115
  • 129
0

The sequence being iterated in a foreach loop might not support indexing or know such a concept it just needs to implement a method called GetEnumerator that returns an object that as a minimum has the interface of IEnumerator though implmenting it is not required. If you know that what you iterate does support indexing and you need the index then I suggest to use a for loop instead.

An example class that can be used in foreach:

    class Foo {

        public iterator GetEnumerator() {
            return new iterator();
        }

        public class iterator {
            public Bar Current {
                get{magic}
            }

            public bool MoveNext() {
                incantation
            }
        }
    }
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Rune FS
  • 20,632
  • 6
  • 57
  • 92
  • `GetEnumerator()` doesn't even need to return a type that implements `IEnumerator`... – Jon Skeet Dec 02 '10 at 17:13
  • @Jon Skeet: What do you mean? Isn't `IEnumerator GetEnumerator();` the signature of the only method defined within `IEnumerable`? – Yodan Tauber Dec 02 '10 at 17:17
  • @Yodan: Yes, but you don't have to implement IEnumerable either :) – Jon Skeet Dec 02 '10 at 17:19
  • Well, I tend to forget about .NET > 2.0, as that is the framework version I'm currently stuck with :) – Yodan Tauber Dec 02 '10 at 17:21
  • 1
    @yodan I'm pretty sure that It's actually a 1.0 artifact that you can in essence really on duck typing when it comes to foreach – Rune FS Dec 02 '10 at 17:36
  • @Yodan: This has nothing to do with .NET 2.0. It's *partly* for compile-time duck-typing as Rune mentions, but there's also a matter of efficiency. For example, although `List` *does* implement `IEnumerable` explicitly, it *also* has a strongly-typed `GetEnumerator()` method which returns a mutable struct. Apparently this makes iterating over a list which is known to be a list at compile-time more efficient... but it also involves mutable structs, which are generally nasty :( – Jon Skeet Dec 02 '10 at 17:46
0

From MSDN:

The foreach statement repeats a group of embedded statements for each element in an array or an object collection that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable(Of T) interface.

So, it's not necessarily Array. It could even be a lazy collection with no idea about the count of items in the collection.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Manish Basantani
  • 15,105
  • 22
  • 65
  • 96