How do you find the last loop in a For Each (VB.NET)?
Solution 1
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
Solution 2
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.
Solution 3
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.
Solution 4
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;
}
Solution 5
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
Comments
-
RichC over 3 years
How can I determine if I'm in the final loop of a For Each statement in VB.NET?