使用Linq如何返回两个列表中所有元素的组合对?

22

给定列表 l1 = {1, 2}l2 = {4, 5, 6 },我想要得到一个新的列表,其中包含以下元素:

rez = { {1, 4}, {1, 5}, {1, 6}, {2, 4}, {2, 5}, {2, 6} }

有什么建议吗?


请注意,这是https://dev59.com/JXA75IYBdhLWcg3w0cjx的副本。 - Eric Lippert
7个回答

43

是的,这是可能的。Eric Lippert在这个主题上撰写了一篇非常好的文章:

使用LINQ计算笛卡尔积

如果你只有两个列表,那么你可以直接像下面这样使用多个from

from a in s1 
from b in s2 
select new [] { a, b};

甚至更好:

s1.SelectMany(a => s2.Select(b => new [] { a, b }));

但是Eric Lippert在之前的文章中给出的解决方案可以让你计算多个序列的笛卡尔积。具体实现可以使用如下的扩展方法:

public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
    return sequences.Aggregate(
        emptyProduct,
        (accumulator, sequence) =>
        from accseq in accumulator
        from item in sequence
        select accseq.Concat(new[] { item }));
}

您可以这样写:

var l1 = new[] {1, 2};
var l2 = new[] {4, 5, 6};
var l3 = new[] {7, 3};

foreach (var result in new []{l1,l2,l3}.CartesianProduct())
{
    Console.WriteLine("{"+string.Join(",",result)+"}");
}

并获得:

{1,4,7}
{1,4,3}
{1,5,7}
{1,5,3}
{1,6,7}
{1,6,3}
{2,4,7}
{2,4,3}
{2,5,7}
{2,5,3}
{2,6,7}
{2,6,3}

4
Eric Lippert已经为您完成了这项工作!
请查看以下链接:http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx 你可能只想使用linq流畅语法的SelectMany
var s1 = new[] {a, b}; 
var s2 = new[] {x, y, z}; 


var product = 
from first in s1 
from second in s2 
select new[] { first, second };

product.SelectMany(o => o);

或者Eric的博客版本

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) 
{ 
  // base case: 
  IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() }; 
  foreach(var sequence in sequences) 
  { 
    var s = sequence; // don't close over the loop variable 
    // recursive case: use SelectMany to build the new product out of the     old one 
    result = 
      from seq in result 
      from item in s 
      select seq.Concat(new[] {item}); 
  } 
  return result; 
}

product.CartesianProduct();


3
var result = from a in l1
             from b in l2
             select new[] { a, b }

2

Here you go;

var rez =  from first in l1 
           from second in l2 
           select new[] { first, second };

2

Eric Lippert的文章非常棒 - 在其他答案中可以看到链接。

更好的是,在查看此页面上的答案之前,这是我第一次尝试 :)

简而言之:

var rez = 
    from e1 in l1
    from e2 in l2 
    select new {e1, e2};

1

像这样的代码片段将会完成你所需要的功能。

var l1 = new List<int>{1,2};
var l2 = new List<int>{4,5,6};

var p = from n in l1
        from m in l2
        select new { Fst = n, Scd = m };

通过这个答案,你的元组 {x,y} 就成为了一个匿名类型。


-3

你想要什么

l1.Join(l2, a => 1, b => 1, (a, b) => new [] { a, b });

不需要使用Join操作,甚至可能会增加一些开销(最后一部分不要引用我的话)。其他答案展示了更直观的方法。 - Anthony Pegram
4
没问题,这样做的确有效,但这是完全没有必要滥用连接序列运算符。连接序列运算符的目的是在笛卡尔积上高效执行筛选操作,但您在这里使用它来明确地不执行任何筛选。这意味着Join实现内部的所有机制都是为了确保高效过滤而设计的,现在它们正在反对你而不是支持你。如果您想进行简单的未经过滤的笛卡尔积运算,我们提供SelectMany序列运算符来执行此操作。用正确的工具完成工作。 - Eric Lippert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接