2

What is the best way to apply SelectMany to get a cross join of three or more sequences using only extension methods? Is there any other way to get a cross join?

Test Data

var a = Enumerable.Range(11, 2);
var b = Enumerable.Range(21, 2);
var c = Enumerable.Range(31, 2);

Expected Result

 X  Y  Z 

11 21 31 
11 21 32 
11 22 31 
11 22 32 
12 21 31 
12 21 32 
12 22 31 
12 22 32 

What I tried

Here's the code that works but I wonder if there's any alternative that'd be easier to read and understand:

var d = a
    .SelectMany(rb => b
    .SelectMany(rc => c, (y, z) => new { Y = y, Z = z}), 
                        (x, yz) => new { X = x, Y = yz.Y, Z = yz.Z });

The equivalent query expression is good but not what I'm looking for:

var e = from x in a
        from y in b
        from z in c
        select new { X = x, Y = y, Z = z };
4

3 回答 3

3

您可以通过这种方式简化(即使不多)您的SelectMany查询:

var res = a.SelectMany(X => b.SelectMany(Y => c.Select(Z => new { X, Y, Z })));
于 2012-12-01T13:41:24.653 回答
1

您可以使用Join始终匹配的项目密钥。

var e = a.Join(b, x => true, y => true, (x, y) => new { A = x, B = y })
         .Join(c, x => true, y => true, (x, y) => new { x.A, x.B, C = y });

诚然,它的效率可能低于您的SelectMany版本。

于 2012-12-01T12:59:57.523 回答
1

我仍然不确定是否可以制作自定义方法。但无论如何,这里是:

public static IEnumerable<IEnumerable<T>> CrossJoin<T>(params IEnumerable<T>[] sequences)
{
    IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
    foreach (var sequence in sequences)
    {
        result = result.SelectMany(i => sequence.Select(s => i.Concat(new[] { s })));
    }
    return result;
}

如果添加这样的方法,那么重要的代码将变得非常可读:

var d = CrossJoin(
    Enumerable.Range(11, 2),
    Enumerable.Range(21, 2),
    Enumerable.Range(31, 2)
);

结果:

Console.WriteLine("X  Y  Z");
foreach( var item in d ) {
    Console.WriteLine(String.Join( ",", item ));
}
/*
X  Y  Z
11,21,31
11,21,32
11,22,31
11,22,32
12,21,31
12,21,32
12,22,31
12,22,32
*/
于 2012-12-01T14:38:38.877 回答