Создайте список из двух списков объектов с помощью linq

У меня есть следующая ситуация

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

Мне нужно объединить 2 списка в новый List если это тот же человек, что и запись объединения, это имя, значение человека в списке2, изменение будет значением list2 – значением list1. Изменение равно 0, если нет дубликата

Это легко сделать с помощью метода расширения Linq Union. Например:

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

Это вернет список, в котором два списка объединены, и удваивается. Если вы не укажете компаратор в методе расширения Union, как в моем примере, он будет использовать методы Equals и GetHashCode по умолчанию в вашем classе Person. Если вы, например, хотите сравнить людей, сравнивая их свойство Name, вы должны переопределить эти методы для выполнения сравнения самостоятельно. Проверьте следующий пример кода, чтобы выполнить это. Вы должны добавить этот код в свой 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); } 

Если вы не хотите, чтобы метод Equals по умолчанию вашего classа Person всегда использовал имя для сравнения двух объектов, вы также можете написать class сравнения, который использует интерфейс IEqualityComparer. Затем вы можете предоставить этот компаратор как второй параметр в методе расширения Linq. Более подробную информацию о том, как написать такой метод сравнения, можно найти на http://msdn.microsoft.com/en-us/library/system.collections.iequalitycomparer.aspx

Почему вы не просто используете Concat ?

Concat является частью linq и более эффективен, чем использование AddRange()

в твоем случае:

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

Я заметил, что этот вопрос не был отмечен как ответ через 2 года – я думаю, самый близкий ответ – Ричардс, но его можно довольно упростить:

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

Хотя это не будет ошибкой в случае, если у вас есть дубликаты имен в любом наборе.

Некоторые другие ответы предложили использовать unioning – это определенно не путь, потому что он только даст вам отличный список, не делая комбинирования.

Для этого есть несколько частей, предполагая, что каждый список не содержит дубликатов, имя – уникальный идентификатор, и ни один список не упорядочен.

Сначала создайте метод расширения append, чтобы получить один список:

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

Таким образом, можно получить один список:

 var oneList = list1.Append(list2); 

Затем группа по имени

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

Затем можно обрабатывать каждую группу с помощью помощника для обработки одной группы за раз

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

Которые могут применяться к каждому элементу grouped :

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

(Предупреждение: непроверено.)

Это Linq

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

Это Normaly (AddRange)

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

Это Normaly (Foreach)

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

Это 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); } } 

Вам нужно что-то вроде полного внешнего соединения. System.Linq.Enumerable не имеет метода, который реализует полное внешнее соединение, поэтому мы должны сделать это сами.

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

Использует ли следующий код для вашей проблемы? Я использовал foreach с немного linq внутри, чтобы сделать объединение списков и предположил, что люди равны, если их имена совпадают, и, кажется, печатает ожидаемые значения при запуске. Resharper не предлагает никаких предложений по преобразованию foreach в linq, поэтому это, вероятно, так же хорошо, как и для этого.

 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); } } 
  • Почему LINQ to Entities не распознает метод «System.String ToString ()?
  • Entity Framework 4 Single () vs First () vs FirstOrDefault ()
  • Найти, если listA содержит любые элементы, не входящие в списокB
  • Зачем использовать LINQ Join для простого отношения «один-много»?
  • Как выполнять объединения в LINQ для нескольких полей в одном соединении
  • Как получить дубликаты элементов из списка с помощью LINQ?
  • System.LINQ.Dynamic: выберите («new (...)») в List (или любую другую перечислимую коллекцию )
  • Как проверить, является ли IEnumerable пустой или пустой?
  • Полнолуние эквивалентно LINQ Any ()?
  • Случайный массив с использованием LINQ и C #
  • Linq-to-entities - метод Include () не загружается
  • Interesting Posts
    Давайте будем гением компьютера.