LINQ: определите, содержат ли две последовательности точно такие же элементы


Мне нужно определить, содержат ли два набора точно такие же элементы. Заказ не имеет значения.

например, эти два массива следует считать равными:

IEnumerable<int> data = new []{3, 5, 6, 9};
IEnumerable<int> otherData = new []{6, 5, 9, 3}

один набор не может содержать элементов, которых нет в другом.

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

7 61

7 ответов:

Если вы хотите обработать массивы как "наборы" и игнорировать порядок и повторяющиеся элементы, вы можете использовать HashSet<T>.SetEquals метод:

var isEqual = new HashSet<int>(first).SetEquals(second);

в противном случае, ваш лучший выбор, вероятно, сортировка последовательности таким же образом и с помощью SequenceEqual сравнивать их.

Я предлагаю сортировать оба и делать поэлементное сравнение.

data.OrderBy(x => x).SequenceEqual(otherData.OrderBy(x => x))

Я не уверен, как быстро реализации OrderBy есть, но если это o(n log n) сортировка, как вы ожидаете, общий алгоритм также O(n log n).

для некоторых случаев данных вы можете улучшить это, используя пользовательскую реализацию OrderBy, которая, например, использует сортировку подсчета для O(n+k) с K размером диапазона, в котором лежат значения.

Если у вас могут быть дубликаты (или если вам нужно решение, которое лучше работает для более длинных списков), я бы попробовал что-то вроде этого:

static bool IsSame<T>(IEnumerable<T> set1, IEnumerable<T> set2)
{
    if (set1 == null && set2 == null)
        return true;
    if (set1 == null || set2 == null)
        return false;

    List<T> list1 = set1.ToList();
    List<T> list2 = set2.ToList();

    if (list1.Count != list2.Count)
        return false;

    list1.Sort();
    list2.Sort();

    return list1.SequenceEqual(list2);
}

UPDATE: упс, вы, ребята, правы-решение Except() ниже должно смотреть в обе стороны, прежде чем переходить улицу. И у него есть паршивый perf для более длинных списков. Игнорируйте предложение ниже! : -)

вот один простой способ сделать это. Обратите внимание, что это предполагает, что списки не имеют дубликатов.

bool same = data.Except (otherData).Count() == 0;

вот еще один способ сделать это:

IEnumerable<int> data = new[] { 3, 5, 6, 9 };
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };

data = data.OrderBy(d => d);
otherData = otherData.OrderBy(d => d);
data.Zip(otherData, (x, y) => Tuple.Create(x, y)).All(d => d.Item1 == d.Item2);
  1. во-первых, проверьте длину. Если они разные, то наборы разные.
  2. можно сделать data.Intersect(otherData);, и проверьте, что длина идентична.
  3. или просто отсортируйте наборы и повторите их.

Это должно помочь:

    IEnumerable<int> data = new []{ 3,5,6,9 };
    IEnumerable<int> otherData = new[] {6, 5, 9, 3};

    if(data.All(x => otherData.Contains(x)))
    {
        //Code Goes Here
    }

сначала проверьте, имеют ли обе коллекции данных одинаковое количество элементов, и проверьте, представлены ли все элементы в одной коллекции в другой

        IEnumerable<int> data = new[] { 3, 5, 6, 9 };
        IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };

        bool equals = data.Count() == otherData.Count() && data.All(x => otherData.Contains(x));