LINQ Join 2 List <T> s
Предисловие: я не понимаю, что это делает:
o => o.ID, i => i.ID, (o, id) => o
Так что будь проще.: -)
У меня есть 2 списка, которые мне нужно объединить:
// list1 contains ALL contacts for a customer.
// Each item has a unique ID.
// There are no duplicates.
ContactCollection list1 = myCustomer.GetContacts();
// list2 contains the customer contacts (in list1) relevant to a REPORT
// the items in this list may have properties that differ from those in list1.
/*****/// e.g.:
/*****/ bool SelectedForNotification;
/*****/// may be different.
ContactCollection list2 = myReport.GetContacts();
Мне нужно создать третий ContactCollection, содержащий все контакты в list1
, но со свойствами элементов в list2
, если элемент находится в списке [2] (list3.Count == list1.Count
).
Мне нужно заменить все элементы в list1
на элементы в list2
, где элементы в list1
имеют идентификаторы элементов в list2
. Полученный список (list3
) должен содержать одинаковое количество элементов в list1
.
Мне кажется, что я не имею никакого смысла. Поэтому, пожалуйста, задавайте вопросы в комментариях, и я попытаюсь уточнить.
Ответы
Ответ 1
Совпадения не так сложны, но ваша проблема может, вероятно, использовать некоторые дополнительные объяснения.
Чтобы присоединиться к двум спискам, вы можете сделать что-то вроде
var joined = from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 };
это даст IEnumerable<'a>
, где 'a - анонимный тип, содержащий элемент из списка1 и связанный с ним элемент из списка2. Затем вы можете выбрать, какие свойства объектов использовать по мере необходимости.
Чтобы получить результат в конкретном списке, все, что вам нужно, это вызов .ToList(). Вы можете сделать это как
var list3 = joined.ToList();
// or
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
select new { Item1, Item2 }).ToList();
Чтобы сделать левое соединение, чтобы выбрать все элементы из списка1, даже без совпадения в списке2, вы можете сделать что-то вроде этого
var list3 = (from Item1 in list1
join Item2 in list2
on Item1.Id equals Item2.Id // join on some property
into grouping
from Item2 in grouping.DefaultIfEmpty()
select new { Item1, Item2 }).ToList();
Это даст вам список, в котором Item1 равно элементу из первого списка, а Item2 будет либо равным совпадающему элементу из второго списка, либо по умолчанию, который будет нулевым для ссылочного типа.
Ответ 2
Вот что я придумал (на основе this):
List<Contact> list3 = (from item1 in list1
join item2 in list2
on item1.ContactID equals item2.ContactID into g
from o in g.DefaultIfEmpty()
select o == null ? item1 :o).ToList<Contact>();
Моя любимая часть - большой носовой смайлик
:o)
Спасибо за вашу помощь!
Ответ 3
Вот DotNetFiddle с присоединением группы Linq
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
class Order
{
public int Id;
public string Name;
public Order(int id, string name)
{
this.Id = id;
this.Name = name;
}
}
class OrderItem
{
public int Id;
public string Name;
public int OrderId;
public OrderItem(int id, string name, int orderId)
{
this.Id = id;
this.Name = name;
this.OrderId = orderId;
}
}
List<Order> orders = new List<Order>()
{
new Order(1, "one"),
new Order(2, "two")
};
List<OrderItem> orderItems = new List<OrderItem>()
{
new OrderItem(1, "itemOne", 1),
new OrderItem(2, "itemTwo", 1),
new OrderItem(3, "itemThree", 1),
new OrderItem(4, "itemFour", 2),
new OrderItem(5, "itemFive", 2)
};
var joined =
from o in orders
join oi in orderItems
on o.Id equals oi.OrderId into gj // gj means group join and is a collection OrderItem
select new { o, gj };
// this is just to write the results to the console
string columns = "{0,-20} {1, -20}";
Console.WriteLine(string.Format(columns, "Order", "Item Count"));
foreach(var j in joined)
{
Console.WriteLine(columns, j.o.Name, j.gj.Count() );
}
Ответ 4
Похоже, вам не нужно полное соединение. Вместо этого вы можете выполнить полусоединение, проверяя каждый контакт в списке 2, чтобы увидеть, содержится ли он в списке 1:
ContactCollection list3 = list2.Where(c => list1.Contains(c));
Я не знаю, насколько велики ваши списки, но обратите внимание, что этот подход имеет сложность O (nm), если list1 не сортируется или не поддерживает быстрый поиск (как в hashset), и в этом случае он может быть таким же эффективным, как O (nlog (m)) или переписан как объединение слиянием и O (n).