13

Is it possible to have a foreach statement that will traverse through a Collections object in reverse order?

If not a foreach statement, is there another way?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sam F
  • 621
  • 1
  • 8
  • 16

5 Answers5

29

You can use a normal for loop backwards, like this:

for (int i = collection.Count - 1; i >= 0 ; i--) {
    var current = collection[i];
    //Do things
}

You can also use LINQ:

foreach(var current in collection.Reverse()) {
    //Do things
}

However, the normal for loop will probably be a little bit faster.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 2
    The `for` loop may be faster, but the LINQ is certainly clearer. I would therefore prefer the LINQ version. – jball Jun 16 '10 at 18:40
  • I don't know why I didn't realize you could traverse through a collection with brackets... so i guess its just for (int i = collections.Count; i >= 0; i--)... Ugh I'm a moron. Thanks. – Sam F Jun 16 '10 at 18:41
  • 6
    @Sam F. No you are not! :-) –  Jun 16 '10 at 18:44
  • 2
    @jball: Iterating backwards in a for loop is somehow "unclear"? Really? – Ed S. Jun 16 '10 at 18:46
  • @Sam F: Not all collections have random access (ie. brackets), so you're not wrong in any case. :) – Mauricio Jun 16 '10 at 18:47
  • Note that your first example only works if the Collection has an indexer; not all collections do. `Reverse` is the best option. – Randolpho Jun 16 '10 at 18:47
  • 2
    That username is genius. I love the fact that everyone will address you as "Hey, moron" – Pierreten Jun 16 '10 at 18:48
  • 1
    @Kyte, @Randolpho: AFAIK, the only collections that don't have indexers are the intrinsically unordered ones (eg, Dictionary.ValuesCollection), which you shoudn't be reversing in the first place. – SLaks Jun 16 '10 at 18:49
  • 4
    @Ed: In this age of iterators and LINQ, for loops seem downright antiquated. – Pierreten Jun 16 '10 at 18:49
  • 1
    @SLaks: you left out `LinkedList`, which is ordered and reversible, but is not indexible. – Randolpho Jun 16 '10 at 18:52
  • @SLaks: I may be defiling the proper use of IEnumerable, but I tend to use them even on objects that have easy linear transversal but no random access. Say, data incoming from a network stream or a sequential-access file. (That last one was for homework, mind you.) – Mauricio Jun 16 '10 at 18:53
  • @SLaks: and as I go through them, there are `SortedDictionary`, `SortedSet`, and `Stack`, all of which are ordered and reversible, but not indexible. And that's just in the `System.Collections.Generic` namespace... I could search for more... – Randolpho Jun 16 '10 at 18:55
  • 1
    @Ed Swangren, you really think the loop is as clear as the LINQ? The LINQ expresses in 1 line using an english verb as the method name what it will do. The `for` loop requires 2 lines, awareness of the bounds of the list, and succeeds in saying "reverse" via `--`. The LINQ is clearly more expressive. – jball Jun 16 '10 at 18:56
  • 1
    I didn't look, but `Reverse()` is probably smart enough to use indexes if they are available. For instance, the LINQ `Count()` method is smart enough to access the `Length` or `Count` property if it exists, and to count the elements in the enumeration one-by-one only if no such property exists. –  Jun 16 '10 at 19:02
  • 2
    @SLaks: Reading back I realize I sound a little dickish with that; such was not my intent. I'm just trying to point out that it's best to use `Reverse` for the general case, since not every collection has an indexer. – Randolpho Jun 16 '10 at 19:08
7

You could just call Reverse() on the collection.

foreach(var item in collection.Reverse()) { ... }
48klocs
  • 6,073
  • 3
  • 27
  • 34
  • I don't believe there is a Reverse() on Collections that was my first thought. – Sam F Jun 16 '10 at 18:43
  • 2
    @Sam F: It's an [extension method](http://msdn.microsoft.com/en-us/library/bb383977.aspx) on any `IEnumerable` (which includes `Collection`), available from .NET 3.5 onwards. See [here](http://msdn.microsoft.com/en-us/library/system.linq.enumerable_methods.aspx) for a list of all of them. If you're using .NET 3.5 or later you just need to add `using System.Linq;` to the top of your file. – Dan Tao Jun 16 '10 at 18:49
  • 2
    I suppose I should add a note - confusingly enough, Reverse() has a different meaning for IList than it does for standard IEnumerable. IList.Reverse() returns void so in order for my snippet to work, you'd have to say collection.AsEnumerable().Reverse(). Confusing business, that. – 48klocs Jun 16 '10 at 19:10
  • 48klocs, can you put the comment answer in the real answer....? (the AsEnumerable()). I almost gave up on the posted-answer before I read the magic comment. – granadaCoder May 26 '17 at 11:20
5

If you're using 3.5, looks like there is a Reverse() method in LINQ That won't iterate through in reverse order, but would reverse the entire list, then you could do your foreach.

Or you could use a simple for statement:

for(int i = list.Count -1; i >= 0; --i)
{
   x = list[i];
}
taylonr
  • 10,732
  • 5
  • 37
  • 66
  • 5
    You're asking for an `IndexOutOfBounds` error. Start at `i = count - 1` – Greg Jun 16 '10 at 18:39
  • 2
    "That won't iterate through in reverse order, but would reverse the entire list." This is untrue. Iterating in reverse order is *precisely* what the `Enumerable.Reverse` extension method does. You may be thinking of `List.Reverse`, which [confusingly has the same name](http://stackoverflow.com/questions/2828917/most-awkward-misleading-method-in-the-net-api/2829051#2829051). – Dan Tao Jun 16 '10 at 18:44
  • @Greg, good catch, updated. @Dan Tao, I was thinking of List.Reverse. – taylonr Jun 16 '10 at 18:45
4

Alternatively, if the collection's an IEnumerable and therefore without random access, use System.Linq's IEnumerable.Reverse() method and apply forearch as usual.

using System.Linq;

foreach (var c in collection.Reverse()) {
}
Mauricio
  • 1,683
  • 12
  • 18
  • Reverse is the only *viable* option for reverse iterating over a collection. Too many collections are not indexible. – Randolpho Jun 16 '10 at 18:59
2
        List<string> items = new List<string> 
        { 
            "item 1", 
            "item 2",
            "item 3", 
            "item 4", 
        };
        lines.AsEnumerable().Reverse()
                            .Do(a => Console.WriteLine(a), ex => Console.WriteLine(ex.Message), () => Console.WriteLine("Completed"))
                            .Run();           

Reactive Extension for .NET 3.5/4.0

Thurein
  • 2,536
  • 7
  • 34
  • 49