13

Is there a way to use a foreach loop to iterate through a collection backwards or in a completely random order?

swilliams
  • 48,060
  • 27
  • 100
  • 130
Jason Z
  • 13,122
  • 15
  • 50
  • 62

11 Answers11

16

Using System.Linq you could do...

// List<...> list;
foreach (var i in list.Reverse())
{
}

For a random order you'd have to sort it randomly using list.OrderBy (another Linq extension) and then iterate that ordered list.

var rnd = new Random();
var randomlyOrdered = list.OrderBy(i => rnd.Next());
foreach (var i in randomlyOrdered)
{
}
cfeduke
  • 23,100
  • 10
  • 61
  • 65
  • 3
    The i=> rnd.Next() is a little dangerous - it assumes that the delegate is only invoked once per item (i,e. an implementation detail). If not, the Sort could explode. As it happens, I think you get away with it - but an explicit Select first (to a temp object) might be adviseable... – Marc Gravell Oct 31 '08 at 20:50
  • 3
    Also, doing OrderBy(g => Guid.NewGuid()) is miles faster (and more random) – Kirschstein Feb 07 '10 at 19:07
  • Good point, I use this same technique when I randomly order results in a query in SQL Server but it didn't even cross my mind to use it here. – cfeduke Feb 08 '10 at 15:09
15

As other answers mention, the Reverse() extension method will let you enumerate a sequence in reverse order.

Here's a random enumeration extension method:

public static IEnumerable<T> OrderRandomly<T>(this IEnumerable<T> sequence)
{
    Random random = new Random();
    List<T> copy = sequence.ToList();

    while (copy.Count > 0)
    {
        int index = random.Next(copy.Count);
        yield return copy[index];
        copy.RemoveAt(index);
    }
}

Your usage would be:

foreach (int n in Enumerable.Range(1, 10).OrderRandomly())
    Console.WriteLine(n);
Jacob Carpenter
  • 4,134
  • 1
  • 29
  • 30
  • cfeduke's OrderBy solution is much nicer. – Domenic Oct 31 '08 at 19:57
  • Though I must admit I like the extension method better, because really, extension methods are my hammer and all problems in my code are nails. Seriously, its gotten to that point for me. If only extension methods could be declared privately I'd be in heaven. – cfeduke Oct 31 '08 at 21:22
  • 2
    Using `List` and `RemoveAt` like this has O(n^2) complexity. For a similar, but O(n) algorithm, see this question: http://stackoverflow.com/questions/4412405/is-there-a-performance-difference-between-these-two-algorithms-for-shuffling-an-i – Matthew Dec 10 '10 at 19:46
  • @cfeduke extension methods can be declared as private, isnt it? – nawfal May 30 '13 at 13:44
1

I don't think there is a way to do so directly, but it's pretty much as good to use an extension method that returns a new collection via the yield return keyword. These could come from a pre-existing library; the others have pointed out that LINQ has a Reverse method, and things like OrderBy would also work.

Example: if you use the LINQ extension method Reverse() on IEnumerable<T>, which uses yield return to give the collection in reverse order, then doing a foreach(var myThing in myCollection.Reverse()) will enumerate through the collection in reverse order.

Important: yield return is key. It means "when I enumerate this collection, then go fetch things." As opposed to the alternative of just constructing a new, reversed collection, which is highly inefficient and possibly has side effects.

Domenic
  • 110,262
  • 41
  • 219
  • 271
1

As of C# 2.0 you have the ability to use the yield keyword to implement custom iterators really easy. You can read more about the yield keyword over at MSDN http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

You can think of a yield as the ability to return a value from inside a loop, but you should refer to the link above for a full explanation of what they are and what they can do.

I wrote a short example on how to implement a couple of custom iterators. I've implemented them as extension methods (http://msdn.microsoft.com/en-us/library/bb383977.aspx) to make the code a bit more stream lined and I also use array initializers (http://msdn.microsoft.com/en-us/library/aa664573.aspx) to set the initial values for the list of integers.

