Porównaj dwie listy różnic

Chciałbym uzyskać kilka opinii na temat tego, jak najlepiej napisać ogólną funkcję, która umożliwi porównanie dwóch list. Listy zawierają obiekty klas i chcielibyśmy przejrzeć jedną listę, szukając tego samego elementu na drugiej liście i zgłaszać wszelkie różnice.

Mamy już metodę porównywania klas, więc potrzebujemy informacji zwrotnej na temat tego, jak możemy karmić metodę (pokazaną poniżej) z dwóch list.

Na przykład, powiedzmy, że mamy prostą klasę "pracownika", która ma trzy właściwości, Nazwisko, dowód, Wydział. Chcemy zgłosić różnice między listą a inną listą.

Uwaga:
Obie listy zawsze będą zawierały tę samą liczbę pozycji.

Jak wspomniano powyżej, mamy metodę generyczną, której używamy do porównania dwóch klas, jak możemy włączyć tę metodę, aby zaspokoić listy, tj. z innej metody, pętli przez Listę i karmić klasy do metody generycznej .... ale jak znaleźć równoważną klasę na drugiej liście, aby przejść do metody poniżej;

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

Jakieś sugestie lub pomysły docenione?

Edit: Targeting. NET 2.0 więc LINQ nie wchodzi w grę.

Author: abatishchev, 2009-03-24

5 answers

.... ale jak znaleźć równoważną klasę na drugiej liście, aby przejść do metody poniżej;

To jest Twój rzeczywisty problem; musisz mieć co najmniej jedną niezmienną właściwość, id lub coś w tym stylu, aby zidentyfikować odpowiednie obiekty na obu listach. Jeśli nie masz takiej właściwości, nie możesz rozwiązać problemu bez błędów. Możesz po prostu spróbować odgadnąć odpowiednie obiekty, wyszukując minimalne lub logiczne zmiany.

Jeśli posiadasz taką nieruchomość, rozwiązanie staje się naprawdę proste.

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

Dziękuję zarówno danbruc i Noldorin za opinie. obie listy będą takie same długość i w tej samej kolejności. tak więc powyższa metoda jest bliska, ale czy można to zmodyfikować metoda zaliczenia enum.Aktualne do metody, którą zamieściłem powyżej?

Teraz jestem zdezorientowany ... w czym problem? Dlaczego nie tylko następujące?
for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
    yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}
Matematyka.Wywołanie Min() może być nawet pominięte, jeśli jednakowa długość / align = "left" /

Implementacja Noldorina jest oczywiście mądrzejsza ze względu na delegata i użycie enumeratorów zamiast korzystania Zkolekcji.

 15
Author: Daniel Brückner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 02:01:37

To rozwiązanie tworzy listę wyników, która zawiera wszystkie różnice z obu list wejściowych. Możesz porównać swoje obiekty według dowolnej właściwości, w moim przykładzie jest to ID. Jedynym ograniczeniem jest to, że listy powinny być tego samego typu:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));
 72
Author: Artur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 11:09:40

Myślę, że szukasz takiej metody:

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);
    }
}
Nie jest testowany, ale i tak powinien zadziałać. Zauważ, że to, co jest szczególnie przydatne w tej metodzie, to to, że jest pełna generyczna, tzn. może przyjmować dwie sekwencje dowolnych (i różnych) typów i zwracać obiekty dowolnego typu.

To rozwiązanie oczywiście zakłada, że chcesz porównać n-tą pozycję seq1 Z N-tą pozycją w seq2. Jeśli chcesz dopasować elementy w dwóch sekwencjach w oparciu o następnie będziesz chciał wykonać jakąś operację join (zgodnie z sugestią danbruc przy użyciu Enumerable.Join. Daj mi znać, jeśli żadne z tych podejść nie jest dokładnie tym, czego szukam i może mogę zasugerować coś innego.

Edit: Oto przykład użycia metody CompareSequences z pierwotnie opublikowaną funkcją porównawczą.

// 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
Author: Noldorin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 01:11:37

To podejście firmy Microsoft działa bardzo dobrze i zapewnia opcję porównywania jednej listy do drugiej i przełączania ich, aby uzyskać różnicę w każdej z nich. Jeśli porównujesz klasy, po prostu Dodaj obiekty do dwóch oddzielnych list, a następnie uruchom porównanie.

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

 2
Author: user2284452,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-20 02:48:25

Mam nadzieję, że dobrze rozumiem twoje pytanie, ale możesz to zrobić bardzo szybko z Linq. Zakładam, że zawsze będziesz miał dowód osobisty. Wystarczy stworzyć interfejs, aby to zapewnić.

Jeśli w jaki sposób identyfikujesz obiekt jako taki zmienia się z klasy na klasę, polecam przekazanie delegata, który zwraca true, jeśli oba obiekty mają ten sam stały identyfikator.

Oto Jak to zrobić w 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
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 00:45:53