RichC
RichC

Reputation: 7879

How do you find the last loop in a For Each (VB.NET)?

How can I determine if I'm in the final loop of a For Each statement in VB.NET?

Upvotes: 28

Views: 46999

Answers (13)

Alex Essilfie
Alex Essilfie

Reputation: 12613

The generally, collections on which you can perform For Each on implement the IEnumerator interface. This interface has only two methods, MoveNext and Reset and one property, Current.

Basically, when you use a For Each on a collection, it calls the MoveNext function and reads the value returned. If the value returned is True, it means there is a valid element in the collection and element is returned via the Current property. If there are no more elements in the collection, the MoveNext function returns False and the iteration is exited.

From the above explanation, it is clear that the For Each does not track the current position in the collection and so the answer to your question is a short No.

If, however, you still desire to know if you're on the last element in your collection, you can try the following code. It checks (using LINQ) if the current item is the last item.

For Each item in Collection
    If item Is Collection.Last Then
        'do something with your last item'
    End If
Next

It is important to know that calling Last() on a collection will enumerate the entire collection. It is therefore not recommended to call Last() on the following types of collections:

  • Streaming collections
  • Computationally expensive collections
  • Collections with high tendency for mutation

For such collections, it is better to get an enumerator for the collection (via the GetEnumerator() function) so you can keep track of the items yourself. Below is a sample implementation via an extension method that yields the index of the item, as well as whether the current item is the first or last item in the collection.

<Extension()>
Public Iterator Function EnumerateEx(Of T)(collection As IEnumerable(Of T))
    As IEnumerable(Of (value As T, index As Integer, isFirst As Boolean, isLast As Boolean))

    Using e = collection.GetEnumerator()
        Dim index = -1
        Dim toYield As T

        If e.MoveNext() Then
            index += 1
            toYield = e.Current
        End If

        While e.MoveNext()
            Yield (toYield, index, index = 0, False)
            index += 1
            toYield = e.Current
        End While

        Yield (toYield, index, index = 0, True)
    End Using
End Function

Here is a sample usage:

Sub Main()
    Console.WriteLine("Index   Value   IsFirst   IsLast")
    Console.WriteLine("-----   -----   -------   ------")

    Dim fibonacci = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}

    For Each i In fibonacci.EnumerateEx()
        Console.WriteLine(String.Join("   ", $"{i.index,5}",
                                             $"{i.value,5}",
                                             $"{i.isFirst,-7}",
                                             $"{i.isLast,-6}"))
    Next

    Console.ReadLine()
End Sub

Output

Index   Value   IsFirst   IsLast
-----   -----   -------   ------
    0       0   True      False
    1       1   False     False
    2       1   False     False
    3       2   False     False
    4       3   False     False
    5       5   False     False
    6       8   False     False
    7      13   False     False
    8      21   False     False
    9      34   False     False
   10      55   False     False
   11      89   False     True

Upvotes: 64

Martin
Martin

Reputation: 1

For Each xObj In xColl
    If xObj = xColl(xColl.Count) Then ...
Next

Upvotes: 0

bobby shneider
bobby shneider

Reputation: 31

here is a simple thing i dont know if it is politically correct but it works

for each item in listOfThings
   if(item = listOfThings.last)then 
       'you found the last loop
   end if
next 

Upvotes: 3

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391306

