Существует ли встроенный метод сравнения коллекций?

StackOverflow https://stackoverflow.com/questions/43500

  •  09-06-2019
  •  | 
  •  

Вопрос

Я хотел бы сравнить содержимое пары коллекций в моем методе Equals.У меня есть словарь и IList.Есть ли встроенный метод для этого?

Отредактированный:Я хочу сравнить два словаря и два ILists, поэтому я думаю, что значение равенства понятно - если два словаря содержат одинаковые ключи, сопоставленные с одинаковыми значениями, то они равны.

Это было полезно?

Решение

Enumerable.SequenceEqual

Определяет, равны ли две последовательности, сравнивая их элементы с помощью указанного IEqualityComparer(T).

Вы не можете напрямую сравнить список и словарь, но вы могли бы сравнить список значений из словаря со списком

Другие советы

Как предлагали и отмечали другие, SequenceEqual чувствителен к порядку.Чтобы решить эту проблему, вы можете отсортировать словарь по ключу (который уникален, и, следовательно, сортировка всегда стабильна), а затем использовать SequenceEqual.Следующее выражение проверяет, равны ли два словаря независимо от их внутреннего порядка:

dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

Редактировать: Как указал Йеппе Стиг Нильсен, некоторые объекты имеют IComparer<T> это несовместимо с их IEqualityComparer<T>, что приводит к неверным результатам.При использовании ключей с таким объектом необходимо указать правильный IComparer<T> за этими ключами.Например, со строковыми ключами (которые демонстрируют эту проблему) вы должны выполнить следующее, чтобы получить правильные результаты:

dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))

В дополнение к упомянутому Последовательность одинаковая, который

значение true, если два списка имеют одинаковую длину и соответствующие элементы равны по данным компаратор

(который может быть средством сравнения по умолчанию, т.е.переопределенный Equals())

стоит упомянуть, что в файле .Net4 есть Установленные значения вкл . ISet объекты, которые

игнорирует порядок элементов и любые повторяющиеся элементы.

Поэтому, если вы хотите иметь список объектов, но они не обязательно должны располагаться в определенном порядке, учтите, что ISet (как HashSet) может быть правильным выбором.

Взгляните на Перечислимый.Последовательность одинаковая способ

var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);

В .NET отсутствуют какие-либо мощные инструменты для сравнения коллекций.Я разработал простое решение, которое вы можете найти по ссылке ниже:

http://robertbouillon.com/2010/04/29/comparing-collections-in-net/

Это позволит выполнить сравнение на равенство независимо от порядка:

var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;

Это позволит проверить, были ли элементы добавлены / удалены:

var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added;   //Sally
var inbothlists = diff.Equal;   //Bob

При этом будет видно, какие элементы в словаре изменились:

var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
  Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa

Я не знал о методе Enumerable.SequenceEqual (вы каждый день чему-то учитесь ....), но я собирался предложить использовать метод расширения;что - то вроде этого:

    public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
    {
        if (InternalList.Count != ExternalList.Count)
        {
            return false;
        }
        else
        {
            for (int i = 0; i < InternalList.Count; i++)
            {
                if (InternalList[i] != ExternalList[i])
                    return false;
            }
        }

        return true;

    }

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

Это не дает прямого ответа на ваши вопросы, но как тестовые инструменты MS, так и NUnit предоставляют

 CollectionAssert.AreEquivalent

который делает в значительной степени то, что вы хотите.

Для сравнения коллекций вы также можете использовать LINQ. Enumerable.Intersect возвращает все пары, которые равны.Вы можете сравнить два словаря следующим образом:

