Il modo più semplice per confrontare le matrici in C #


180

In Java, Arrays.equals()consente di confrontare facilmente il contenuto di due array di base (sono disponibili sovraccarichi per tutti i tipi di base).

C'è qualcosa del genere in C #? Esiste un modo "magico" per confrontare il contenuto di due array in C #?


1
Aggiunto ".net" ai tag perché questa tecnica potrebbe essere utilizzata in altri linguaggi simili basati su .net.
Evan Plaice,

3
A tutti coloro che leggono questo, tieni presente che la risposta accettata utilizza SequenceEqual. SequenceEqual controlla non solo se contengono gli stessi dati, ma anche se contengono gli stessi dati nello stesso ordine
John Demetriou,

Risposte:


262

Puoi usare Enumerable.SequenceEqual. Questo funziona per qualsiasi IEnumerable<T>, non solo array.


Funziona solo se sono nello stesso ordine
John Demetriou,

1
SequenceEqualpotrebbe non essere una buona scelta dal punto di vista delle prestazioni, poiché la sua implementazione corrente può elencare completamente una delle sue fonti se differiscono solo per lunghezza. Con le matrici, potremmo Lengthprima verificare l' uguaglianza, al fine di evitare di enumerare matrici di lunghezze diverse solo per finire per cedere false.
Frédéric,

72

Utilizzare Enumerable.SequenceEqualin LINQ .

int[] arr1 = new int[] { 1,2,3};
int[] arr2 = new int[] { 3,2,1 };

Console.WriteLine(arr1.SequenceEqual(arr2)); // false
Console.WriteLine(arr1.Reverse().SequenceEqual(arr2)); // true

1
Tieni presente che questo genera argomenti nulli, quindi assicurati di non assumerlonew int[] {1}.SequenceEquals(null) == false
sara

30

Anche per array (e tuple) è possibile utilizzare nuove interfacce da .NET 4.0: IStructuralComparable e IStructuralEquatable . Usandoli è possibile non solo verificare l'uguaglianza degli array, ma anche confrontarli.

static class StructuralExtensions
{
    public static bool StructuralEquals<T>(this T a, T b)
        where T : IStructuralEquatable
    {
        return a.Equals(b, StructuralComparisons.StructuralEqualityComparer);
    }

    public static int StructuralCompare<T>(this T a, T b)
        where T : IStructuralComparable
    {
        return a.CompareTo(b, StructuralComparisons.StructuralComparer);
    }
}

{
    var a = new[] { 1, 2, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.Equals(b)); // False
    Console.WriteLine(a.StructuralEquals(b)); // True
}
{
    var a = new[] { 1, 3, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.StructuralCompare(b)); // 1
}

Perdonami, dovrebbe essere 1 o 0 in a.StructuralCompare(b)?
mafu

Su array di tipi di valore di grandi dimensioni, si riscontra un impatto negativo sull'utilizzo di questi, poiché la loro implementazione corrente impacchetterà ciascun valore da confrontare.
Frédéric,

18

Per .NET 4.0 e versioni successive è possibile confrontare elementi in array o tuple utilizzando il tipo StructuralComparisons :

object[] a1 = { "string", 123, true };
object[] a2 = { "string", 123, true };

Console.WriteLine (a1 == a2);        // False (because arrays is reference types)
Console.WriteLine (a1.Equals (a2));  // False (because arrays is reference types)

IStructuralEquatable se1 = a1;
//Next returns True
Console.WriteLine (se1.Equals (a2, StructuralComparisons.StructuralEqualityComparer)); 

Modifica: parlato troppo presto. Posso fare StructualEqualityCompare con IStructuralComparable? Voglio chiamare CompareTo con due matrici di oggetti per scoprire quale viene "per primo". Ho provato IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Ottenere: impossibile convertire da "System.Collections.IEqualityComparer" a "System.Collections.IComparer"
shindigo

1
OK - la chiamata corretta è: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
shindigo,

15

SequenceEqual restituirà vero solo se due condizioni o soddisfatte.

  1. Contengono gli stessi elementi.
  2. Gli elementi sono nello stesso ordine.

Se vuoi solo verificare se contengono gli stessi elementi indipendentemente dal loro ordine e il tuo problema è del tipo

Valori2 contiene tutti i valori contenuti in Valori1?

è possibile utilizzare il metodo di estensione LINQ Enumerable.Excepte quindi verificare se il risultato ha qualche valore. Ecco un esempio

int[] values1 = { 1, 2, 3, 4 };
int[] values2 = { 1, 2, 5 };
var result = values1.Except(values2);
if(result.Count()==0)
{
   //They are the same
}
else
{
    //They are different
}

E anche usando questo ottieni automaticamente anche i diversi elementi. Due piccioni con una fava.

Tieni presente che se esegui il codice in questo modo

var result = values2.Except(values1);

otterrai risultati diversi.

Nel mio caso ho una copia locale di un array e voglio verificare se qualcosa è stato rimosso dall'array originale, quindi uso questo metodo.


2
Le matrici che contengono gli stessi valori in ordine diverso, semplicemente NON SONO PARI. Ti piace 'Demetriou' == 'uoirtemeD'?
edc65,

Dipende. Se si utilizzano gli array come raccolte non ordinate e è necessario verificare solo che contengano gli stessi elementi (ad es. Valori di un database rispetto a un elenco di configurazione), questo è il modo più semplice che ho trovato. Se l'ordine conta (es. Una stringa), allora useresti SequenceEqual.
Armando,

11

Per i test unitari, è possibile utilizzare CollectionAssert.AreEqualinvece di Assert.AreEqual.

È probabilmente il modo più semplice.


11

