C# tip: how to get the index of an item in a foreach loop
Do you need the index of the current item in a foreach loop with C#? Here you’ll see two approaches.
Table of Contents
Just a second! π«·
If you are here, it means that you are a software developer. So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible - I don't want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding.
- Davide
Sometimes, when looping over a collection of elements in C#, you need not only the items itself, but also its position in the collection.
How to get the index of the current element in a foreach
loop?
The easiest way is to store and update the index in a separate variable
List<string> myFriends = new List<string> {
"Emma", "Rupert", "Daniel", "Maggie", "Alan"
};
int index = 0;
foreach (var friend in myFriends)
{
Console.WriteLine($"Friend {index}: {friend}");
index++;
}
This works fine, nothing to add.
But, if you want something a little more elegant and compact, you can use the Select
method from LINQ:
List<string> myFriends = new List<string> {
"Emma", "Rupert", "Daniel", "Maggie", "Alan"
};
foreach (var friend in myFriends.Select((name, index) => (name, index)))
{
Console.WriteLine($"Friend {friend.index}: {friend.name}");
}
Why do I like this solution?
- it’s more compact than the first one
- there is a tight bond between the current item in the loop and the index
- I find it cleaner and easier to read
Or… You can just replace it with a simple for
loop!
What about performance?
I’ve done a simple benchmark (see here), and it resulted that for lists with less than 1000 items, the first solution is faster, and for lists with 10000 items, using LINQ is way faster than using an external index.
Size (#items) | With simple index (ms) | With LINQ (ms) |
---|---|---|
100 | 96 | 128 |
1000 | 1225 | 1017 |
10000 | 5523 | 786 |
This happens with .NET 5.
Update 2021-06-09: the previous benchmark was wrong!!π
The times listed in the previous table were misleading: I calculated those durations using a StopWatch
and calling it in different methods.
But, when performing a more precise benchmark using Benchmark.NET, the results are totally different.
With .NET Core 3.1.14 I get the following results:
Method | array | Mean | Error |
---|---|---|---|
WithIndex | Int32[10000] | 269,386.4 ns | 6,168.76 ns |
WithLinq | Int32[10000] | 396,421.3 ns | 7,778.64 ns |
WithIndex | Int32[1000] | 25,438.3 ns | 504.03 ns |
WithLinq | Int32[1000] | 39,981.3 ns | 1,578.48 ns |
WithIndex | Int32[100] | 2,440.8 ns | 48.34 ns |
WithLinq | Int32[100] | 3,687.7 ns | 73.60 ns |
WithIndex | Int32[10] | 185.6 ns | 3.52 ns |
WithLinq | Int32[10] | 369.5 ns | 9.51 ns |
While with .NET 5 I get these results:
Method | array | Mean | Error |
---|---|---|---|
WithIndex | Int32[10000] | 134,431.02 ns | 2,181.244 ns |
WithLinq | Int32[10000] | 273,691.68 ns | 5,334.833 ns |
WithIndex | Int32[1000] | 12,961.69 ns | 233.351 ns |
WithLinq | Int32[1000] | 26,023.63 ns | 495.341 ns |
WithIndex | Int32[100] | 1,088.25 ns | 21.485 ns |
WithLinq | Int32[100] | 2,299.12 ns | 21.901 ns |
WithIndex | Int32[10] | 48.01 ns | 0.748 ns |
WithLinq | Int32[10] | 228.66 ns | 4.531 ns |
As you can see, actually using LINQ is slower than using a simple index. While in .NET Core 3 the results were quite similar, with .NET 5 there was a huge improvement both cases, but now using a simple index is two times faster than using LINQ.
SORRY FOR THAT MISLEADING INFO! Thank you, Ben, for pointing it out in the comments section! π
Below you can see the code I used for this benchmark. I you want to get started with Benchmark.NET, look at the documentation or to my article Enum.HasFlag performance with BenchmarkDotNet
public class ForeachIndexBenchmark
{
public IEnumerable<int[]> Arrays()
{
yield return Enumerable.Range(0, 10).ToArray();
yield return Enumerable.Range(0, 100).ToArray();
yield return Enumerable.Range(0, 1000).ToArray();
yield return Enumerable.Range(0, 10000).ToArray();
}
[Benchmark]
[ArgumentsSource(nameof(Arrays))]
public void WithIndex(int[] array)
{
int index = 0;
var asString = "0";
foreach (var friend in array)
{
asString = "" + index;
index++;
}
}
[Benchmark]
[ArgumentsSource(nameof(Arrays))]
public void WithLinq(int[] array)
{
var asString = "0";
foreach (var friend in array.Select((item, index) => (item, index)))
{
asString = "" + friend.index;
}
}
}
This article first appeared on Code4IT
Conclusions
We’ve discovered that there are many ways to use indexes tightly bound with items. If you look at performance, go for the simplest ways (for
loop or foreach
with simple index). If you want a more concise code, go for LINQ.
Anything else to add?
π Let’s discuss it on Twitter or on the comment section below!
π§