How do you get the index of the current iteration of a foreach loop?

Viewed 1304297

Is there some rare language construct I haven't encountered (like the few I've learned recently, some on Stack Overflow) in C# to get a value representing the current iteration of a foreach loop?

For instance, I currently do something like this depending on the circumstances:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}
35 Answers

The foreach is for iterating over collections that implement IEnumerable. It does this by calling GetEnumerator on the collection, which will return an Enumerator.

This Enumerator has a method and a property:

  • MoveNext()
  • Current

Current returns the object that Enumerator is currently on, MoveNext updates Current to the next object.

The concept of an index is foreign to the concept of enumeration, and cannot be done.

Because of that, most collections are able to be traversed using an indexer and the for loop construct.

I greatly prefer using a for loop in this situation compared to tracking the index with a local variable.

Finally C#7 has a decent syntax for getting an index inside of a foreach loop (i. e. tuples):

foreach (var (item, index) in collection.WithIndex())
{
    Debug.WriteLine($"{index}: {item}");
}

A little extension method would be needed:

using System.Collections.Generic;

public static class EnumExtension {
    public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)       
       => self.Select((item, index) => (item, index));
}

Could do something like this:

public static class ForEachExtensions
{
    public static void ForEachWithIndex<T>(this IEnumerable<T> enumerable, Action<T, int> handler)
    {
        int idx = 0;
        foreach (T item in enumerable)
            handler(item, idx++);
    }
}

public class Example
{
    public static void Main()
    {
        string[] values = new[] { "foo", "bar", "baz" };

        values.ForEachWithIndex((item, idx) => Console.WriteLine("{0}: {1}", idx, item));
    }
}

I disagree with comments that a for loop is a better choice in most cases.

foreach is a useful construct, and not replaceble by a for loop in all circumstances.

For example, if you have a DataReader and loop through all records using a foreach it automatically calls the Dispose method and closes the reader (which can then close the connection automatically). This is therefore safer as it prevents connection leaks even if you forget to close the reader.

(Sure it is good practise to always close readers but the compiler is not going to catch it if you don't - you can't guarantee you have closed all readers but you can make it more likely you won't leak connections by getting in the habit of using foreach.)

There may be other examples of the implicit call of the Dispose method being useful.

Literal Answer -- warning, performance may not be as good as just using an int to track the index. At least it is better than using IndexOf.

You just need to use the indexing overload of Select to wrap each item in the collection with an anonymous object that knows the index. This can be done against anything that implements IEnumerable.

System.Collections.IEnumerable collection = Enumerable.Range(100, 10);

foreach (var o in collection.OfType<object>().Select((x, i) => new {x, i}))
{
    Console.WriteLine("{0} {1}", o.i, o.x);
}

Just add your own index. Keep it simple.

int i = 0;
foreach (var item in Collection)
{
    item.index = i;
    ++i;
}

Here's a solution I just came up with for this problem

Original code:

int index=0;
foreach (var item in enumerable)
{
    blah(item, index); // some code that depends on the index
    index++;
}

Updated code

enumerable.ForEach((item, index) => blah(item, index));

Extension Method:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
    {
        var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
        enumerable.Select((item, i) => 
            {
                action(item, i);
                return unit;
            }).ToList();

        return pSource;
    }

It's only going to work for a List and not any IEnumerable, but in LINQ there's this:

IList<Object> collection = new List<Object> { 
    new Object(), 
    new Object(), 
    new Object(), 
    };

foreach (Object o in collection)
{
    Console.WriteLine(collection.IndexOf(o));
}

Console.ReadLine();

@Jonathan I didn't say it was a great answer, I just said it was just showing it was possible to do what he asked :)

@Graphain I wouldn't expect it to be fast - I'm not entirely sure how it works, it could reiterate through the entire list each time to find a matching object, which would be a helluvalot of compares.

That said, List might keep an index of each object along with the count.

Jonathan seems to have a better idea, if he would elaborate?

It would be better to just keep a count of where you're up to in the foreach though, simpler, and more adaptable.

int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

This would work for collections supporting IList.

//using foreach loop how to get index number:
    
foreach (var result in results.Select((value, index) => new { index, value }))
    {
     //do something
    }

Unless your collection can return the index of the object via some method, the only way is to use a counter like in your example.

However, when working with indexes, the only reasonable answer to the problem is to use a for loop. Anything else introduces code complexity, not to mention time and space complexity.

I don't believe there is a way to get the value of the current iteration of a foreach loop. Counting yourself, seems to be the best way.

May I ask, why you would want to know?

It seems that you would most likley be doing one of three things:

1) Getting the object from the collection, but in this case you already have it.