With a foreach, you cannot know this until it is too late (ie. you're out of the loop).

Note, I'm assuming you're using something where you only have an IEnumerable interface. If you have a list, array, etc. then follow the other answers here which uses .Count or similar to find out how many items there are, and thus you can keep track of where you are in the collection.

However, with just IEnumerable/IEnumerator, there is no way to know for sure wether or not there are more, if you use foreach.

If you need to know this, use IEnumerable yourself, which is what foreach does.

The below solution is for C# but should translate easily to VB.NET:

List<Int32> nums = new List<Int32>();
nums.Add(1);
nums.Add(2);
nums.Add(3);

IEnumerator<Int32> enumerator = nums.GetEnumerator();
if (enumerator.MoveNext())
{
    // at least one value available
    while (true)
    {
        // make a copy of it
        Int32 current = enumerator.Current;

        // determine if it was the last value
        // if not, enumerator.Current is now the next value
        if (enumerator.MoveNext())
        {
            Console.Out.WriteLine("not last: " + current);
        }
        else
        {
            Console.Out.WriteLine("last: " + current);
            break;
        }
    }
}
enumerator.Dispose();

This will print:

not last: 1
not last: 2
last: 3

The trick is to take a copy of the current value, then ask the enumerator to attempt to move on to the next one. If that fails, the copy you made was indeed the last value, otherwise there is more.

Upvotes: 7

sh0hei
sh0hei

Reputation: 51

this code sample might help

For Each item in Collection
  If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
    'do something with your last item'
  End If
Next

Upvotes: 1

Steven Liekens
Steven Liekens

Reputation: 14088

I keep coming back to this post, so I decided to sit down and think about this issue and I came up with this:

    For Each obj In myCollection
        Console.WriteLine("current object: {0}", obj.ToString)

        If Object.ReferenceEquals(obj, myCollection.Last()) Then
            Console.WriteLine("current object is the last object")
        End If
    Next

You could even reduce the If...Then...End If statement to a single line if you wanted to. I'm not sure if calling .Last() on every iteration has a large impact on performance, but you can always assign it to a variable outside of the loop if that's what keeps you awake at night.

Upvotes: 1

panky sharma
panky sharma

Reputation: 2159

Well there are few workaround Suppose you are working with CheckBoxList

Then this code sample might help :

Dim count As Integer = 0
Dim totalListCount As Integer = CheckBoxList.Items.Count()
For Each li In CheckBoxList.Items
         count += 1
           // This is the Last Item
      If count = totalListCount THEN
          // DO Somthing.....
      END IF
NEXT     

Upvotes: 0

Bevan
Bevan

Reputation: 44307

Short answer: You can't

Long answer: There's nothing in the semantics of a For Each statement that allows you to identify whether you're running the first, last or any particular iteration.

For Each is built in the IEnumerable and IEnumerable<> interfaces, and the behavior is dependent on the implementation you're calling. It's valid, though confusing, for the collection you're iterating to return elements in a different order every time. Fortunately, List<> doesn't do this.

If you really need to know that a particular iteration is the last, you could identify the last element (in advance) and then take different action when you encounter that element.

Easier would be to detect the first iteration (say, through a boolean) and then do something different.

An example (C#, but the VB will be similar):

StringBuilder result = new StringBuilder();
bool firstTime = true;
foreach(string s in names)
{
    if (!firstTime)
    {
        result.Append(", ");
    }

    result.Append(s);
    firstTime = false;
}

Upvotes: 2

JaredPar
JaredPar

Reputation: 754575

Using a standard "For Each" loop you can't. The intent of a "for Each" loop is to allow you to concentrate on the data in lieu of the underlying collection.

Upvotes: 1

Rex Morgan
Rex Morgan

Reputation: 3029

It would be easier to use a For loop instead of a ForEach, however you could do something like

If item.Equals(itemCollection(itemCollection.Count)) Then
    ...
End If

inside of your ForEach loop... Assuming the object has properly overridden the Equals method.

This is probably much more resource intensive than just using a For loop or keeping track of the current index in a separate variable.

I'm not sure if this is the correct syntax, it's been a long time since I've used VB.

Upvotes: 1

Greg Dean
Greg Dean

Reputation: 30047

If you are tied to IEnumerable. Do you have to be inside the foreach loop? If not you could declare a variable just before the foreach loop. Set it during the loop. Then, use it after the loop (if its not null)

Upvotes: 0

Strelok
Strelok

Reputation: 51441

It probably would be easier to just use a For loop instead of ForEach. But, similarly, you could keep a counter inside your ForEach loop and see if its equal to yourCollection.Count - 1, then you are in the last iteration.

Upvotes: 14

Tamara Wijsman
Tamara Wijsman

Reputation: 12348

Check if the element is the last element of the container.

Why do you want to do that?
You could just place instructions after the loop. (That you execute on the last element)

Upvotes: 1

Related Questions