Se desideri gestire gli nullinput con garbo e ignorare l'ordine degli articoli, prova la seguente soluzione:

static class Extensions
{
    public static bool ItemsEqual<TSource>(this TSource[] array1, TSource[] array2)
    {
        if (array1 == null && array2 == null)
            return true;
        if (array1 == null || array2 == null)
            return false;
        return array1.Count() == array2.Count() && !array1.Except(array2).Any();
    }
}

Il codice di test è simile a:

class Program
{
    static void Main(string[] args)
    {
        int[] a1 = new int[] { 1, 2, 3 };
        int[] a2 = new int[] { 3, 2, 1 };
        int[] a3 = new int[] { 1, 3 };
        int[] a4 = null;
        int[] a5 = null;
        int[] a6 = new int[0];

        Console.WriteLine(a1.ItemsEqual(a2)); // Output: True.
        Console.WriteLine(a2.ItemsEqual(a3)); // Output: False.
        Console.WriteLine(a4.ItemsEqual(a5)); // Output: True. No Exception.
        Console.WriteLine(a4.ItemsEqual(a3)); // Output: False. No Exception.
        Console.WriteLine(a5.ItemsEqual(a6)); // Output: False. No Exception.
    }
}

Questo è stato utile per me, ma se a1 = { 1, 1 }e a2 = { 1, 2 }, quindi il primo test restituisce il risultato sbagliato. La dichiarazione di ritorno dovrebbe esserereturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant

2

Per alcune applicazioni potrebbe essere migliore:

string.Join(",", arr1) == string.Join(",", arr2)

2

Questa soluzione LINQ funziona, non sono sicuro di come si confronta in termini di prestazioni con SequenceEquals. Ma gestisce diverse lunghezze di array e .All uscirà sul primo elemento che non è uguale senza iterare l'intero array.

private static bool arraysEqual<T>(IList<T> arr1, IList<T> arr2)
        =>
            ReferenceEquals(arr1, arr2) || (
                arr1 != null && arr2 != null &&
                arr1.Count == arr2.Count &&
                arr1.Select((a, i) => arr2[i].Equals(a)).All(i => i)
            );

1

confrontare elementally? che dire

public void Linq78a()
{
 int[] numbers1 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 bool bb = numbers.Zip(numbers1, (a, b) => (a == b)).Any(p => !p);
 if (!bb) Console.WriteLine("Lists are equal (bb)");
   else Console.WriteLine("Lists are not equal (bb)");
}

Sostituisci la condizione (a == b) con qualsiasi cosa desideri confrontare in aeb.

(questo combina due esempi dagli esempi Linq dello sviluppatore MSDN )


1
Non gestisce matrici di lunghezze diverse (può produrre in modo errato true) e nullmatrici (va in crash).
Frédéric,

1

L'ho fatto in studi visivi e ha funzionato perfettamente; confrontando le matrici indice per indice con breve questo codice.

private void compareButton_Click(object sender, EventArgs e)
        {
            int[] answer = { 1, 3, 4, 6, 8, 9, 5, 4, 0, 6 };
            int[] exam = { 1, 2, 3, 6, 8, 9, 5, 4, 0, 7 };

            int correctAnswers = 0;
            int wrongAnswers = 0;

            for (int index = 0; index < answer.Length; index++)
            {
                if (answer[index] == exam[index])
                {
                    correctAnswers += 1;
                }
                else
                {
                    wrongAnswers += 1;
                }
            }

            outputLabel.Text = ("The matching numbers are " + correctAnswers +
                "\n" + "The non matching numbers are " + wrongAnswers);
        }

l'output sarà; I numeri corrispondenti sono 7 I numeri non corrispondenti sono 3


2
Non gestisce matrici di diverse lunghezze (si arresta in modo anomalo), nullmatrici (si arresta in modo anomalo) e fa qualcos'altro rispetto a quanto richiesto dall'OP. Ha solo chiesto di conoscere l'uguaglianza, senza contare quanti elementi differiscono o corrispondono.
Frédéric,

0

Supponendo che l'uguaglianza dell'array significhi che entrambi gli array hanno elementi uguali a indici uguali, c'è la SequenceEqualrisposta e la IStructuralEquatablerisposta .

Ma entrambi hanno degli svantaggi, per quanto riguarda le prestazioni.

SequenceEqual l'implementazione corrente non farà scorciatoie quando le matrici hanno lunghezze diverse e quindi può elencarne una interamente, confrontando ciascuno dei suoi elementi.

IStructuralEquatablenon è generico e può causare un inscatolamento di ciascun valore confrontato. Inoltre, non è molto semplice da usare e richiede già la codifica di alcuni metodi di supporto che lo nascondono.

Potrebbe essere meglio, per quanto riguarda le prestazioni, usare qualcosa come:

bool ArrayEquals<T>(T[] first, T[] second)
{
    if (first == second)
        return true;
    if (first == null || second == null)
        return false;
    if (first.Length != second.Length)
        return false;
    for (var i = 0; i < first.Length; i++)
    {
        if (first[i] != second[i])
            return false;
    }
    return true;
}

Ma ovviamente, questo non è nemmeno un "modo magico" di controllare l'uguaglianza dell'array.

Quindi attualmente no, non esiste un equivalente a Java Arrays.equals()in .Net.


Il primo e il secondo, essendo entrambi nulli, non sarebbero veri? null == null, no?
Jesse Williams,

1
Il primo test tornerà vero se entrambi lo sono null. Qual è il tuo punto?
Frédéric,

1
if (first [i]! = second [i]) non funzionerà con generics. Devi usare if (! First [i] .Equals (second [i])).
Jack Griffin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.