Domanda

Vorrei confrontare il contenuto di un paio di raccolte nel mio metodo Equals.Ho un dizionario e un IList.Esiste un metodo integrato per farlo?

Modificato:Voglio confrontare due dizionari e due IList, quindi penso che il significato di uguaglianza sia chiaro: se i due dizionari contengono le stesse chiavi mappate sugli stessi valori, allora sono uguali.

È stato utile?

Soluzione

Enumerable.SequenceEqual

Determina se due sequenze sono uguali confrontando i relativi elementi utilizzando un oggetto IEqualityComparer(T) specificato.

Non puoi confrontare direttamente l'elenco e il dizionario, ma puoi confrontare l'elenco dei valori del Dizionario con l'elenco

Altri suggerimenti

Come altri hanno suggerito e notato, SequenceEqual è sensibile all'ordine.Per risolvere questo problema, puoi ordinare il dizionario per chiave (che è univoca e quindi l'ordinamento è sempre stabile) e quindi utilizzare SequenceEqual.La seguente espressione controlla se due dizionari sono uguali indipendentemente dal loro ordine interno:

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

MODIFICARE: Come sottolineato da Jeppe Stig Nielsen, alcuni oggetti hanno un IComparer<T> che è incompatibile con il loro IEqualityComparer<T>, producendo risultati errati.Quando si utilizzano chiavi con tale oggetto, è necessario specificare un valore Correct IComparer<T> per quelle chiavi.Ad esempio, con le chiavi stringa (che presentano questo problema), è necessario effettuare le seguenti operazioni per ottenere risultati corretti:

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

Oltre a quanto menzionato SequenzaUguale, Quale

è vero se due elenchi sono di uguale lunghezza e i loro elementi corrispondenti si confrontano uguali secondo un confronto

(che potrebbe essere l'operatore di confronto predefinito, ad es.un override Equals())

vale la pena ricordare che in .Net4 è presente ImpostaEquali SU ISet oggetti, che

ignora l'ordine degli elementi e tutti gli elementi duplicati.

Quindi, se vuoi avere un elenco di oggetti, ma non è necessario che siano in un ordine specifico, considera che an ISet (come un HashSet) potrebbe essere la scelta giusta.

Dai un'occhiata a Enumerable.SequenceEqual metodo

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 non dispone di strumenti potenti per confrontare le raccolte.Ho sviluppato una soluzione semplice che puoi trovare al link seguente:

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

Ciò eseguirà un confronto di uguaglianza indipendentemente dall'ordine:

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

Questo controllerà se gli elementi sono stati aggiunti/rimossi:

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

Questo vedrà quali elementi nel dizionario sono cambiati:

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

Non conoscevo il metodo Enumerable.SequenceEqual (si impara qualcosa ogni giorno...), ma volevo suggerirti di utilizzare un metodo di estensione;qualcosa come questo:

    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;

    }

È interessante notare che, dopo aver impiegato 2 secondi per leggere SequenceEqual, sembra che Microsoft abbia creato la funzione che ho descritto per te.

Questo non risponde direttamente alle tue domande, ma lo forniscono sia TestTools di MS che NUnit

 CollectionAssert.AreEquivalent

che fa praticamente quello che vuoi.

Per confrontare le raccolte puoi anche utilizzare LINQ. Enumerable.Intersect restituisce tutte le coppie uguali.Puoi confrontare due dizionari come questo:

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

Il primo confronto è necessario perché dict2 può contenere tutte le chiavi da dict1 e altro ancora.

Puoi anche pensare alle variazioni usando Enumerable.Except E Enumerable.Union che portano a risultati simili.Ma può essere utilizzato per determinare le differenze esatte tra i set.

Che ne dici di questo esempio:

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

Per gentile concessione: https://www.dotnetperls.com/dictionary-equals

Per raccolte ordinate (List, Array) utilizzare SequenceEqual

per l'uso di HashSet SetEquals

per il dizionario puoi fare:

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

(Una soluzione più ottimizzata utilizzerà l'ordinamento ma ciò richiederà IComparable<TValue>)

NO.Il framework della raccolta non ha alcun concetto di uguaglianza.Se ci pensi, non c'è modo di confrontare le collezioni che non sia soggettivo.Ad esempio, confrontando il tuo IList con il tuo dizionario, sarebbero uguali se tutte le chiavi fossero in IList, tutti i valori fossero in IList o se entrambi fossero in IList?Non esiste un modo ovvio per confrontare queste due raccolte senza sapere per cosa devono essere utilizzate, quindi un metodo di scopo generale uguale non ha senso.

No, perché il framework non sa come confrontare il contenuto delle tue liste.

Dai un'occhiata a questo:

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

Non c'era, non c'è e potrebbe non esserci, almeno così credo.Il motivo è che l'uguaglianza delle raccolte è probabilmente un comportamento definito dall'utente.

Gli elementi nelle raccolte non dovrebbero essere in un ordine particolare, sebbene abbiano un ordinamento naturale, non è ciò su cui dovrebbero fare affidamento gli algoritmi di confronto.Supponiamo che tu abbia due raccolte di:

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

Sono uguali o no?Devi saperlo, ma non so quale sia il tuo punto di vista.

Le raccolte sono concettualmente non ordinate per impostazione predefinita, finché gli algoritmi non forniscono le regole di ordinamento.La stessa cosa che SQL Server porterà alla tua attenzione è quando provi a eseguire l'impaginazione, richiede di fornire regole di ordinamento:

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

Ancora altre due collezioni:

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

Ancora una volta, sono uguali o no?Dimmelo tu ..

La ripetibilità degli elementi di una raccolta gioca il suo ruolo in diversi scenari e in alcune raccolte simili Dictionary<TKey, TValue> non consentire nemmeno elementi ripetuti.

Credo che questi tipi di uguaglianza siano definiti dall'applicazione e quindi il quadro non ha fornito tutte le possibili implementazioni.

Bene, in casi generali Enumerable.SequenceEqual è abbastanza buono ma restituisce false nel seguente caso:

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

Ho letto alcune risposte a domande come questa (potresti Google per loro) e cosa utilizzerei, in generale:

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

Ciò significa che una raccolta rappresenta l'altra nei suoi elementi, comprese le volte ripetute, senza tenere conto dell'ordinamento originale.Alcune note di implementazione:

  • GetHashCode() è solo per l'ordinamento, non per l'uguaglianza;Penso che sia sufficiente in questo caso

  • Count() non enumera realmente la raccolta e rientra direttamente nell'implementazione della proprietà di ICollection<T>.Count

  • Se i riferimenti sono pari è proprio Boris

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top