C#:比较两个 IEnumerable 的内容

问题描述 投票:0回答:10

是否有一个内置的 linq 方法可以用来查找两个序列是否包含相同的项目,而不考虑顺序?

例如:

{1, 2, 3} == {2, 1, 3}
{1, 2, 3} != {2, 1, 3, 4}
{1, 2, 3} != {1, 2, 4}

你有 SequenceEquals,但是我必须先对两个序列进行排序,不是吗?

c# linq
10个回答
50
投票

有很多方法。假设 A 和 B 是 IEnumerable。

!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc

12
投票

如果您不关心重复项(即您认为

{1, 2, 3}
等于
{1, 2, 3, 2}
),那么:

new HashSet<int>(A).SetEquals(B)

(或者任何类型是元素类型而不是

int
)。

否则:

public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second)
{
    if (first == null)
        return second == null; // or throw if that's more appropriate to your use.
    if (second == null)
        return false;   // likewise.
    var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired.
    foreach(T element in first)
    {
        int count;
        dict.TryGetValue(element, out count);
        dict[element] = count + 1;
    }
    foreach(T element in second)
    {
        int count;
        if (!dict.TryGetValue(element, out count))
            return false;
        else if (--count == 0)
            dict.Remove(element);
        else
            dict[element] = count;
    }
    return dict.Count == 0;
}

记录第一个序列中的每个元素,然后对照它检查第二个元素。当第二个序列中有太多元素时,您可以返回 false,否则,如果计数字典中没有剩余任何元素,则它们相等,如果还有任何元素,则返回 false。

而不是使用

OrderBy()
的两种 O(n log n) 类型,然后进行 O(n) 比较,您有一个 O(n) 操作来构建计数集,并对其进行 O(n) 检查.


11
投票

有两个 IEnumerables(A 和 B):

bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any()))

我认为这比 except(A).Count 更好,因为整个 Excep 不会被评估。一旦在 except 中找到一个元素,它就会停止。通过计数,可以评估整个 except。 最重要的是,我们可以通过首先检查 Count 属性来避免对这些昂贵的 except 进行评估。如果计数不相等,则我们检查例外情况。


4
投票

尝试 HashSet 类:

var enumA = new[] { 1, 2, 3, 4 };
var enumB = new[] { 4, 3, 1, 2 };

var hashSet = new HashSet<int>(enumA);
hashSet.SymmetricExceptWith(enumB);
Console.WriteLine(hashSet.Count == 0); //true => equal

但是只有当值不同时,这才可以正确工作。

例如

var enumA = new[] { 1, 1, 1, 2 };
var enumB = new[] { 1, 2, 2, 2 };

也被认为与上述方法“相等”。


2
投票

坚持您的示例,您可以将 IEnumerable 都设置为 List 类型,然后使用 SequenceEqual ,如下例所示:

var first = Enumerable.Range(1, 3);
var second = Enumerable.Range(1, 3);
var areTheyEqual = first.ToList().SequenceEqual(second.ToList());
if (areTheyEqual)
{ /* do something... */}

2
投票

为了比较两个对象中的数据,我简单地使用了这个

A.Except(B).Any() || B.Except(A).Any()

1
投票

我这样做是为了将新项目合并到没有重复的集合中, 它需要两个集合并返回所有项目,没有任何重复项

List<Campaign> nonMatching = (from n in newCampaigns 
where !(from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>();

现在删除 !对于 contains 语句

List<Campaign> nonMatching = (from n in newCampaigns 
where (from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>();

它将返回重复项


0
投票

我认为排序是实现此目的的最快方法。


0
投票

如果您真的只是测试一下是否有重复项,那么 leppie 的建议应该有效:

if (A.Except(B).Count == 0 && B.Except(A).Count == 0) {...}

但是如果您只需要得到一个没有重复项的 IEnumerable:

var result = A.Union(B).Distinct();

0
投票

对于那些正在谷歌搜索并且确实关心顺序的人来说,这里有一些可能有用的东西。用于枚举的静态 CompareTo 方法和 IComparer 实现。随后进行的测试:

    public class EnumerableComparer<T> : IComparer<IEnumerable<T>>
    where T : IComparable<T>
    {
        readonly IComparer<T> comparer;
        
        public EnumerableComparer(IComparer<T> comparer = null)
        {
            this.comparer = comparer;
        }
        
        public int Compare(IEnumerable<T> first, IEnumerable<T> second)
        {
            return first.CompareTo(second, comparer);
        }
    }
    
    public static int CompareTo<T>(this IEnumerable<T> first, IEnumerable<T> second, IComparer<T> comparer = null)
        where T : IComparable<T>
    {
        comparer ??= Comparer<T>.Default;
        
        if (ReferenceEquals(first, second))
            return 0;

        if (first == null)
            return -1;

        if (second == null)
            return 1;
        
        using (var iter1 = first.GetEnumerator())
        using (var iter2 = second.GetEnumerator())
        {
            while (iter1.MoveNext())
            {
                if (iter2.MoveNext())
                {
                    var result = comparer.Compare(iter1.Current, iter2.Current);

                    if (result != 0)
                        return result;
                }
                else
                {
                    return 1;
                }
            }
            while (iter2.MoveNext())
            {
                return -1;
            }

            return 0;
        }
    }
    [Fact]
    public void CompareToWorksForEnumerables()
    {
        Array.Empty<int>().CompareTo(Array.Empty<int>()).Should().Be(0);
        ((IEnumerable<int>)null).CompareTo(null).Should().Be(0);
        Array.Empty<int>().CompareTo(null).Should().Be(1);
        ((IEnumerable<int>)null).CompareTo(Array.Empty<int>()).Should().Be(-1);
        new []{1, 2, 3}.CompareTo(new []{1, 2, 3}).Should().Be(0);
        new []{1, 2}.CompareTo(new []{1, 2, 3}).Should().Be(-1);
        new []{1, 2, 3}.CompareTo(new []{1, 2}).Should().Be(1);
        new []{2, 2}.CompareTo(new []{1, 2, 3}).Should().Be(1);
    }
© www.soinside.com 2019 - 2024. All rights reserved.