In computational programs there is often such code as
foreach (var x in xx)
foreach (var y in yy)
foreach (var z in zz)
Calculate(x, y, z);
Can it be simplified to just one loop, like
foreach (var (x, y, z) in F(xx, yy, zz)) Calculate(x, y, z);
For any number of variables, probably having different types?
CodePudding user response:
If you want to deal with arbitrary types, you will have to define a generic method for all each amount of parameters you have - this is the same problem as with the predefined delagates Action
, Action<T>
, Action<T1,T2>
, ...
Hence your code could look like this
public static class Helper
{
public static IEnumerable<(T1,T2)> CrossJoin<T1,T2>(IEnumerable<T1> input1, IEnumerable<T2> input2)
{
return input1.SelectMany(x => input2, (x,y) => (x,y));
}
public static IEnumerable<(T1,T2,T3)> CrossJoin<T1,T2,T3>(IEnumerable<T1> input1, IEnumerable<T2> input2, IEnumerable<T3> input3)
{
return input1.SelectMany(x => input2, (x,y) => (x,y))
.SelectMany(x => input3, (x,y) => (x.Item1, x.Item2, y));
}
// and so on
}
Because we have to define a method for every amount of parameters, there is no need for a sophisticated recursive method which catches all cases. Because only value tuples are used and no arrays are created, the garbage collection won't have so much work as in your solution.
Online demo: https://dotnetfiddle.net/cQwXa5
CodePudding user response:
I have come up with a simple cross join method, but all variables should have the same type T:
public static IEnumerable<T[]> CrossJoin<T>(params IEnumerable<T>[] seqs)
{
if (seqs.Length == 1) foreach (var x in seqs[0]) yield return new[] { x };
else
{
var subres = CrossJoin(seqs.Skip(1).ToArray());
var res = from x in seqs[0] from xx in subres select xx.Prepend(x);
foreach (var x in res) yield return x.ToArray();
}
}
usage:
foreach (var (x, y, z) in Helper.CrossJoin(xx, yy, zz)) Calculate(x, y, z);
It can be changed to work with object
but it will require type casting to use the variables..