Le moyen le plus simple de comparer des tableaux en C #


196

En Java, Arrays.equals()permet de comparer facilement le contenu de deux tableaux de base (des surcharges sont disponibles pour tous les types de base).

Existe-t-il une telle chose en C #? Existe-t-il un moyen "magique" de comparer le contenu de deux tableaux en C #?


2
Ajout de «.net» aux balises car cette technique pourrait être utilisée dans d'autres langages similaires basés sur .net.
Evan Plaice

4
À tous ceux qui liront ceci, gardez à l'esprit que la réponse acceptée utilise SequenceEqual. SequenceEqual vérifie non seulement s'ils contiennent les mêmes données, mais aussi s'ils contiennent les mêmes données dans le même ordre
John Demetriou

Réponses:


281

Vous pourriez utiliser Enumerable.SequenceEqual. Cela fonctionne pour tous IEnumerable<T>, pas seulement pour les tableaux.


3
Cela ne fonctionne que s'ils sont dans le même ordre
John Demetriou

2
SequenceEqualpeut ne pas être un bon choix en termes de performances, car son implémentation actuelle peut énumérer complètement l'une de ses sources si elles ne diffèrent que par la longueur. Avec les tableaux, nous pourrions d'abord vérifier l' Lengthégalité, afin d'éviter d'énumérer des tableaux de différentes longueurs juste pour finir par céder false.
Frédéric

80

Utiliser Enumerable.SequenceEqualdans 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
Gardez à l'esprit que cela génère des arguments nuls, alors assurez-vous de ne pas supposer quenew int[] {1}.SequenceEquals(null) == false
sara

31

Pour les tableaux (et les tuples), vous pouvez également utiliser de nouvelles interfaces de .NET 4.0: IStructuralComparable et IStructuralEquatable . En les utilisant, vous pouvez non seulement vérifier l'égalité des tableaux, mais aussi les comparer.

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
}

1
Pardonnez-moi, est-ce que ça devrait être un 1 ou un 0 a.StructuralCompare(b)?
mafu

1
Sur les tableaux de type grande valeur, l'utilisation de ceux-ci a un impact négatif, car leur implémentation actuelle encadrera chaque valeur à comparer.
Frédéric le

18

Pour .NET 4.0 et supérieur, vous pouvez comparer des éléments dans un tableau ou des tuples en utilisant le type 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)); 

Edit: Parlé trop tôt. Puis-je faire le StructualEqualityCompare avec IStructuralComparable? Je veux appeler CompareTo avec deux tableaux d'objets pour savoir lequel vient «en premier». J'ai essayé IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Obtenir: impossible de convertir de 'System.Collections.IEqualityComparer' à 'System.Collections.IComparer'
shindigo

1
OK - l'appel correct est: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
shindigo

17

SequenceEqual ne retournera vrai que si deux conditions ou remplies.

  1. Ils contiennent les mêmes éléments.
  2. Les éléments sont dans le même ordre.

Si vous voulez seulement vérifier s'ils contiennent les mêmes éléments quel que soit leur ordre et que votre problème est du type

Valeurs2 contient-il toutes les valeurs contenues dans Valeurs1?

vous pouvez utiliser la méthode d'extension LINQ Enumerable.Except, puis vérifier si le résultat a une valeur. Voici un exemple

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
}

Et aussi en utilisant cela, vous obtenez automatiquement les différents éléments. Deux oiseaux avec une pierre.

Gardez à l'esprit que si vous exécutez votre code de cette manière

var result = values2.Except(values1);

vous obtiendrez des résultats différents.

Dans mon cas, j'ai une copie locale d'un tableau et je veux vérifier si quelque chose a été supprimé du tableau d'origine, j'utilise donc cette méthode.


2
Les tableaux contenant les mêmes valeurs dans un ordre différent ne sont tout simplement PAS ÉGAUX. Avez-vous un truc 'Demetriou' == 'uoirtemeD'?
edc65

1
Ça dépend. Si vous utilisez les tableaux comme des collections non ordonnées et que vous avez seulement besoin de vérifier qu'ils contiennent les mêmes éléments (par exemple, les valeurs d'une base de données par rapport à une liste de configuration), alors c'est le moyen le plus simple que j'ai trouvé. Si l'ordre est important (par exemple, une chaîne), vous utiliserez SequenceEqual.
Armando

12

Si vous souhaitez gérer les nullentrées correctement et ignorer l'ordre des éléments, essayez la solution suivante:

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;
        if (array1.Count() != array2.Count())
            return false;
        return !array1.Except(array2).Any() && !array2.Except(array1).Any();
    }
}

Le code de test ressemble à:

