比较两个 IEnumerable 的内容

发布于 2024-07-14 09:20:04 字数 235 浏览 8 评论 0原文

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

例如:

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

您有 SequenceEquals,但是我必须首先 Order 两个序列,不是吗?

Is there a built in LINQ method thing I can use to find out if two sequences contains the same items, not taking the order into account?

For example:

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

You have the SequenceEquals, but then I would have to Order both sequences first, wouldn't I?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

甚是思念 2024-07-21 09:20:04

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

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

There are quite a few ways. Assume A and B is IEnumerable.

!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc
痴梦一场 2024-07-21 09:20:04

如果您不关心重复项(即您认为 {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) 比较的两种 O(n log n) 类型不同,您有一个 O(n) 操作来构建计数集,并有一个 O (n) 检查。

If you don't care about duplicates (i.e. you'd consider {1, 2, 3} to be equal to {1, 2, 3, 2}) then:

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

(Or whatever type is the element type instead of int).

Otherwise:

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;
}

Keep a tally of each element in the first sequence, then check the second against it. The moment you have one too many in the second sequence you can return false, otherwise if you have nothing left in the dictionary of tallies they are equal, or false if there's any elements left.

Rather than the two O(n log n) sorts of using OrderBy() followed by the O(n) comparison, you've an O(n) operation building the set of tallies, and an O(n) check against it.

冷月断魂刀 2024-07-21 09:20:04

使用两个 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 进行评估。 如果计数不相等,则我们检查例外。

With two IEnumerables (A and B) :

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

I think this is better than Except(A).Count because the entire Excep will not be evaluated. It will stop as soon as one element is found in the Except. With the Count, the entire Except is evaluated.
On top of this, we can avoid the evaluation of these costly Except just by checking the Count properties first. If Counts are not Equal, then we check the Excepts.

以往的大感动 2024-07-21 09:20:04

尝试使用 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 };

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

Try the HashSet class:

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

But that does only work correctly if the values are distinct.

For example

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

are also considered as "equal" with the mentioned method.

倾城月光淡如水﹏ 2024-07-21 09:20:04

坚持您的示例,您可以将 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... */}

Sticking with your example, you can make both of IEnumerable to be of type List and then use SequenceEqual as the example below:

var first = Enumerable.Range(1, 3);
var second = Enumerable.Range(1, 3);
var areTheyEqual = first.ToList().SequenceEqual(second.ToList());
if (areTheyEqual)
{ /* do something... */}
睫毛溺水了 2024-07-21 09:20:04

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

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

To compare the data in the two objects, I simply used this

A.Except(B).Any() || B.Except(A).Any()
最单纯的乌龟 2024-07-21 09:20:04

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

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>();

它将返回重复项

I did this for merging new items into a collection without duplicates,
it takes two collections and returns all the items with out any duplicates

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

Now by removing the ! for the contains statement

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

it will return the duplicates

烟若柳尘 2024-07-21 09:20:04

我认为排序顺序是实现这一目标的最快方法。

I think ordering the sequence is the fastest way you can achieve this.

鲜肉鲜肉永远不皱 2024-07-21 09:20:04

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

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

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

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

If you're really just testing to see if there are duplicates, then leppie's suggestion should work:

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

But if you just need to arrive at an IEnumerable with no duplicates:

var result = A.Union(B).Distinct();
暗恋未遂 2024-07-21 09:20:04

对于发现问题也关心顺序的人来说,这里有一些可能有用的东西。 用于枚举的静态 CompareTo 方法和 IComparer 实现。 之后的测试包括:

    public class EnumerableComparer<T> : IComparer<IEnumerable<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)
    {
        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);
    }

For people finding question that also care about order, here is something that may be useful. A static CompareTo method for enumerables and an IComparer implementation. Tests included afterwards:

    public class EnumerableComparer<T> : IComparer<IEnumerable<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)
    {
        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);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文