查找所有相交数据,而不仅仅是唯一值

时间:2010-02-01 21:00:45

标签: c# linq intersection

我以为我理解Intersect,但事实证明我错了。

 List<int> list1 = new List<int>() { 1, 2, 3, 2, 3};
 List<int> list2 = new List<int>() { 2, 3, 4, 3, 4};

 list1.Intersect(list2) =>      2,3

 //But what I want is:
 // =>  2,3,2,3,2,3,3

我可以想象一下:

 var intersected = list1.Intersect(list2);
 var list3 = new List<int>();
 list3.AddRange(list1.Where(I => intersected.Contains(I)));
 list3.AddRange(list2.Where(I => intersected.Contains(I)));

在LINQ中有更简单的方法来实现这一目标吗?

我确实需要说明我不关心结果的顺序。

2,2,2,3,3,3,3也可以。

问题是我在一个非常大的集合中使用它,所以我需要效率。

我们谈论的是对象,而不是整体。这只是一个简单的例子,但我意识到这可以有所作为。

4 个答案:

答案 0 :(得分:16)

让我们看看我们是否可以精确地描述您想要的东西。如果我错了,请纠正我。你想要的是:列表1的所有元素,按顺序,也出现在列表2中,然后依次列出列表2的所有元素,它们也出现在列表1中。是吗?

似乎很简单。

return list1.Where(x=>list2.Contains(x))
     .Concat(list2.Where(y=>list1.Contains(y)))
     .ToList();

请注意,对于大型列表,这效率不高。如果列表每个都有一千个项目,那么这将进行几百万次比较。如果您处于这种情况,那么您希望使用更有效的数据结构来测试成员资格:

list1set = new HashSet(list1);
list2set = new HashSet(list2);

return list1.Where(x=>list2set.Contains(x))
     .Concat(list2.Where(y=>list1set.Contains(y)))
     .ToList();

只进行了几千次比较,但可能会占用更多内存。

答案 1 :(得分:1)

var set = new HashSet(list1.Intersect(list2));
return list1.Concat(list2).Where(i=>set.Contains(i));

答案 2 :(得分:0)

也许这可以帮助:https://gist.github.com/mladenb/b76bcbc4063f138289243fb06d099dda

  

原始的Except / Intersect返回唯一项的集合,即使它们的合同没有这样声明(例如,这些方法的返回值不是HashSet / Set,而是IEnumerable),这可能是结果糟糕的设计决策。相反,我们可以使用更直观的实现,它从第一个枚举中返回尽可能多的相同元素,而不仅仅是唯一的元素(使用Set.Contains)。

     

此外,添加了映射功能,以帮助相交/排除不同类型的集合。

如果不需要相交/除不同类型的集合,只需检查“相交/除”的源代码,然后将通过第一个枚举迭代的部分更改为使用Set.Contains而不是Set.Add / Set删除。

答案 3 :(得分:-1)

我不相信内置API可以实现这一点。但您可以使用以下内容来获得您正在寻找的结果。

IEnumerable<T> Intersect2<T>(this IEnumerable<T> left, IEnumerable<T> right) {
  var map = left.ToDictionary(x => x, y => false);
  foreach ( var item in right ) {
    if (map.ContainsKey(item) ) {
      map[item] = true;
    }
  }
  foreach ( var cur in left.Concat(right) ) {
    if ( map.ContainsKey(cur) ) {
      yield return cur;
    }
  }
}