3

Are there any languages that support foreach over multiple lists at once? Something like this:

 foreach (string a in names, string b in places, string c in colors) {
      // do stuff with a, b, c
 }

Sure, this can be done with a regular for loop:

 for (int n = 0; n<max, n++) {
      a = names[n]; b = places[n]; c = colors[n];
 }

but this loses the abstraction of foreach.

My meager google-fu only reveals people asking how to do multi-foreach in various languages that don't support it. I'm guessing that some language uses it, I just can't figure out which one.

Joe
  • 388
  • 1
  • 4
  • 13
  • What exactly is the purpose of this question? – phant0m May 18 '13 at 18:15
  • A project I'm on needs a syntax for something like multi-foreach. If there's a syntax already out there, I'd like to use that instead of inventing a new one. – Joe May 18 '13 at 18:16

2 Answers2

8

Sure, Python can do that:

for a, b, c in zip(names, places, colors):
    pass

zip builds a list of tuples from the iterables passed to it. Python's for loop supports "unpacking", that is to say the tuples are split into their components. It's a weak form of pattern matching.

phant0m
  • 2,644
  • 5
    Note that this particular feature (iterating over several things "in parallel") arises naturally from a simpler and more general-purpose features (tuple unpacking). There's no special syntax for this in particular (as OP seems to be expecting), and that's a good thing. –  May 18 '13 at 18:26
4
  • In C#, you can transform multiple collections into one collection of tuples, then use a foreach. There is no specific language syntax, but .NET Framework still allows you to do it.

  • In SQL, you can use join. I'm not sure if the "languages" you're looking for extend to SQL.

Note that in all cases, there is an ambiguity with your simple. What if colors.length < places.length? If places.length > names.length, would the remaining places be simply discarded?


Example for C#:

public static void Main(string[] args)
{
    var names = new[] { "John", "Bill", "Joel" };
    var places = new[] { 1, 2, 3 };
    var colors = new[] { Color.AliceBlue, Color.BlueViolet, Color.DarkMagenta };

    var zip = names.Zip(places, (n, p) => new { n, p }).Zip(colors, (t, c) => new { Name = t.n, Place = t.p, Color = c });

    foreach (var z in zip)
    {
        Console.WriteLine(string.Join(", ", new[] { (object)z.Name, z.Place, z.Color }));
    }

    Console.WriteLine("Press any key to continue...");
    Console.ReadKey(true);
}

Or, using an extension method:

public static IEnumerable<Tuple<T1, T2, T3>> ZipSeveral<T1, T2, T3>(this IEnumerable<T1> first, IEnumerable<T2> second, IEnumerable<T3> third)
{
    return first.Zip(second, (a, b) => new { a, b }).Zip(third, (t, c) => Tuple.Create(t.a, t.b, c));
}

you can reduce the code to a simple foreach:

foreach (var z in names.ZipSeveral(places, colors))
{
    Console.WriteLine(string.Join(", ", new[] { (object)z.Item1, z.Item2, z.Item3 }));
}