public static void Main()
{
    int[] a1 = new int[] { 1, 2, 3 };
    int[] a2 = new int[] { 3, 2, 1 };
    int[] a3 = new int[] { 1, 3 };
    Console.WriteLine(a1.ItemsEqual(a2)); // Output: True.
    Console.WriteLine(a2.ItemsEqual(a3)); // Output: False.
    Console.WriteLine(a3.ItemsEqual(a2)); // Output: False.
   
    int[] a4 = new int[] { 1, 1 };
    int[] a5 = new int[] { 1, 2 };
    Console.WriteLine(a4.ItemsEqual(a5)); // Output: False 
    Console.WriteLine(a5.ItemsEqual(a4)); // Output: False 
    
    int[] a6 = null;
    int[] a7 = null;
    int[] a8 = new int[0];

    Console.WriteLine(a6.ItemsEqual(a7)); // Output: True. No Exception.
    Console.WriteLine(a8.ItemsEqual(a6)); // Output: False. No Exception.
    Console.WriteLine(a7.ItemsEqual(a8)); // Output: False. No Exception.
}

2
Cela m'a été utile, mais si a1 = { 1, 1 }et a2 = { 1, 2 }, alors le premier test renvoie le mauvais résultat. La déclaration de retour devrait êtrereturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant

Merci. Correction du bug
Harry He

11

Pour les tests unitaires, vous pouvez utiliser à la CollectionAssert.AreEqualplace de Assert.AreEqual.

C'est probablement le moyen le plus simple.


3

Pour certaines applications, cela peut être mieux:

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

C'est une solution très pratique pour de nombreux tests unitaires
Mohammad Nikravan

2

Cette solution LINQ fonctionne, je ne sais pas comment elle se compare en termes de performances à SequenceEquals. Mais il gère différentes longueurs de tableau et le .All se fermera sur le premier élément qui n'est pas égal sans itérer dans tout le tableau.

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

comparer par élément? qu'en est-il de

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

Remplacez la condition (a == b) par tout ce que vous souhaitez comparer en a et b.

(ceci combine deux exemples des exemples Linq du développeur MSDN )


1
Il ne gère pas les tableaux de différentes longueurs (peut mal céder true) et les nulltableaux (se bloque).
Frédéric le

1

Je l'ai fait dans des studios visuels et cela a parfaitement fonctionné; comparaison des tableaux index par index avec ce code court.

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

la sortie sera; Les numéros correspondants sont 7 Les numéros non correspondants sont 3


2
Il ne gère pas les tableaux de différentes longueurs (se plantera), les nulltableaux (se plantera aussi), et il fait autre chose que ce que l'OP a demandé. Il a seulement demandé à connaître l'égalité, sans compter combien d'éléments diffèrent ou correspondent.
Frédéric le

1

En supposant que l'égalité des tableaux signifie que les deux tableaux ont des éléments égaux à des index égaux, il y a la SequenceEqualréponse et la IStructuralEquatableréponse .

Mais les deux ont des inconvénients, en termes de performances.

SequenceEqual l'implémentation actuelle ne raccourcira pas lorsque les tableaux ont des longueurs différentes, et peut donc en énumérer un entièrement, en comparant chacun de ses éléments.

IStructuralEquatablen'est pas générique et peut provoquer un encadrement de chaque valeur comparée. De plus, il n'est pas très simple à utiliser et nécessite déjà de coder des méthodes d'aide pour le cacher.

Il peut être préférable, en termes de performances, d'utiliser quelque chose comme:

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].Equals(second[i]))
            return false;
    }
    return true;
}

Mais bien sûr, ce n'est pas non plus une "manière magique" de vérifier l'égalité des tableaux.

Donc actuellement, non, il n'y a pas vraiment d'équivalent à Java Arrays.equals()en .Net.


1

Vous pouvez utiliser Enumerable.Intersect:

int[] array1 = new int[] { 1, 2, 3, 4,5 },
      array2 = new int[] {7,8};

if (array1.Intersect(array2).Any())
    Console.WriteLine("matched");
else
    Console.WriteLine("not matched");

Je ne pense pas que ce soit ce que demande OP. Comment cela compare-t-il les tableaux? Il identifie uniquement si les tableaux ont un élément commun. Ce n'est guère un équivalent de Java Arrays.equals().
Martin Prikryl le

-1
        int[] a = { 2, 1, 3, 4, 5, 2 };

        int[] b = { 2, 1, 3, 4, 5, 2 };

        bool ans = true;

        if(a.Length != b.Length)
        {
            ans = false;
        }
        else
        {
            for (int i = 0; i < a.Length; i++)
            {
                if( a[i] != b[i])
                {
                    ans = false;
                }
            }
        }

        string str = "";

        if(ans == true)
        {
            str = "Two Arrays are Equal";
        }

        if (ans == false)
        {
            str = "Two Arrays are not Equal";
        }

       //--------------Or You can write One line of Code-------------

        var ArrayEquals = a.SequenceEqual(b);   // returns true

C'est assez sous-optimal. Vous devez arrêter la comparaison sur le premier élément non correspondant. + Cette réponse ne montre rien qui ne soit déjà couvert dans les réponses existantes.
Martin Prikryl
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.