Сравните два списка различий
Мне хотелось бы получить некоторые отзывы о том, как лучше всего написать общую функцию, которая позволит сравнивать два списка. Списки содержат объекты класса, и мы хотели бы перебирать один список, ища тот же элемент во втором списке и сообщать о любых различиях.
У нас уже есть метод для сравнения классов, поэтому нам нужна обратная связь о том, как мы можем подать метод (показано ниже) из двух списков.
Например, скажем, у нас есть простой класс "Сотрудник", который имеет три свойства: имя, идентификатор, отдел. Мы хотим сообщить о различиях между списком и другим списком.
Примечание:
Оба списка всегда будут содержать одинаковое количество элементов.
Как уже упоминалось выше, у нас есть общий метод, который мы используем для сравнения двух классов, как мы можем включить этот метод для обслуживания списков, т.е. из другого метода, перебирать список и комбинировать классы с общим методом... но как мы можем найти эквивалентный класс во втором списке, чтобы перейти к следующему методу:
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;
}
Любые предложения или идеи оценены?
Изменить: Ориентация .NET 2.0, поэтому LINQ не может быть и речи.
Ответы
Ответ 1
.... но как найти эквивалентный класс во втором списке, чтобы перейти к методу ниже;
Это ваша фактическая проблема; вы должны иметь по крайней мере одно неизменяемое свойство, идентификатор или что-то в этом роде, чтобы идентифицировать соответствующие объекты в обоих списках. Если у вас нет такого имущества, вы не можете решить проблему без ошибок. Вы можете просто попытаться угадать соответствующие объекты, выполнив минимальные или логические изменения.
Если у вас есть такое свойство, решение становится очень простым.
Enumerable.Join(
listA, listB,
a => a.Id, b => b.Id,
(a, b) => CompareTwoClass_ReturnDifferences(a, b))
благодаря вам и danbruc и Noldorin за ваши отзывы. оба списка будут одинаковыми длины и в том же порядке. поэтому метод выше близок, но вы можете изменить это метод для передачи enum.Current к методу, указанному выше?
Теперь я смущен... в чем проблема? Почему не только следующее?
for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}
Вызов Math.Min() может даже быть опущен, если гарантирована равная длина.
Реализация Noldorin, конечно, умнее из-за делегирования и использования счетчиков вместо использования ICollection.
Ответ 2
Это решение создает список результатов, содержащий все отличия от обоих входных списков.
Вы можете сравнить свои объекты по любому свойству, в моем примере это идентификатор.
Единственное ограничение состоит в том, что списки должны быть одного типа:
var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
.Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));
Ответ 3
Я думаю, вы ищете такой метод:
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);
}
}
Это не проверено, но тем не менее должно выполнять эту работу. Обратите внимание, что особенно полезным в этом методе является то, что он является полным, т.е. Он может принимать две последовательности произвольных (и разных) типов и возвращать объекты любого типа.
Это решение, конечно, предполагает, что вы хотите сравнить n-й элемент seq1
с n-м элементом в seq2
. Если вы хотите соответствовать элементам в двух последовательностях, основанным на определенном свойстве/сравнении, тогда вы захотите выполнить какой-то join (как было предложено danbruc с помощью Enumerable.Join
. Сообщите мне, не является ли это ни одним из этих подходов, что я потом, и, возможно, я могу предложить что-то еще.
Edit:
Вот пример того, как вы можете использовать метод CompareSequences
с функцией сопоставления, которую вы изначально разместили.
// 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());
}
Ответ 4
Этот подход от Microsoft работает очень хорошо и предоставляет возможность сравнить один список с другим и переключить их, чтобы получить разницу в каждом. Если вы сравниваете классы, просто добавьте свои объекты в два отдельных списка и затем выполните сравнение.
http://msdn.microsoft.com/en-us/library/bb397894.aspx
Ответ 5
Я надеюсь, что правильно понимаю ваш вопрос, но вы можете сделать это очень быстро с Linq. Я предполагаю, что универсально вы всегда будете иметь свойство Id. Просто создайте интерфейс, чтобы обеспечить это.
Если вы определяете, что объект имеет одинаковые изменения от класса к классу, я бы рекомендовал передать делегат, который возвращает true, если два объекта имеют одинаковый постоянный идентификатор.
Вот как это сделать в 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);
}