valor una restar otra objetos mvc listas lista con comparar buscar actualizar c# linq

c# - restar - Crea una lista a partir de dos listas de objetos con linq



lista de objetos c# (8)

¿Funciona el siguiente código para su problema? He usado un foreach con un poco de linq dentro para hacer la combinación de listas y asumí que las personas son iguales si sus nombres coinciden, y parece imprimir los valores esperados cuando se ejecutan. Resharper no ofrece ninguna sugerencia para convertir el foreach en linq, así que probablemente sea tan bueno como hacerlo de esta manera.

public class Person { public string Name { get; set; } public int Value { get; set; } public int Change { get; set; } public Person(string name, int value) { Name = name; Value = value; Change = 0; } } class Program { static void Main(string[] args) { List<Person> list1 = new List<Person> { new Person("a", 1), new Person("b", 2), new Person("c", 3), new Person("d", 4) }; List<Person> list2 = new List<Person> { new Person("a", 4), new Person("b", 5), new Person("e", 6), new Person("f", 7) }; List<Person> list3 = list2.ToList(); foreach (var person in list1) { var existingPerson = list3.FirstOrDefault(x => x.Name == person.Name); if (existingPerson != null) { existingPerson.Change = existingPerson.Value - person.Value; } else { list3.Add(person); } } foreach (var person in list3) { Console.WriteLine("{0} {1} {2} ", person.Name,person.Value,person.Change); } Console.Read(); } }

Tengo la siguiente situación

class Person { string Name; int Value; int Change; } List<Person> list1; List<Person> list2;

Necesito combinar las 2 listas en una nueva List<Person> en caso de que sea la misma persona en la que el registro de la cosechadora tenga ese nombre, valor de la persona en list2, el cambio sería el valor de list2 - el valor de list1. El cambio es 0 si no hay duplicado


¿Por qué no solo usas Concat ?

Concat es una parte de linq y más eficiente que hacer un AddRange()

en tu caso:

List<Person> list1 = ... List<Person> list2 = ... List<Person> total = list1.Concat(list2);


Esto es Linq

var mergedList = list1.Union(list2).ToList();

Esto es Normaly (AddRange)

var mergedList=new List<Person>(); mergeList.AddRange(list1); mergeList.AddRange(list2);

Esto es Normaly (Foreach)

var mergedList=new List<Person>(); foreach(var item in list1) { mergedList.Add(item); } foreach(var item in list2) { mergedList.Add(item); }

Esto es Normaly (Foreach-Dublice)

var mergedList=new List<Person>(); foreach(var item in list1) { mergedList.Add(item); } foreach(var item in list2) { if(!mergedList.Contains(item)) { mergedList.Add(item); } }


Esto puede hacerse fácilmente utilizando el método de extensión Linq Union. Por ejemplo:

var mergedList = list1.Union(list2).ToList();

Esto devolverá una lista en la que las dos listas se fusionan y los dobles se eliminan. Si no especifica un comparador en el método de extensión de la Unión como en mi ejemplo, utilizará los métodos Equals y GetHashCode predeterminados en su clase Person. Si, por ejemplo, desea comparar personas comparando su propiedad Nombre, debe anular estos métodos para realizar la comparación usted mismo. Verifique el siguiente ejemplo de código para lograr eso. Debe agregar este código a su clase Person.

/// <summary> /// Checks if the provided object is equal to the current Person /// </summary> /// <param name="obj">Object to compare to the current Person</param> /// <returns>True if equal, false if not</returns> public override bool Equals(object obj) { // Try to cast the object to compare to to be a Person var person = obj as Person; return Equals(person); } /// <summary> /// Returns an identifier for this instance /// </summary> public override int GetHashCode() { return Name.GetHashCode(); } /// <summary> /// Checks if the provided Person is equal to the current Person /// </summary> /// <param name="personToCompareTo">Person to compare to the current person</param> /// <returns>True if equal, false if not</returns> public bool Equals(Person personToCompareTo) { // Check if person is being compared to a non person. In that case always return false. if (personToCompareTo == null) return false; // If the person to compare to does not have a Name assigned yet, we can''t define if it''s the same. Return false. if (string.IsNullOrEmpty(personToCompareTo.Name) return false; // Check if both person objects contain the same Name. In that case they''re assumed equal. return Name.Equals(personToCompareTo.Name); }