(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count

Первое сравнение необходимо, потому что dict2 может содержать все ключи от dict1 и даже больше.

Вы также можете использовать think of variations, используя Enumerable.Except и Enumerable.Union это приводит к аналогичным результатам.Но может быть использован для определения точных различий между наборами.

Как насчет этого примера:

 static void Main()
{
    // Create a dictionary and add several elements to it.
    var dict = new Dictionary<string, int>();
    dict.Add("cat", 2);
    dict.Add("dog", 3);
    dict.Add("x", 4);

    // Create another dictionary.
    var dict2 = new Dictionary<string, int>();
    dict2.Add("cat", 2);
    dict2.Add("dog", 3);
    dict2.Add("x", 4);

    // Test for equality.
    bool equal = false;
    if (dict.Count == dict2.Count) // Require equal count.
    {
        equal = true;
        foreach (var pair in dict)
        {
            int value;
            if (dict2.TryGetValue(pair.Key, out value))
            {
                // Require value be equal.
                if (value != pair.Value)
                {
                    equal = false;
                    break;
                }
            }
            else
            {
                // Require key be present.
                equal = false;
                break;
            }
        }
    }
    Console.WriteLine(equal);
}

Вежливость : https://www.dotnetperls.com/dictionary-equals

Для упорядоченных коллекций (список, Массив) используйте SequenceEqual

для использования HashSet SetEquals

для словаря вы можете сделать:

namespace System.Collections.Generic {
  public static class ExtensionMethods {
    public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
      if (object.ReferenceEquals(d1, d2)) return true; 
      if (d2 is null || d1.Count != d2.Count) return false;
      foreach (var (d1key, d1value) in d1) {
        if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
        if (!d1value.Equals(d2value)) return false;
      }
      return true;
    }
  }
}

(Более оптимизированное решение будет использовать сортировку, но для этого потребуется IComparable<TValue>)

Нет.В системе сбора данных нет никакого понятия равенства.Если вы подумаете об этом, то не существует способа сравнения коллекций, который не был бы субъективным.Например, сравнивая ваш IList с вашим словарем, были бы они равны, если бы все ключи были в IList, все значения были в IList или если бы оба были в IList?Нет очевидного способа сравнить эти две коллекции без знания того, для чего они должны использоваться, поэтому метод equals общего назначения не имеет смысла.

Нет, потому что фреймворк не знает, как сравнивать содержимое ваших списков.

Взгляните на это:

http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx

public bool CompareStringLists(List<string> list1, List<string> list2)
{
    if (list1.Count != list2.Count) return false;

    foreach(string item in list1)
    {
        if (!list2.Contains(item)) return false;
    }

    return true;
}

Этого не было, нет и, возможно, не будет, по крайней мере, я бы так считал.Причина в том, что равенство коллекций, вероятно, является поведением, определяемым пользователем.

Элементы в коллекциях не должны располагаться в определенном порядке, хотя они, естественно, упорядочены, это не то, на что должны полагаться алгоритмы сравнения.Допустим, у вас есть две коллекции:

{1, 2, 3, 4}
{4, 3, 2, 1}

Равны они или нет?Вы должны знать, но я не знаю, какова ваша точка зрения.

Коллекции концептуально неупорядочены по умолчанию, пока алгоритмы не предоставят правила сортировки.То же самое, на что обращает ваше внимание SQL server, - это когда вы пытаетесь выполнить разбивку на страницы, это требует от вас предоставления правил сортировки:

https://docs.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

Еще две коллекции:

{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}

Опять же, равны они или нет?Это ты мне скажи ..

Повторяемость элементов коллекции играет свою роль в различных сценариях, и некоторые коллекции, такие как Dictionary<TKey, TValue> даже не допускайте повторения элементов.

Я полагаю, что эти виды равенства определяются приложением, и поэтому фреймворк не предоставил всех возможных реализаций.

Ну, в общих случаях Enumerable.SequenceEqual достаточно хорош, но он возвращает false в следующем случае:

var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false

Я прочитал несколько ответов на подобные вопросы (вы можете Google для них) и что бы я использовал, в целом:

public static class CollectionExtensions {
    public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
        if(object.ReferenceEquals(first, second)) {
            return true;
        }

        if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
            return Enumerable.SequenceEqual(first, second);
        }

        if(first is ICollection<T> && second is ICollection<T>) {
            if(first.Count()!=second.Count()) {
                return false;
            }
        }

        first=first.OrderBy(x => x.GetHashCode());
        second=second.OrderBy(x => x.GetHashCode());
        return CollectionExtensions.Represents(first, second);
    }
}

Это означает, что одна коллекция представляет другую в своих элементах, включая повторяющиеся времена, без учета первоначального порядка.Некоторые замечания по реализации:

  • GetHashCode() это просто для упорядочения, а не для равенства;Я думаю, в данном случае этого достаточно

  • Count() на самом деле не будет перечислять коллекцию, а напрямую попадет в реализацию свойства ICollection<T>.Count

  • Если ссылки равны, то это просто Борис

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top