La forma más fácil de comparar matrices en C #

180

En Java, Arrays.equals()permite comparar fácilmente el contenido de dos matrices básicas (hay sobrecargas disponibles para todos los tipos básicos).

¿Existe tal cosa en C #? ¿Hay alguna forma "mágica" de comparar el contenido de dos matrices en C #?

asmo
fuente
1
Se agregó '.net' a las etiquetas porque esta técnica podría usarse en otros lenguajes similares basados ​​en .net.
Evan Plaice
3
Para todos los que lean esto, tenga en cuenta que la respuesta aceptada es usar SequenceEqual. SequenceEqual no solo comprueba si contienen los mismos datos, sino también si contienen los mismos datos en el mismo orden
John Demetriou

Respuestas:

262

Podrías usar Enumerable.SequenceEqual. Esto funciona para cualquier IEnumerable<T>, no solo matrices.

Quartermeister
fuente
Sin embargo, esto solo funciona si están en el mismo orden
John Demetriou,
1
SequenceEqualpuede que no sea una buena elección en cuanto al rendimiento, porque su implementación actual puede enumerar completamente una de sus fuentes si difieren solo en longitud. Con las matrices, Lengthprimero podríamos verificar la igualdad, para evitar enumerar matrices de diferentes longitudes solo para terminar rindiendo false.
Frédéric
72

Usar Enumerable.SequenceEqualen 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
John Buchanan
fuente
1
Tenga en cuenta que esto arroja argumentos nulos, así que asegúrese de no asumir esonew int[] {1}.SequenceEquals(null) == false
sara
30

También para matrices (y tuplas) puede usar nuevas interfaces de .NET 4.0: IStructuralComparable e IStructuralEquatable . Al usarlos, no solo puede verificar la igualdad de las matrices sino también compararlas.

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
}
desco
fuente
Disculpe, ¿debería ser un 1 o un 0 a.StructuralCompare(b)?
mafu
En las matrices de tipo de valor grande, hay un impacto en el rendimiento al usarlas, porque su implementación actual incluirá cada valor para comparar.
Frédéric
18

Para .NET 4.0 y superior, puede comparar elementos en una matriz o tuplas mediante el uso del 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)); 
Yuliia Ashomok
fuente
Editar: Habló demasiado pronto. ¿Puedo hacer StructualEqualityCompare with IStructuralComparable? Quiero llamar a CompareTo con dos matrices de objetos para averiguar cuál viene "primero". Intenté IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Obtención: no se puede convertir de 'System.Collections.IEqualityComparer' a 'System.Collections.IComparer'
shindigo
1
OK, la llamada correcta es: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
shindigo
15

SequenceEqual solo volverá verdadero si se cumplen dos condiciones o se cumplen.

  1. Contienen los mismos elementos.
  2. Los elementos están en el mismo orden.

Si solo desea verificar si contienen los mismos elementos independientemente de su orden y si su problema es del tipo

¿Los valores2 contienen todos los valores contenidos en los valores1?

puede usar el método de extensión LINQ Enumerable.Excepty luego verificar si el resultado tiene algún valor. Aquí hay un ejemplo

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
}

Y también al usar esto obtienes los diferentes elementos también automáticamente. Dos pájaros con una piedra.

Tenga en cuenta que si ejecuta su código así

var result = values2.Except(values1);

Obtendrás diferentes resultados.

En mi caso, tengo una copia local de una matriz y quiero verificar si se ha eliminado algo de la matriz original, así que utilizo este método.

John Demetriou
fuente
2
Las matrices que contienen los mismos valores en diferente orden, simplemente NO SON IGUALES. ¿Te parece 'Demetriou' == 'uoirtemeD'?
edc65
Eso depende. Si está utilizando las matrices como colecciones desordenadas y necesita verificar solo que contienen los mismos elementos (por ejemplo, valores de una base de datos en una lista de configuración), entonces esta es la forma más fácil que he encontrado. Si el orden es importante (por ejemplo, una cadena), entonces lo usaría SequenceEqual.
Armando hace
11

Para pruebas unitarias, puede usar en CollectionAssert.AreEquallugar de Assert.AreEqual.

Es probablemente la forma más fácil.

Paris Qian Sen
fuente
11

Si desea manejar las nullentradas con gracia e ignorar el orden de los elementos, pruebe la siguiente solución:

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

El código de prueba se ve así:

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.
    }
}
Harry He
fuente
Esto fue útil para mí, pero si a1 = { 1, 1 }y a2 = { 1, 2 }, entonces, la primera prueba devuelve el resultado incorrecto. La declaración de devolución debe serreturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant
2

Para algunas aplicaciones puede ser mejor:

string.Join(",", arr1) == string.Join(",", arr2)
alexkovelsky
fuente
2

Esta solución LINQ funciona, no estoy seguro de cómo se compara en rendimiento con SequenceEquals. Pero maneja diferentes longitudes de matriz y .All saldrá en el primer elemento que no es igual sin iterar a través de toda la matriz.

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)
            );
JoeS
fuente
1

¿Comparar elementos? qué pasa

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

Reemplace la condición (a == b) por cualquier cosa que desee comparar en ay b.

(esto combina dos ejemplos de ejemplos de Linq para desarrolladores de MSDN )

Golosinas
fuente
1
No maneja matrices de diferentes longitudes (puede producir incorrectamente true) y nullmatrices (se bloqueará).
Frédéric
1

Lo hice en estudios visuales y funcionó perfectamente; comparar matrices índice por índice con este código corto.

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 salida será; Los números coincidentes son 7 Los números no coincidentes son 3

lucy
fuente
2
No maneja matrices de diferentes longitudes (se bloqueará), nullmatrices (también se bloqueará), y hace algo más de lo que pidió el OP. Solo pidió conocer la igualdad, sin contar cuántos elementos difieren o coinciden.
Frédéric
0

Suponiendo que la igualdad de matriz significa que ambas matrices tienen elementos iguales en índices iguales, existe la SequenceEqualrespuesta y la IStructuralEquatablerespuesta .

Pero ambos tienen inconvenientes, en cuanto al rendimiento.

SequenceEqual La implementación actual no será un atajo cuando las matrices tengan diferentes longitudes, por lo que puede enumerar una de ellas por completo, comparando cada uno de sus elementos.

IStructuralEquatableno es genérico y puede causar el encajonamiento de cada valor comparado. Además, no es muy fácil de usar y ya requiere codificar algunos métodos auxiliares para ocultarlo.

Puede ser mejor, en cuanto al rendimiento, usar algo como:

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

Pero, por supuesto, esa tampoco es una "forma mágica" de verificar la igualdad de la matriz.

Entonces, actualmente, no, no hay realmente un equivalente a Java Arrays.equals()en .Net.

Frédéric
fuente
¿No serían el primero y el segundo ambos valores nulos en verdadero? nulo == nulo, ¿no es así?
Jesse Williams el
1
La primera prueba devolverá verdadero si ambos lo son null. ¿Cuál es tu punto?
Frédéric
1
if (first [i]! = second [i]) no funcionará con genéricos. Debe usar if (! First [i] .Equals (second [i])).
Jack Griffin