c# - son - Compara dos listas para las diferencias
comparar elementos de una lista c# (5)
.... pero ¿cómo encontramos la clase equivalente en la segunda lista para pasar al siguiente método;
Este es su problema real; debe tener al menos una propiedad inmutable, una identificación o algo así, para identificar los objetos correspondientes en ambas listas. Si no tiene esa propiedad, no puede resolver el problema sin errores. Puede tratar de adivinar los objetos correspondientes buscando cambios mínimos o lógicos.
Si tiene esa propiedad, la solución se vuelve realmente simple.
Enumerable.Join(
listA, listB,
a => a.Id, b => b.Id,
(a, b) => CompareTwoClass_ReturnDifferences(a, b))
gracias a ustedes, Danbruc y Noldorin por sus comentarios. ambas Listas tendrán la misma longitud y en el mismo orden. por lo que el método anterior está cerca, pero ¿puedes modificar este método para pasar la enumeración actual del método que publiqué anteriormente?
Ahora estoy confundido ... ¿cuál es el problema con eso? ¿Por qué no solo lo siguiente?
for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}
La llamada Math.Min () incluso puede omitirse si se garantiza la misma longitud.
La implementación de Noldorin es, por supuesto, más inteligente debido al delegado y el uso de enumeradores en lugar de usar ICollection.
Me gustaría recibir algunos comentarios sobre cómo podemos escribir mejor una función genérica que permita comparar dos listas. Las listas contienen objetos de clase y nos gustaría iterar a través de una lista, buscando el mismo elemento en una segunda lista e informar cualquier diferencia.
Ya tenemos un método para comparar clases, por lo que necesitamos comentarios sobre cómo podemos alimentar el método (que se muestra a continuación) de dos listas.
Por ejemplo, supongamos que tenemos una clase simple de "Empleado" que tiene tres propiedades, Nombre, ID, Departamento. Queremos informar las diferencias entre List y otra lista.
Nota:
Ambas listas contendrán siempre la misma cantidad de elementos.
Como mencionamos anteriormente, tenemos un método genérico que usamos para comparar dos clases, ¿cómo podemos incorporar este método para atender listas, es decir, desde otro método, recorrer la Lista y alimentar las clases al método genérico ... pero ¿Cómo encontramos la clase equivalente en la segunda lista para pasar al siguiente método;
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;
}
¿Alguna sugerencia o idea apreciada?
Editar: Targeting .NET 2.0 por lo que LINQ está fuera de cuestión.
Creo que estás buscando un método como este:
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);
}
}
No está probado, pero debería hacer el trabajo, no obstante. Tenga en cuenta que lo que es particularmente útil acerca de este método es que es completamente genérico, es decir, puede tomar dos secuencias de tipos arbitrarios (y diferentes) y devolver objetos de cualquier tipo.
Por supuesto, esta solución asume que quiere comparar el enésimo ítem de seq1
con el enésimo ítem en seq2
. Si quiere hacer coincidir los elementos en las dos secuencias en función de una propiedad / comparación particular, entonces querrá realizar algún tipo de operación de join (como lo sugiere danbruc usando Enumerable.Join
. No dude en decirme si estos enfoques son todo lo que busco y tal vez pueda sugerir algo más.
Editar: Aquí hay un ejemplo de cómo puede usar el método CompareSequences
con la función del comparador que publicó originalmente.
// 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());
}
Espero que entienda su pregunta correctamente, pero puede hacerlo rápidamente con Linq. Supongo que, universalmente, siempre tendrá una propiedad Id. Solo crea una interfaz para asegurar esto.
Si la forma en que identifica un objeto para que sea el mismo cambio de clase a clase, recomendaría pasar un delegado que devuelva verdadero si los dos objetos tienen la misma identificación persistente.
Aquí es cómo hacerlo en 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);
}
Esta solución produce una lista de resultados, que contiene todas las diferencias de ambas listas de entrada. Puede comparar sus objetos por cualquier propiedad, en mi ejemplo es ID. La única restricción es que las listas deben ser del mismo tipo:
var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
.Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));
Este enfoque de Microsoft funciona muy bien y ofrece la opción de comparar una lista con otra y cambiarlas para obtener la diferencia en cada una. Si está comparando clases, simplemente agregue sus objetos a dos listas separadas y luego ejecute la comparación.