Neither extension methods nor array initializers are necessary for implementing custom iterators but they are nice features of c# 3.0 which helps write cleaner code

Here are my examples. It shows how to iterate over a list of integers by only returning Odd numbers, Even numbers, the numbers in reversed or in a completly random fashion.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> ints = 
                new List<int> { 1,2,3,4,5,6,7,8,9,10};

            Console.WriteLine("Iterating over Odd numbers only.");
            foreach (int i in ints.Odd())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over Even numbers only.");
            foreach (int i in ints.Even())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in reversed order.");
            foreach (int i in ints.Reversed())
            {
                Console.WriteLine(i);
            }

            Console.WriteLine("Iterating over the list in random order.");
            foreach (int i in ints.Random())
            {
                Console.WriteLine(i);
            }

            Console.ReadLine();
        }
    }

    public static class ListExtensions
    {
        /// <summary>
        /// Iterates over the list only returns even numbers
        /// </summary>
        /// <param name="list"></param>
        public static IEnumerable<int> Even(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 == 0)
                 {
                    yield return i;
                }
           }
        }

        /// <summary>
        /// Iterates over the list only returns odd numbers
        /// </summary>
        public static IEnumerable<int> Odd(this List<int> list)
        {
            foreach (var i in list)
            {
                if (i % 2 != 0)
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Iterates over the list in reversed order
        /// </summary>
        public static IEnumerable<int> Reversed(this List<int> list)
        {
            for (int i = list.Count; i >= 0; i--)
            {
                yield return i;
            }
        }

        /// <summary>
        /// Iterates over the list in random order
        /// </summary>
        public static IEnumerable<int> Random(this List<int> list)
        {
            // Initialize a random number generator with a seed.
            System.Random rnd =
                new Random((int)DateTime.Now.Ticks);

            // Create a list to keep track of which indexes we've
            // already returned
            List<int> visited =
                new List<int>();

            // loop until we've returned the value of all indexes
            // in the list
            while (visited.Count < list.Count)
            {
                int index =
                    rnd.Next(0, list.Count);

                // Make sure we've not returned it already
                if (!visited.Contains(index))
                {
                    visited.Add(index);
                    yield return list[index];
                }
            }
        }
    }
}
RenniePet
  • 11,420
  • 7
  • 80
  • 106
TheCodeJunkie
  • 9,378
  • 7
  • 43
  • 54
  • I have a very bad feeling about your Random() enumerator. Isn't it incredibly inefficient? Assume the List to be enumerated has 1 million entries. To start with, the way you test for random indexes that you've already returned involves adding entries to the "visited" List so it slowly but surely grows, getting reallocated many times. Worse, testing for an already-returned index is a linear search, so the simple question "is this index already returned" can involve up to 1 million comparisons, searching through the "visited" List. – RenniePet Apr 06 '15 at 21:52
  • But that's not the worst part. As you get towards the end of the process, you are generating more and more random numbers, trying to find the few indexes that haven't been returned yet. Specifically, to "find" the 1 millionth index, the last one that you haven't already returned, I'm thinking that you might have to generate several million random numbers, and testing each of these numbers to determine that it's already been used involves 999,999 comparisons. – RenniePet Apr 06 '15 at 21:58
  • visited should be a Set. This way `Contains()` would be faster. – Mariusz Jamro Feb 02 '17 at 07:58
  • this is by no means production grade code - it's a simple sample and I would hope nobody put it into production :D – TheCodeJunkie Feb 03 '17 at 11:05
1

I actually liked cfeduke approach with LINQ and it bugs me that it slipped my mind. To add to my previous example. If you want to do the Odd and Even iterations with the help of LINQ you can use

// Even
foreach (var i in ints.FindAll(number => number % 2 == 0))
{
      Console.WriteLine(i);
}

// Odd
foreach (var i in ints.FindAll(number => number % 2 != 0))
{
      Console.WriteLine(i);
}
TheCodeJunkie
  • 9,378
  • 7
  • 43
  • 54
1

Using an IList<T> from the C5 Generic Collection Library, Reverse iteration is a feature, rather than extension:

