Подтвердить что ты не робот

С#: Сравнить содержимое двух IEnumerables

Есть ли встроенная функция метода linq, которую я могу использовать, чтобы узнать, содержат ли две последовательности одни и те же элементы, не принимая во внимание порядок?

Например:

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

У вас есть SequenceEquals, но тогда мне придется сначала заказывать обе последовательности, не так ли?

4b9b3361

Ответ 1

Существует немало способов. Предположим, что A и B IEnumerable.

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

Ответ 2

С двумя IEnumerables (A и B):

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

Я думаю, что это лучше, чем Except (A). Укажите, что весь Excep не будет оцениваться. Он остановится, как только один элемент будет найден в Except. С графом оценивается весь Except. Кроме того, мы можем избежать оценки этих затрат. Кроме того, сначала проверяя свойства Count. Если Counts не равны, тогда мы проверяем Excepts.

Ответ 3

Если вам не нужны дубликаты (т.е. вы считаете {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 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, иначе, если у вас ничего не осталось в словаре таблеток, они равны или ложны, если остались какие-либо элементы.

Вместо двух типов O (n log n), использующих OrderBy(), за которым следует сравнение O (n), вы выполняете операцию O (n) с набором наборов и проверку O (n) против него.

Ответ 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 };

также считаются "равными" с указанным методом.

Ответ 5

Я думаю, что упорядочение последовательности - это самый быстрый способ добиться этого.

Ответ 6

Я сделал это для слияния новых элементов в коллекцию без дубликатов,  он берет две коллекции и возвращает все элементы без каких-либо дубликатов

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

он вернет дубликаты

Ответ 7

Если вы действительно просто проверяете, есть ли дубликаты, то предложение leppie должно работать:

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

Но если вам просто нужно получить IEnumerable без дубликатов:

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

Ответ 8

Придерживаясь своего примера, вы можете сделать так, чтобы оба 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... */}