相反的Intersect()


275

相交可用于查找两个集合之间的匹配项,如下所示:

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call Intersect extension method.
var intersect = array1.Intersect(array2);
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 2, 3
}

但是我想要实现的却是相反的,我想列出一个集合中另一个集合中缺少的项目

// Assign two arrays.
int[] array1 = { 1, 2, 3 };
int[] array2 = { 2, 3, 4 };
// Call "NonIntersect" extension method.
var intersect = array1.NonIntersect(array2); // I've made up the NonIntersect method
// Write intersection to screen.
foreach (int value in intersect)
{
    Console.WriteLine(value); // Output: 4
}

13
请您确认是否要4作为输出,或1和4
岛之风Bråthen如此阐述道

@ oyvind-knobloch-brathen是的,我只想要4
Peter Bridger

23
作为附带说明,这种类型的集合称为“ 对称差”
Mike T

19
从技术上讲,对称差异将导致[1,4]。由于彼得只需要array2中的元素而不是array1中的元素(即4),因此称为相对补码(又称集合理论差)
rtorres 2014年

Answers:


376

如前所述,如果您想得到4,则可以这样:

var nonintersect = array2.Except(array1);

如果您想要真正的非相交(也包括1和4),那么就可以做到这一点:

var nonintersect = array1.Except(array2).Union( array2.Except(array1));

这不是性能最高的解决方案,但是对于较小的列表,它应该可以正常工作。


2
什么是性能更好的解决方案?谢谢!
shanabus 2012年

6
通过使用两个嵌套的for循环,您可能可以更快地完成此操作,但是代码会比这更脏。还要考虑到可读性,我很明显会使用此变体,因为它很容易阅读。
岛之风Bråthen如此阐述道

5
如果要添加,只需添加一个副标题:如果int []之前= {1,2,3}; int [] after = {2,3,3,4}; 并且您尝试使用Except来查找自'before'之后添加到'after'中的内容:var diff = after.Except(before); 'diff'包含4,而不是3,4。即当心重复元素会给您带来意想不到的结果
Paul Ryland

这会更好吗?array1.AddRange(array2.Except(array1));
LBW

86

您可以使用

a.Except(b).Union(b.Except(a));

或者你可以使用

var difference = new HashSet(a);
difference.SymmetricExceptWith(b);

2
有趣地使用SymmetricExceptWith(),我不会想到这种方法
Peter Bridger

SymmetricExceptWith这可能是我最喜欢的方法。
Ash Clarke 2013年

6
我在一个真实的应用程序中比较了两者,在每个应用程序中我都有一对大约125个字符串的列表。实际上,对于这种大小的列表,使用第一种方法实际上更快,尽管对于这两种方法而言,在不到半毫秒的时间内它几乎是微不足道的。

1
如果BCL为此具有Linq扩展方法,那就太好了。似乎是一个遗漏。
Drew Noakes

有人对SymmetricExceptWith进行了基准测试,发现它的速度更快:skylark-software.com/2011/07/linq-and-set-notation.html
Colin

11

此代码仅枚举每个序列一次,并用于Select(x => x)隐藏结果以获取干净的Linq样式的扩展方法。因为哈希使用得很好,所以使用了HashSet<T>它的运行时O(n + m)。任一列表中的重复元素均被省略。

public static IEnumerable<T> SymmetricExcept<T>(this IEnumerable<T> seq1,
    IEnumerable<T> seq2)
{
    HashSet<T> hashSet = new HashSet<T>(seq1);
    hashSet.SymmetricExceptWith(seq2);
    return hashSet.Select(x => x);
}

6

我认为您可能正在寻找Except

Except运算符产生两个序列之间的设置差。它只会返回第一个序列中未出现在第二个序列中的元素。您可以选择提供自己的相等性比较功能。

请查看此链接此链接或Google,以获取更多信息。


2

我不是100%确定您的NonIntersect方法应该做什么(关于集合论)-是
B \ A(B中所有在A中不发生的事情)吗?
如果是,那么您应该能够使用Except操作(B.Except(A))。


集的交集==A∪B\
A∩B–未知

2
/// <summary>
/// Given two list, compare and extract differences
/// http://stackoverflow.com/questions/5620266/the-opposite-of-intersect
/// </summary>
public class CompareList
{
    /// <summary>
    /// Returns list of items that are in initial but not in final list.
    /// </summary>
    /// <param name="listA"></param>
    /// <param name="listB"></param>
    /// <returns></returns>
    public static IEnumerable<string> NonIntersect(
        List<string> initial, List<string> final)
    {
        //subtracts the content of initial from final
        //assumes that final.length < initial.length
        return initial.Except(final);
    }

    /// <summary>
    /// Returns the symmetric difference between the two list.
    /// http://en.wikipedia.org/wiki/Symmetric_difference
    /// </summary>
    /// <param name="initial"></param>
    /// <param name="final"></param>
    /// <returns></returns>
    public static IEnumerable<string> SymmetricDifference(
        List<string> initial, List<string> final)
    {
        IEnumerable<string> setA = NonIntersect(final, initial);
        IEnumerable<string> setB = NonIntersect(initial, final);
        // sum and return the two set.
        return setA.Concat(setB);
    }
}

2

array1.NonIntersect(array2);

非相交此类运算符不存在于Linq中,您应该这样做

除了->联合->除

a.except(b).union(b.Except(a));

-1
string left = "411329_SOFT_MAC_GREEN";
string right= "SOFT_MAC_GREEN";

string[] l = left.Split('_');
string[] r = right.Split('_');

string[] distinctLeft = l.Distinct().ToArray();
string[] distinctRight = r.Distinct().ToArray();

var commonWord = l.Except(r, StringComparer.OrdinalIgnoreCase)
string result = String.Join("_",commonWord);
result = "411329"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.