Crie uma lista de duas listas de objects com linq

Eu tenho a seguinte situação

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

Eu preciso combinar as duas listas em uma nova List no caso de ser a mesma pessoa que o registro de combine teria esse nome, o valor da pessoa na list2, change seria o valor de list2 – o valor de list1. A mudança é 0 se não duplicar

Isso pode ser feito facilmente usando o método de extensão Linq Union. Por exemplo:

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

Isso retornará uma Lista na qual as duas listas são mescladas e as duplas são removidas. Se você não especificar um comparador no método de extensão Union como no meu exemplo, ele usará os methods Equals e GetHashCode padrão em sua class Person. Se, por exemplo, você quiser comparar pessoas comparando sua propriedade Name, você deve replace esses methods para realizar a comparação. Verifique o exemplo de código a seguir para conseguir isso. Você deve adicionar esse código à sua class Person.

 ///  /// Checks if the provided object is equal to the current Person ///  /// Object to compare to the current Person /// True if equal, false if not 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); } ///  /// Returns an identifier for this instance ///  public override int GetHashCode() { return Name.GetHashCode(); } ///  /// Checks if the provided Person is equal to the current Person ///  /// Person to compare to the current person /// True if equal, false if not 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); } 

Se você não quiser definir o método Equals padrão da sua class Person para usar sempre o Name para comparar dois objects, também poderá escrever uma class comparadora que use a interface IEqualityComparer. Você pode então fornecer esse comparador como o segundo parâmetro no método de união da extensão Linq. Mais informações sobre como escrever esse método comparador podem ser encontradas em http://msdn.microsoft.com/en-us/library/system.collections.iequalitycomparer.aspx

Por que você não usa apenas o Concat ?

Concat é uma parte do linq e mais eficiente do que fazer um AddRange()

no seu caso:

 List list1 = ... List list2 = ... List total = list1.Concat(list2); 

Eu notei que esta questão não foi marcada como respondida após 2 anos – eu acho que a resposta mais próxima é Richards, mas pode ser simplificada bastante para isso:

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

Embora isso não seja um erro no caso de você ter nomes duplicados em qualquer conjunto.

Algumas outras respostas sugeriram o uso de sindicatos – este definitivamente não é o caminho a percorrer, pois só lhe dará uma lista distinta, sem fazer a combinação.

Há algumas partes para fazer isso, supondo que cada lista não contenha duplicatas, Name é um identificador exclusivo e nenhuma lista é ordenada.

Primeiro, crie um método de extensão de acréscimo para obter uma única lista:

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

Assim, pode obter uma lista única:

 var oneList = list1.Append(list2); 

Então agrupe no nome

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

Em seguida, pode processar cada grupo com um ajudante para processar um grupo de cada vez

 public Person MergePersonGroup(IGrouping 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); } } 

Qual pode ser aplicado a cada elemento de grouped :

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

(Aviso: não testado.)

Este é o Linq

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

Isto é Normaly (AddRange)

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

Isto é Normaly (Foreach)

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

Isto é Normaly (Foreach-Dublice)

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

Você precisa de algo como uma junit externa completa. System.Linq.Enumerable não tem nenhum método que implemente uma junit externa completa, portanto, temos que fazer isso sozinhos.

 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(); 

O código a seguir funciona para o seu problema? Eu usei um foreach com um pouco de linq dentro para fazer a combinação de listas e assumi que as pessoas são iguais se seus nomes coincidirem, e parece imprimir os valores esperados quando executado. O Resharper não oferece nenhuma sugestão para converter o foreach em linq, de modo que isso é provavelmente tão bom quanto conseguirá fazê-lo dessa maneira.

 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 list1 = new List { new Person("a", 1), new Person("b", 2), new Person("c", 3), new Person("d", 4) }; List list2 = new List { new Person("a", 4), new Person("b", 5), new Person("e", 6), new Person("f", 7) }; List 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(); } } 
 public void Linq95() { List customers = GetCustomerList(); List 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); } }