How to determine the last element of a list in a foreach?

Asked

Viewed 4,980 times

10

Given any list, how to know if the foreach is in the last element?

There are some ways to do this, the one I see most is to save the last element and check if the current element is the same, but this way it is necessary to do the verification in each loop, as in the example below:

var last = list.Last();

foreach(var element in list)
{
    if(element == last)
    {
       //is last
    }
}

This is the best form in terms of performance?

  • 2

    There are cases that the Last() will end up iterating through the entire collection. Is it a good idea to use a for?

2 answers

13


There are some cases where using the method Last can end up causing an iteration across the entire collection, for example, when the collection is the result of a Linq query. This would make the collection iterated twice. Which, of course, you’ll want to avoid - especially if you’re working with large collections.

As you may already know, the foreach in itself has nothing that can help you in this. The solution is to find some alternative solution.

1. Using a for

That’s the first one I thought I’d use

for(int i; i < list.Count; i++)
{
    if(i == list.Count - 1)
    {
    }
}

2. Using a counter variable within the loop

There is also the option of "caching" the list size and using a counter variable (I see no advantages in this approach compared to the use of for)

var index = 0;
var count = list.Count; //ou Count() - você sabe
foreach (var item in list)
{
    if(++index == count)
    {
    }
}

3. Using the method IndexOf

foreach (var item in list)   
{   
     if (list.IndexOf(item) == list.Count - 1) 
     {
     }
}

Particularly, I would discourage you from using this approach because the method IndexOf looks for an element in the collection through a loop, that is, to each iteration of the loop main, another iteration will be done in the method IndexOf.

You can confirm this by viewing the source code of the method IndexOf of List<T> and that of the method IndexOf class Array

  • Yes, it’s an option, but I was wondering if foreach() had something more efficient. Ahhh, has how to do the Count - 1 with the foreach() also. xD

  • Yeah, I noticed that later too. I just edited the answer =D

4

I wouldn’t go this way.

As LINQ said this is a case where the for might be a better idea, so just compare the index with the Count() or Length from the list (possibly curly).

It is possible to use the same technique within a foreach, but it would have to have the index variable to compare with the total of items (minus 1 to indicate the number of the latter). If it is to have this variable, why not make a for? Something like that:

var count = list.Length;

foreach(var element in list) {
    if (--count > 0) {
        //is last
    }
}

In some cases it is possible to use some option with foreach. You could store the value of the last item and compare it to the current one. But you have to ensure the uniqueness of the values in the whole list. Risky.

The foreach is suitable when one wants to iterate on the list more evenly, in cases so it is not so suitable. But it depends on each case.

If you can use another form (it doesn’t make so much sense), I found a solution unlocking what the foreach ago:

using (var enumerator = .GetEnumerator()) {
    var last = !enumerator.MoveNext();
    T current;
    while (!last) {
        current = enumerator.Current;
        last = !enumerator.MoveNext();
        if (last) {
            //is last
        }       
    }
}

I don’t really like this solution, and it can’t be used in all cases, but you can use LINQ:

elements.ForEach((element, info) => {
    if (info.IsLast) {
        //is last
    }
});

I put in the Github for future reference.

You can have other creative solutions, variations of these, but in the end it changes little, you have to do what is most suitable for the case. You can’t hold on to a shape, use what’s best in the particular case.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.