40

I don't believe this is possible by conventional methods, but something like this verbose code:

For Each s As String In myStringList Step -1
    //' Do stuff here
Next

I will probably have to invert the myString object before a conventional For..Each Loop, correct?

Anders
  • 12,088
  • 34
  • 98
  • 146

14 Answers14

48

I think the documentation referenced in Mike's answer below is extremely misleading. The order of For Each is defined by the collection it's called (i.e. its implementation of IEnumerable/IEnumerable<T>), but that's not the same as saying it shouldn't be used when the order is important. Many collections (such as arrays, List<T> etc) always go in the "natural" order.

Part of the documentation does allude to this:

Traversal Order. When you execute a For Each...Next loop, traversal of the collection is under the control of the enumerator object returned by the GetEnumerator method. The order of traversal is not determined by Visual Basic, but rather by the MoveNext method of the enumerator object. This means that you might not be able to predict which element of the collection is the first to be returned in element, or which is the next to be returned after a given element.

That's not at all the same as saying it can't be relied upon - it can be relied upon if you know that the collection you're iterating over will produce the results in the desired order. It's not like it's going to pick elements at random. The behaviour in terms of IEnumerable/IEnumerable<T> is clearly defined on that page.

The most important exceptions to predictable orderings are dictionaries and sets, which are naturally unordered.

To reverse an IEnumerable<T>, use Enumerable.Reverse - but if you need to iterate in reverse over a collection which is indexed by position (such as an array or List<T>) then it would be more efficient to use a For loop starting at the end and working backwards.

Ian
  • 1,221
  • 1
  • 18
  • 30
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
46

You'd have to do something like:

For i as integer = myStringList.Count-1 to 0 step -1
    dim s as string = myStringList.Item(i)
    ' Do your stuff with s
Next i

But as far as I know, you can't do a "For...Each" backwards, though that would be nice in a few instances.

SqlRyan
  • 33,116
  • 33
  • 114
  • 199
12

Sadly, the MSDN docs on For Each state that the For Each construct is there explicitly for cases where the order of the iteration is unimportant (unordered sets and the like). So there is unfortunately no concept of reversing a For Each, as the order is not defined anyway.

Good luck!