2) Counting the objects for later post processing...the collections have a Count property that you could make use of.

3) Setting a property on the object based on its order in the loop...although you could easily be setting that when you added the object to the collection.

This way you can use the index and value using LINQ:

ListValues.Select((x, i) => new { Value = x, Index = i }).ToList().ForEach(element =>
    {
        // element.Index
        // element.Value

    });

i want to discuss this question more theoretically (since it has already enough practical answers)

.net has a very nice abstraction model for groups of data (a.k.a. collections)

  • At the very top, and the most abstract, you have an IEnumerable it's just a group of data that you can enumerate. It doesn't matter HOW you enumerate, it's just that you can enumerate some data. And that enumeration is done by a completely different object, an IEnumerator

these interfaces are defined is as follows:

//
// Summary:
//     Exposes an enumerator, which supports a simple iteration over a non-generic collection.
public interface IEnumerable
{
    //
    // Summary:
    //     Returns an enumerator that iterates through a collection.
    //
    // Returns:
    //     An System.Collections.IEnumerator object that can be used to iterate through
    //     the collection.
    IEnumerator GetEnumerator();
}

//
// Summary:
//     Supports a simple iteration over a non-generic collection.
public interface IEnumerator
{
    //
    // Summary:
    //     Gets the element in the collection at the current position of the enumerator.
    //
    // Returns:
    //     The element in the collection at the current position of the enumerator.
    object Current { get; }

    //
    // Summary:
    //     Advances the enumerator to the next element of the collection.
    //
    // Returns:
    //     true if the enumerator was successfully advanced to the next element; false if
    //     the enumerator has passed the end of the collection.
    //
    // Exceptions:
    //   T:System.InvalidOperationException:
    //     The collection was modified after the enumerator was created.
    bool MoveNext();
    //
    // Summary:
    //     Sets the enumerator to its initial position, which is before the first element
    //     in the collection.
    //
    // Exceptions:
    //   T:System.InvalidOperationException:
    //     The collection was modified after the enumerator was created.
    void Reset();
}
  • as you might have noticed, the IEnumerator interface doesn't "know" what an index is, it just knows what element it's currently pointing to, and how to move to the next one.

  • now here is the trick: foreach considers every input collection an IEnumerable, even if it is a more concrete implementation like an IList<T> (which inherits from IEnumerable), it will only see the abstract interface IEnumerable.

  • what foreach is actually doing, is calling GetEnumerator on the collection, and calling MoveNext until it returns false.

  • so here is the problem, you want to define a concrete concept "Indices" on an abstract concept "Enumerables", the built in foreach construct doesn't give you that option, so your only way is to define it yourself, either by what you are doing originally (creating a counter manually) or just use an implementation of IEnumerator that recognizes indices AND implement a foreach construct that recognizes that custom implementation.

personally i would create an extension method like this

public static class Ext
{
    public static void FE<T>(this IEnumerable<T> l, Action<int, T> act)
    {
        int counter = 0;
        foreach (var item in l)
        {
            act(counter, item);
            counter++;
        }
    }
}

and use it like this

var x = new List<string>() { "hello", "world" };
x.FE((ind, ele) =>
{
    Console.WriteLine($"{ind}: {ele}");
});

this also avoids any unnecessary allocations seen in other answers.

Related