Domanda Confronta due elenchi per le differenze


Vorrei ricevere un feedback su come possiamo scrivere al meglio una funzione generica che consentirà la comparazione di due elenchi. Gli elenchi contengono oggetti di classe e vorremmo iterare attraverso un elenco, cercando lo stesso elemento in un secondo elenco e riportare eventuali differenze.

Abbiamo già un metodo per confrontare le classi, quindi abbiamo bisogno di un feedback su come possiamo alimentare il metodo (mostrato sotto) da due elenchi.

Ad esempio, diciamo che abbiamo una semplice classe "Employee" che ha tre proprietà, Name, ID, Department. Vogliamo segnalare le differenze tra Elenco e un altro Elenco.

Nota:
Entrambi gli elenchi contengono sempre lo stesso numero di elementi.

Come accennato sopra, abbiamo un metodo generico che usiamo per confrontare due classi, come possiamo incorporare questo metodo per soddisfare gli elenchi, cioè da un altro metodo, scorrere l'elenco e alimentare le classi con il metodo generico .... ma come troviamo la classe equivalente nella seconda lista per passare al metodo seguente;

public static string CompareTwoClass_ReturnDifferences<T1, T2>(T1 Orig, T2 Dest)
    where T1 : class
    where T2 : class
{
    // Instantiate if necessary
    if (Dest == null) throw new ArgumentNullException("Dest", "Destination class must first be instantiated.");

    var Differences = CoreFormat.StringNoCharacters;

    // Loop through each property in the destination  
    foreach (var DestProp in Dest.GetType().GetProperties())
    {
        // Find the matching property in the Orig class and compare
        foreach (var OrigProp in Orig.GetType().GetProperties())
        {

            if (OrigProp.Name != DestProp.Name || OrigProp.PropertyType != DestProp.PropertyType) continue;
            if (OrigProp.GetValue(Orig, null).ToString() != DestProp.GetValue(Dest, null).ToString())
                Differences = Differences == CoreFormat.StringNoCharacters 
                    ? string.Format("{0}: {1} -> {2}", OrigProp.Name,
                                                       OrigProp.GetValue(Orig, null),
                                                       DestProp.GetValue(Dest, null)) 
                    : string.Format("{0} {1}{2}: {3} -> {4}", Differences,
                                                              Environment.NewLine,
                                                              OrigProp.Name,
                                                              OrigProp.GetValue(Orig, null),
                                                              DestProp.GetValue(Dest, null));
        }
    }
    return Differences;
}

Qualche suggerimento o idea apprezzata?

Modifica: Targeting .NET 2.0 in modo che LINQ sia fuori questione.


44
2018-03-24 00:10


origine


risposte:


.... ma come troviamo la classe equivalente nel secondo Elenco da passare al metodo   sotto;

Questo è il tuo vero problema; devi avere almeno una proprietà immutabile, un id o qualcosa del genere, per identificare gli oggetti corrispondenti in entrambi gli elenchi. Se non possiedi una proprietà del genere, non puoi risolvere il problema senza errori. Puoi semplicemente provare a indovinare gli oggetti corrispondenti cercando le modifiche minime o logiche.

Se hai una tale proprietà, la soluzione diventa davvero semplice.

Enumerable.Join(
   listA, listB,
   a => a.Id, b => b.Id,
   (a, b) => CompareTwoClass_ReturnDifferences(a, b))

grazie a te sia danbruc che Noldorin per il tuo feedback. entrambe le liste saranno uguali   lunghezza e nello stesso ordine. quindi il metodo sopra è vicino, ma puoi modificarlo   metodo per passare l'enum. Corrente al metodo che ho postato sopra?

Ora sono confuso ... qual è il problema? Perché non solo il seguente?

for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
    yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}

La chiamata Math.Min () può anche essere omessa se viene garantita una lunghezza uguale.


L'implementazione di Noldorin è ovviamente più intelligente a causa del delegato e dell'uso di enumeratori invece di usare ICollection.


15
2018-03-24 00:23



Questa soluzione produce un elenco di risultati, che contiene tutte le differenze da entrambi gli elenchi di input. Puoi confrontare i tuoi oggetti con qualsiasi proprietà, nel mio esempio è ID. L'unica limitazione è che gli elenchi devono essere dello stesso tipo:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));

72
2018-05-09 11:09



Penso che tu stia cercando un metodo come questo:

public static IEnumerable<TResult> CompareSequences<T1, T2, TResult>(IEnumerable<T1> seq1,
    IEnumerable<T2> seq2, Func<T1, T2, TResult> comparer)
{
    var enum1 = seq1.GetEnumerator();
    var enum2 = seq2.GetEnumerator();

    while (enum1.MoveNext() && enum2.MoveNext())
    {
        yield return comparer(enum1.Current, enum2.Current);
    }
}

Non è stato testato, ma dovrebbe comunque funzionare. Si noti che ciò che è particolarmente utile in questo metodo è che è completamente generico, cioè può prendere due sequenze di tipi arbitrari (e diversi) e restituire oggetti di qualsiasi tipo.

Questa soluzione presuppone ovviamente che si desideri confrontare l'ennesimo elemento di seq1 con l'ennesimo elemento in seq2. Se vuoi fare corrispondere gli elementi nelle due sequenze in base a una particolare proprietà / confronto, allora ti consigliamo di eseguire una sorta di aderire operazione (come suggerito da danbruc usando Enumerable.Join. Fammi sapere se nessuno di questi approcci è proprio quello che sto cercando e forse posso suggerire qualcos'altro.

Modificare: Ecco un esempio di come potresti usare il CompareSequences metodo con la funzione di confronto che hai originariamente pubblicato.

// Prints out to the console all the results returned by the comparer function (CompareTwoClass_ReturnDifferences in this case).
var results = CompareSequences(list1, list2, CompareTwoClass_ReturnDifferences);
int index;    

foreach(var element in results)
{
    Console.WriteLine("{0:#000} {1}", index++, element.ToString());
}

6
2018-03-24 00:30



Questo approccio di Microsoft funziona molto bene e offre la possibilità di confrontare una lista con un'altra e di cambiarle per ottenere la differenza in ciascuna. Se si confrontano le classi, aggiungere semplicemente gli oggetti a due elenchi separati e quindi eseguire il confronto.

http://msdn.microsoft.com/en-us/library/bb397894.aspx


2
2018-06-18 01:55



Spero di aver risposto correttamente alla tua domanda, ma puoi farlo molto velocemente con Linq. Presumo che universalmente avrai sempre una proprietà di identificazione. Basta creare un'interfaccia per garantire questo.

Se il modo in cui identifichi un oggetto per essere lo stesso cambia da classe a classe, ti consiglio di passare un delegato che restituisce true se i due oggetti hanno lo stesso ID persistente.

Ecco come farlo in Linq:

List<Employee> listA = new List<Employee>();
        List<Employee> listB = new List<Employee>();

        listA.Add(new Employee() { Id = 1, Name = "Bill" });
        listA.Add(new Employee() { Id = 2, Name = "Ted" });

        listB.Add(new Employee() { Id = 1, Name = "Bill Sr." });
        listB.Add(new Employee() { Id = 3, Name = "Jim" });

        var identicalQuery = from employeeA in listA
                             join employeeB in listB on employeeA.Id equals employeeB.Id
                             select new { EmployeeA = employeeA, EmployeeB = employeeB };

        foreach (var queryResult in identicalQuery)
        {
            Console.WriteLine(queryResult.EmployeeA.Name);
            Console.WriteLine(queryResult.EmployeeB.Name);
        }

1
2018-03-24 00:45