foreach (var i in list.Reverse())
{
}

As well, you can use the Shuffle() method to get a random ordering:

var listClone = (IList<T>) list.Clone();
listClone.Shuffle();
foreach (var i in listClone)
{
}
Marcus Griep
  • 8,156
  • 1
  • 23
  • 24
0

you can do it backwards:

for (int i=col.count-1; i>0; i--){ 
      DoSomething ( col.item[i]) ;
}

Not certain about the exact syntax, but that's the paradigm.

As for completely random order, you can access a collection element via it's index. To ensure you hit every item, you would need to keep track of which elements you had already processed (probably by copying the collection and then removing the element after access).

EDIT: More details for random access The code for the random access could look something like this:

 collection c = originalCollection;
 while (c.count > 0) {
     int i = randomNumber(seed) mod c.count
     element d = c[i];
     c.remove(d);
     DoSomething(d);
}
Stephen Wrighton
  • 36,783
  • 6
  • 67
  • 86
0

You could sort the List by supplying your own Comparator and iterate over that one.

Tigraine
  • 23,358
  • 11
  • 65
  • 110
0

Do you want to rand a collection and interect with it?

If yes, try this:

Random rand = new Random(Environment.TickCount);

test.Sort((string v1, string v2) => {
                if (v1.Equals(v2))
                {
                    return 0;
                }

                int x = rand.Next();
                int y = rand.Next();

                if (x == y)
                {
                    return 0;
                }
                else if (x > y)
                {
                    return 1;
                }

                return -1; 
            });

for (string item in test)
{
  Console.WriteLn(item);
}
// Note that test is List<string>;
Zote
  • 5,343
  • 5
  • 41
  • 43
  • Uh, ew? How about test.OrderBy(t => rand.Next())? Granted, it's not a stable sort like yours is, but presumably it'd be easy to modify in that direction. Also, Environment.TickCount is unnecessary when initializing a Random. – Domenic Oct 31 '08 at 19:57
  • Domenic, I liked so much (t => rand.Next()) but I already writed this too big example :) and in my tests, I need to use Environment.TickCount because without it, I get same results too many times... – Zote Nov 03 '08 at 10:54
0

From my reading of the C# Language Specification, the foreach iteration statement depends on the struct/class/interface which is being iterated having the GetEnumerator() function defined upon it. The object returned by GetEnumerator() must have MoveNext() defined as a member function. MoveNext() is defined as accessing the "first" object in the list on its first call, then the "next" on subsequent calls, returning true until no further elements exist in the list, upon which it returns false.

The feature Domenic refers to, yield return, first appears in the 2.0 version of the specification, and does appear to be useful for this purpose. For version 1.1, your only option would be to derive a new struct/class/interface from your base and override GetEnumerator() to return a new IEnumerator, where the MoveNext() function would follow different rules in select the first collection element and any subsequent collection element.

My own recommendation would be to use an indexed collection, then use a for loop with an appropriate index calculation (here one could use a random number generator if necessary, with an integer array or some other technique for verifying that the same index value is not used twice) if you have to do this in actual practice.

0

Use random ordering
http://www.dailycoding.com/..using_linq.aspx

List<Employee> list = new List<Employee>();

list.Add(new Employee { Id = 1, Name = "Davolio Nancy" });
list.Add(new Employee { Id = 2, Name = "Fuller Andrew" });
list.Add(new Employee { Id = 3, Name = "Leverling Janet" });
list.Add(new Employee { Id = 4, Name = "Peacock Margaret" });
list.Add(new Employee { Id = 5, Name = "Buchanan Steven" });
list.Add(new Employee { Id = 6, Name = "Suyama Michael" });
list.Add(new Employee { Id = 7, Name = "King Robert" });
list.Add(new Employee { Id = 8, Name = "Callahan Laura" });
list.Add(new Employee { Id = 9, Name = "Dodsworth Anne" });

list = list.OrderBy(emp => Guid.NewGuid()).ToList();
Ramesh Soni
  • 15,867
  • 28
  • 93
  • 113