Si no desea establecer el método Equals predeterminado de su clase Person para usar siempre el Nombre para comparar dos objetos, también puede escribir una clase comparer que use la interfaz IEqualityComparer. A continuación, puede proporcionar este comparador como el segundo parámetro en el método de Unión de extensión Linq. Se puede encontrar más información sobre cómo escribir dicho método comparador en http://msdn.microsoft.com/en-us/library/system.collections.iequalitycomparer.aspx


Hay algunas piezas para hacer esto, asumiendo que cada lista no contiene duplicados, Name es un identificador único, y ninguna de las listas está ordenada.

Primero crea un método de extensión de anexión para obtener una lista única:

static class Ext { public static IEnumerable<T> Append(this IEnumerable<T> source, IEnumerable<T> second) { foreach (T t in source) { yield return t; } foreach (T t in second) { yield return t; } } }

Por lo tanto, puede obtener una lista única:

var oneList = list1.Append(list2);

Luego agrupe en nombre

var grouped = oneList.Group(p => p.Name);

Luego puede procesar cada grupo con un ayudante para procesar un grupo a la vez

public Person MergePersonGroup(IGrouping<string, Person> pGroup) { var l = pGroup.ToList(); // Avoid multiple enumeration. var first = l.First(); var result = new Person { Name = first.Name, Value = first.Value }; if (l.Count() == 1) { return result; } else if (l.Count() == 2) { result.Change = first.Value - l.Last().Value; return result; } else { throw new ApplicationException("Too many " + result.Name); } }

Que se puede aplicar a cada elemento de grouped :

var finalResult = grouped.Select(g => MergePersonGroup(g));

(Advertencia: no probado)


Necesitas algo así como una combinación externa completa. System.Linq.Enumerable no tiene un método que implemente una combinación externa completa, por lo que debemos hacerlo nosotros mismos.

var dict1 = list1.ToDictionary(l1 => l1.Name); var dict2 = list2.ToDictionary(l2 => l2.Name); //get the full list of names. var names = dict1.Keys.Union(dict2.Keys).ToList(); //produce results var result = names .Select( name => { Person p1 = dict1.ContainsKey(name) ? dict1[name] : null; Person p2 = dict2.ContainsKey(name) ? dict2[name] : null; //left only if (p2 == null) { p1.Change = 0; return p1; } //right only if (p1 == null) { p2.Change = 0; return p2; } //both p2.Change = p2.Value - p1.Value; return p2; }).ToList();


Noté que esta pregunta no fue marcada como respondida después de 2 años. Creo que la respuesta más cercana es Richards, pero se puede simplificar bastante con esto:

list1.Concat(list2) .ToLookup(p => p.Name) .Select(g => g.Aggregate((p1, p2) => new Person { Name = p1.Name, Value = p1.Value, Change = p2.Value - p1.Value }));

Aunque esto no generará errores en el caso de que tenga nombres duplicados en cualquiera de los conjuntos.

Algunas otras respuestas han sugerido usar la unión: este no es definitivamente el camino a seguir, ya que solo obtendrá una lista distinta, sin realizar la combinación.


public void Linq95() { List<Customer> customers = GetCustomerList(); List<Product> products = GetProductList(); var customerNames = from c in customers select c.CompanyName; var productNames = from p in products select p.ProductName; var allNames = customerNames.Concat(productNames); Console.WriteLine("Customer and product names:"); foreach (var n in allNames) { Console.WriteLine(n); } }