将SelectMany应用于三个或更多序列

时间:2012-12-01 12:52:16

标签: c# linq

使用SelectMany仅使用扩展方法来获取三个或更多序列的交叉连接的最佳方法是什么?有没有其他方法可以进行交叉连接?

测试数据

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

预期结果

 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 

我尝试了什么

这是有效的代码,但我想知道是否有更容易阅读和理解的替代方案:

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 });

等效的查询表达式很好但不是我想要的:

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

3 个答案:

答案 0 :(得分:3)

您可以通过以下方式简化(即使不是很多)SelectMany查询:

var res = a.SelectMany(X => b.SelectMany(Y => c.Select(Z => new { X, Y, Z })));

答案 1 :(得分: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版本。

答案 2 :(得分: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
*/