A maneira mais fácil de comparar matrizes em c #

180

Em Java, Arrays.equals()permite comparar facilmente o conteúdo de duas matrizes básicas (sobrecargas estão disponíveis para todos os tipos básicos).

Existe uma coisa dessas em c #? Existe alguma maneira "mágica" de comparar o conteúdo de duas matrizes em C #?

asmo
fonte
1
Adicionado '.net' às tags, porque essa técnica pode ser usada em outros idiomas similares baseados em .net.
Evan Plaice
3
Para todos que estão lendo isso, lembre-se de que a resposta aceita está usando SequenceEqual. SequenceEqual não apenas verifica se eles contêm os mesmos dados, mas também se eles contêm os mesmos dados na mesma ordem
John Demetriou

Respostas:

262

Você poderia usar Enumerable.SequenceEqual. Isso funciona para qualquer um IEnumerable<T>, não apenas para matrizes.

Quartermeister
fonte
Isso só funciona se eles estão na mesma ordem embora
John Demetriou
1
SequenceEqualpode não ser uma boa escolha em termos de desempenho, porque sua implementação atual pode enumerar completamente uma de suas fontes se elas diferirem apenas no comprimento. Com matrizes, poderíamos verificar a Lengthigualdade primeiro, a fim de evitar enumerar matrizes de diferentes comprimentos apenas para acabar produzindo false.
Frédéric
72

Use Enumerable.SequenceEqualno 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
fonte
1
Tenha em mente que este joga para argumentos nulos, por isso certifique-se de não assumir quenew int[] {1}.SequenceEquals(null) == false
sara
30

Também para matrizes (e tuplas), você pode usar novas interfaces do .NET 4.0: IStructuralComparable e IStructuralEquatable . Usando-os, você pode não apenas verificar a igualdade de matrizes, mas também compará-las.

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
fonte
Perdoe-me, deveria ser 1 ou 0 a.StructuralCompare(b)?
mafu 01/01
Em matrizes de tipo de valor grande, há um impacto no desempenho ao usá-las, porque a implementação atual colocará cada valor em caixa para comparação.
Frédéric
18

Para o .NET 4.0 e superior, você pode comparar elementos na matriz ou nas tuplas usando o 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
fonte
Edit: falou muito cedo. Posso fazer o StructualEqualityCompare com IStructuralComparable? Quero chamar CompareTo com duas matrizes de objetos para descobrir qual vem "primeiro". Eu tentei IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralEqualityComparer)); Obtendo: não é possível converter de 'System.Collections.IEqualityComparer' para 'System.Collections.IComparer'
shindigo 21/03/16
1
OK - a chamada correta é: IStructuralComparable se1 = a1; Console.WriteLine (se1.CompareTo (a2, StructuralComparisons.StructuralComparer));
Shindigo # 22/16
15

SequenceEqual retornará verdadeiro somente se duas condições ou forem atendidas.

  1. Eles contêm os mesmos elementos.
  2. Os elementos estão na mesma ordem.

Se você deseja apenas verificar se eles contêm os mesmos elementos, independentemente da ordem deles, e seu problema é do tipo

Values2 contém todos os valores contidos em values1?

você pode usar o método de extensão LINQ Enumerable.Excepte verificar se o resultado tem algum valor. Aqui está um exemplo

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 também usando isso, você obtém os diferentes itens também automaticamente. Dois pássaros com uma pedra.

Lembre-se, se você executar seu código assim

var result = values2.Except(values1);

você obterá resultados diferentes.

No meu caso, tenho uma cópia local de uma matriz e quero verificar se alguma coisa foi removida da matriz original, portanto, uso esse método.

John Demetriou
fonte
2
Matrizes contendo os mesmos valores em ordem diferente, simplesmente NÃO são iguais. Você acha 'Demetriou' == 'uoirtemeD'?
Edc65 03/02/19
Depende. Se você estiver usando as matrizes como coleções não ordenadas e precisar verificar apenas se elas contêm os mesmos elementos (por exemplo, valores de um banco de dados em relação a uma lista de configuração), essa é a maneira mais fácil que encontrei. Se a ordem importa (por exemplo, uma string), você usaria SequenceEqual.
Armando
11

Para testes de unidade, você pode usar em CollectionAssert.AreEqualvez de Assert.AreEqual.

É provavelmente a maneira mais fácil.

Paris Qian Sen
fonte
11

Se você deseja manipular as nullentradas normalmente e ignorar a ordem dos itens, tente a seguinte solução:

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

O código de teste é semelhante 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.
    }
}
Harry He
fonte
Isso foi útil para mim, mas se a1 = { 1, 1 }e a2 = { 1, 2 }, então o primeiro teste retornará o resultado errado. A declaração de retorno deve serreturn array1.Count() == array2.Count() && !array1.Except(array2).Any() && !array2.Except(array1).Any();
Polshgiant
2

Para algumas aplicações, pode ser melhor:

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

Esta solução LINQ funciona, não sei como ela se compara no desempenho com SequenceEquals. Mas ele lida com diferentes comprimentos de matriz e o .All sai no primeiro item que não é igual sem iterar por toda a 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
fonte
1

elementwise comparar? A respeito

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

Substitua a condição (a == b) por qualquer coisa que você queira comparar em a e b.

(isso combina dois exemplos de exemplos de Linq do desenvolvedor do MSDN )

Guloseimas
fonte
1
Ele não lida com matrizes de comprimentos diferentes (podem produzir incorretamente true) e nullmatrizes (falharão).
Frédéric
1

Eu fiz isso em estúdios visuais e funcionou perfeitamente; comparando matrizes índice por índice com um código curto.

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

a saída será; Os números correspondentes são 7 Os números não correspondentes são 3

lucy
fonte
2
Ele não lida com matrizes de comprimentos diferentes (trava), nullmatrizes (trava também) e faz outra coisa além do que o OP pediu. Ele apenas pediu para conhecer a igualdade, sem contar quantos itens diferem ou correspondem.
Frédéric
0

Assumindo igualdade de matriz significa que ambas as matrizes têm elementos iguais em índices iguais, existe a SequenceEqualresposta e a IStructuralEquatableresposta .

Mas ambos têm desvantagens, em termos de desempenho.

SequenceEqual a implementação atual não será um atalho quando as matrizes tiverem comprimentos diferentes e, portanto, poderá enumerar uma delas completamente, comparando cada um de seus elementos.

IStructuralEquatablenão é genérico e pode causar o boxe de cada valor comparado. Além disso, não é muito simples de usar e já exige a codificação de alguns métodos auxiliares que o ocultam.

Pode ser melhor, em termos de desempenho, 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;
}

Mas é claro que essa também não é uma "maneira mágica" de verificar a igualdade de matrizes.

Portanto, atualmente, não, não existe realmente um equivalente ao Java Arrays.equals()no .Net.

Frédéric
fonte
O primeiro e o segundo valores nulos não seriam verdadeiros? null == null, não é?
Jesse Williams
1
O primeiro teste retornará verdadeiro se ambos forem null. Onde vc quer chegar?
Frédéric
1
se (primeiro [i]! = segundo [i]) não funcionará com genéricos. Você deve usar if (! First [i] .Equals (second [i])).
Jack Griffin