Mike
  • 4,542
  • 1
  • 26
  • 29
  • 1
    I was expecting this but I wasn't 100% sure, so thanks for a definite no w/ reference as a bonus :) – Anders Jun 04 '09 at 18:02
  • this is correct - I always assume that "For Each" loops go in order, and while they usually do, it can't be relied on. If you want a particular order, a "For...Next" on on of the indices in the array is your only choice. – SqlRyan Jun 04 '09 at 18:06
  • 7
    It *can* be relied upon if you know the collection you're using iterates in the right order. For example, arrays will *always* iterate in the natural order. See my answer for more details. – Jon Skeet Jun 04 '09 at 18:15
  • 4
    (Downvoted as the claim that "the order is not defined anyway" is wrong - it's defined by the collection itself.) – Jon Skeet Jun 04 '09 at 18:16
  • 3
    The MSDN page Mike has linked confirms what Jon Skeet says [it wouldn't dare do otherwise] - the section "The order of traversal is not determined by Visual Basic, but rather by the MoveNext method of the enumerator object.". So the order *is* defined, but by the collection being iterated, not by the For Each statement itself. – MarkJ Jun 04 '09 at 18:20
  • i should point out though that if your IEnumerable implementation does not define an order, then For Each won't impose one. – Mike Jun 04 '09 at 18:22
12

Call the System.Linq.Enumerable.Reverse method to get an IEnuemrable(Of TSource) in the reverse order of your enumerable source.

Amy B
  • 108,202
  • 21
  • 135
  • 185
  • Yeah, that is what I will have to end up doing. Thanks! – Anders Jun 04 '09 at 18:03
  • I had no idea this worked, though oddly enough, the MSDN example reversed the array and then proceeds to walk it using a "For Each" loop, which is explicitly not dependable when a particular order is required. Oops... – SqlRyan Jun 04 '09 at 18:09
  • 3
    all foreach'able types implement GetEnumerator(). Some Enumerators have reliable ordering and some don't. Since we are talking about reversed order, odds are that the Enumerator that we're dealing with has reliable ordering to reverse. Of course, not all Enumerators meet this criteria. Enumerators for Arrays, most certainly, are reliably ordered and reversable. – Amy B Jun 04 '09 at 18:12
  • 2
    Answers should not be link-only. – AStopher Nov 09 '14 at 15:42
  • 1
    @cybermonkey it's not link only. It's link + sentence. Sometimes more words does not improve an answer. – Amy B Nov 10 '14 at 12:34
  • @DavidB Your answer *requires* visiting the link in order to know what/how to call the `reverse method`, therefore it is link-only (the two users who upvoted my comment also seem to agree). Please edit your question to expand on your answer, at least. – AStopher Nov 10 '14 at 12:37
  • @cybermonkey I added enough information that the docs could be internet searched without visiting the link to the docs. I hope this meets your pedantic guideline. Please concern yourself with the accepted answer with is in fact a falsehood. Then concern yourself with the second answer which is primarily a reaction to the first answer and secondarily an answer to the question. Then look at the third answer, which repeats the falsehood in the first answer. – Amy B Nov 11 '14 at 01:25
5

Testig in Framework 4, the code

For Each s As String In myStringList.Reverse
    //' Do stuff here
Next

it wasn't working, the right way to do it is:

myStringList.Reverse() ' it is a method not a function
For Each s As String In myStringList
//' Do stuff here
Next

Look at: MSDN: Reserve

Dave
  • 7,028
  • 11
  • 35
  • 58
3
For Each s As String In myStringList.Reverse
    //' Do stuff here
Next
Tonko Boekhoud
  • 819
  • 6
  • 9
1

This works:

Dim myArray() As String = {"1", "2", "3", "4", "5"}

For Each n As String In myArray.Reverse
    Debug.Print(n)
Next

Output: 5 4 3 2 1

'

This also works:

Dim myArray() As String = {"1", "2", "3", "4", "5"}

For i As Integer = myArray.Length - 1 To 0 Step -1
    Debug.Print(myArray(i))
Next

Output: 5 4 3 2 1

MontanaMan
  • 177
  • 1
  • 1
  • 11
1

The reason it can't be done is that, as a basic design feature, For Each can iterate over an enumerable without a last element, or with an as yet unknown last element.

reinierpost
  • 8,425
  • 1
  • 38
  • 70
0

What you have to do is create an array with your for each you had before, then use array.reverse and run the for each on the array. Done

Cheers

  • 2
    Hi xxxmandoxxx. Before adding an answer you should check if the same answer already exists. Your answer as it is doesn't add value to the post. – stenci Feb 05 '14 at 23:44
0

Depending on what happens inside your loop you can do an .InsertAt(object,0) instead of an .Add and produce the same result as a reverse enumeration.

0

You can add a extended function to the class you are trying to reverse

<Serializable()> Public Class SomeCollection
    Inherits CollectionBase
    Public Sub New()
    End Sub

    Public Sub Add(ByVal Value As Something)
        Me.List.Add(Value)
    End Sub

    Public Sub Remove(ByVal Value As Something)
        Me.List.Remove(Value)
    End Sub

    Public Function Contains(ByVal Value As Something) As Boolean
        Return Me.List.Contains(Value)
    End Function

    Public Function Item(ByVal Index As Integer) As Something
        Return DirectCast(Me.List.Item(Index), Something)
    End Function

    Public Function Reverse() As SomeCollection
        Dim revList As SomeCollection = New SomeCollection()
        For index As Integer = (Me.List.Count - 1) To 0 Step -1
             revList.List.Add(Me.List.Item(index))
        Next
        Return revList
    End Function
End Class

Then you would call it like this

For Each s As Something In SomeCollection.Reverse

Next
0

first you should create a list(of string) in for each statement, and after that make another normal for .. step -1 ..next statement. see an example:

Dim ff As New List(Of Integer)
For Each rRow As DataRow In DeletedRows
Dim item As Integer
item = rRow.Table.Rows.IndexOf(rRow)
ff.Add(item)
Next
For i As Integer = ff.Count - 1 To 0 Step -1
dim item as integer=ff(i)
next i

i hope that be helpful

Robert
  • 5,278
  • 43
  • 65
  • 115
0

The accepted answer explains why, but to add another example, for a collection with key (SortedList, SortedDictionary, Dictionary, etc.) you can do

For Each Id as Tkey in MyCollection.Keys.Reverse
   // The item in question is now available as MyCollection(Id)
Next
Adam
  • 6,539
  • 3
  • 39
  • 65
0

' create a list of whatever and step through For Loop Collecting

dim revList as New List (of ToolStripItem)  
For each objItem as ToolStripItem in Menu.DropDownItems
    revList.Add(objItem)
next
revList.reverse ' reverse the order of that list

' step through the reverse ordered list

for each objItem as ToolStripItem in revList   
    ' do whatever (like removing your menu from an ArrayMenu)
next

' replace ToolStripItem and Menu.DropDownItems with whatever you need

Georg
  • 404
  • 5
  • 7