Подтвердить что ты не робот

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.

Мне кажется, что я не имею никакого смысла. Поэтому, пожалуйста, задавайте вопросы в комментариях, и я попытаюсь уточнить.

4b9b3361

Ответ 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).