两个 List 之间的交集

huangapple go评论108阅读模式
英文:

Intersect between two List<int[]>

问题

我想要获取以下两个列表的交集。

List<int[]> list1 = new List<int[]>
{
    new int[] { 0, 0, 0, },
    new int[] { 1, 1, 1, }
};

List<int[]> list2 = new List<int[]>
{
    new int[] { 1, 1, 1, },
    new int[] { 2, 2, 2, }
};

但是当我尝试以下操作时:

List<int[]> intersection = list1.Intersect(list2).ToList();

交集返回为空。当我尝试使用两个 List<int>(而不是数组)进行相同的设置时,它按预期工作。

这是我第一次使用 Linq。如何获取这个交集?

英文:

I want to get the intersection of the following two lists.

List&lt;int[]&gt; list1 = new List&lt;int[]&gt;
{
    new int[] { 0, 0, 0, },
    new int[] { 1, 1, 1, }
};

List&lt;int[]&gt; list2 = new List&lt;int[]&gt;
{
    new int[] { 1, 1, 1, },
    new int[] { 2, 2, 2, }
};

But when I try

List&lt;int[]&gt; intersection = list1.Intersect(list2).ToList();

The intersection returns empty. When I try this exact setup with two List&lt;int&gt; (not array) it works as expected.

This is my first time using Linq. How can I get this intersection?

答案1

得分: 4

使用Where()Any()的方法

你可以使用SequenceEqual来比较列表/数组的相等性以及每个继承IEnumerable的对象。

List<int[]> intersection = list1.Where(l1 => list2.Any(l2 => l1.SequenceEqual(l2))).ToList();
英文:

Approach with Where() and Any()

You can compare the equality of lists / arrays and every object that inherits IEnumerable with SequenceEqual

List&lt;int[]&gt; intersection = list1.Where(l1 =&gt; list2.Any(l2=&gt; l1.SequenceEqual(l2))).ToList();

答案2

得分: 1

默认的比较器用于检查相等性是引用比较。这个默认值不适用于比较数组的内容。

您可以通过使用自定义比较器(派生自IEqualityComparer)来实现您需要的功能,该比较器实际上比较了数组的内容:

// 自定义比较器:
class MyComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] a, int[] b) 
    {
        if (ReferenceEquals(a, b)) return true; 
        if (a is null) return false; 
        if (b is null) return false;
        return a.SequenceEqual(b); 
    }
    public int  GetHashCode(int[] a)     
    { 
        return a.Aggregate(1, (acc, item) => acc * item); 
    }
}

然后,您可以用于交集操作:

List<int[]> list1 = new List<int[]>
{
    new int[] { 0, 0, 0, },
    new int[] { 1, 1, 1, }
};

List<int[]> list2 = new List<int[]>
{
    new int[] { 1, 1, 1, },
    new int[] { 2, 2, 2, }
};

// 使用自定义比较器进行交集操作:
List<int[]> intersection = list1.Intersect(list2, new MyComparer()).ToList();

foreach (var list in intersection)
{
    Console.WriteLine("{0}", string.Join(", ", list));
}

输出:

1, 1, 1

注意:
我的GetHashCode实现只是一个简单的示例,您可能需要进行优化。您可以参考@Oliver的评论以获取如何改进它(需要使用HashCode的Microsoft.Bcl.HashCode NuGet包)。另一个问题是,我的GetHashCode需要遍历整个数组,当数组很长时,可能会溢出。正如@Dmitry Bychenko在评论中所指出的,您可以使用以下代码替代(也需要使用来自Microsoft.Bcl.HashCode的HashCode):

return HashCode.Combine(a.DefaultIfEmpty().Take(4)); 

这将最多考虑4个项目,并让.Net来组合它们。

英文:

The default comparer used for checking equality is a reference comparison.
This default is not suited for comparing the content of your arrays.

You can achieve what you need by using a custom comparer (derived from IEqualityComparer), that actually compares the content of the arrays:

// Custom comparer:
class MyComparer : IEqualityComparer&lt;int[]&gt;
{
    public bool Equals(int[] a, int[] b) 
    {
        if (ReferenceEquals(a, b)) return true; 
        if (a is null) return false; 
        if (b is null) return false;
        return a.SequenceEqual(b); 
    }
    public int  GetHashCode(int[] a)     
    { 
        return a.Aggregate(1, (acc, item) =&gt; acc * item); 
    }
}

Then you can use for the intersection:

List&lt;int[]&gt; list1 = new List&lt;int[]&gt;
            {
                new int[] { 0, 0, 0, },
                new int[] { 1, 1, 1, }
            };

List&lt;int[]&gt; list2 = new List&lt;int[]&gt;
            {
                new int[] { 1, 1, 1, },
                new int[] { 2, 2, 2, }
            };

// Intersection with custom comparer:
//------------------------------------------------vvvvvvvvvvvvvvvv----------
List&lt;int[]&gt; intersection = list1.Intersect(list2, new MyComparer()).ToList();

foreach (var list in intersection)
{
    Console.WriteLine(&quot;{0}&quot;, string.Join(&quot;, &quot;, list));
}

Output:

1, 1, 1

Note:
My implementation of GetHashCode is just a trivial one you might need to refine.
You can see @Oliver's comment below regarding how to improve it (it requires the Microsoft.Bcl.HashCode NuGet for HashCode).
Another issue with my GetHashCode is that it requires to traverse the whole array. It can also overflow especially (easily) when the arrays are long. As commented by @Dmitry Bychenko, you can use the following instead (also requires HashCode from Microsoft.Bcl.HashCode):

return HashCode.Combine(a.DefaultIfEmpty().Take(4)); 

This will take at most 4 items into consideration and let .Net combine them.

huangapple
  • 本文由 发表于 2023年3月7日 15:10:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75658918.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定