C#:比较两个 IEnumerables 的内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/630263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 11:02:10  来源:igfitidea点击:

C#: Compare contents of two IEnumerables

c#linq

提问by Svish

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?

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

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?

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

采纳答案by leppie

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

有好几种方法。假设 A 和 B 是 IEnumerable。

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

回答by Mehrdad Afshari

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

我认为对序列进行排序是实现这一目标的最快方法。

回答by Bob The Janitor

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

现在通过删除 ! 对于 contains 语句

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

它将返回重复项

回答by Daniel

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

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

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

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

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

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

回答by Rauhotz

Try the HashSet class:

试试 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

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.

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

回答by Yoann

With two IEnumerables (A and B) :

使用两个 IEnumerables(A 和 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.

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

回答by Jon Hanna

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

如果您不关心重复项(即您认为{1, 2, 3}等于{1, 2, 3, 2}),则:

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

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

(或者任何类型是元素类型而不是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.

记录第一个序列中的每个元素,然后根据它检查第二个元素。如果第二个序列中的元素过多,则可以返回 false,否则,如果计数字典中没有任何内容,则它们是相等的,如果还有任何元素,则返回 false。

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.

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

回答by Gabriel Marius Popescu

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

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