Оверлей / Объединение двух коллекций с Linq - PullRequest
1 голос
/ 10 июня 2009

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

В списке 1 есть 20 элементов типа TItem, в списке 2 - 5 элементов одного типа. Список 1 уже содержит элементы из Списка 2, но в другом состоянии. Я хочу перезаписать 5 пунктов в списке 1 элементами из списка 2.

Я думал, что объединение может работать, но я хочу перезаписать элементы в Списке 1, а не объединять их вместе и иметь дубликаты.

Существует уникальный ключ, который можно использовать для поиска элементов, которые нужно перезаписать в Списке 1, ключ имеет тип int

Ответы [ 4 ]

2 голосов
/ 11 июня 2009

Вы можете использовать встроенный Linq .Except (), но он хочет IEqualityComparer, поэтому вместо него используйте плавную версию .Except ().

Предполагается, что объект с целочисленной клавишей, как вы указали:

public class Item
{
    public int Key { get; set; }
    public int Value { get; set; }
    public override string ToString()
    {
        return String.Format("{{{0}:{1}}}", Key, Value);
    }
}

Исходный список объектов можно объединить с измененным следующим образом:

IEnumerable<Item> original = new[] { 1, 2, 3, 4, 5 }.Select(x => new Item
    {
        Key = x,
        Value = x
    });
IEnumerable<Item> changed = new[] { 2, 3, 5 }.Select(x => new Item
    {
        Key = x,
        Value = x * x
    });

IEnumerable<Item> result = original.Except(changed, x => x.Key).Concat(changed);
result.ForEach(Console.WriteLine);

выход:

{1:1}
{4:4}
{2:4}
{3:9}
{5:25}
1 голос
/ 10 июня 2009

Как говорит Адам, LINQ о запросах. Однако вы можете создать новую коллекцию правильным образом, используя Enumerable.Union. Вам нужно создать соответствующий IEqualityComparer, хотя было бы неплохо иметь UnionBy. (Еще один для MoreLINQ возможно?)

В основном:

var list3 = list2.Union(list1, keyComparer);

Где keyComparer будет реализация для сравнения двух ключей. MiscUtil содержит ProjectionEqualityComparer, что немного облегчит эту задачу.

В качестве альтернативы, вы можете использовать DistinctBy из MoreLINQ после объединения:

var list3 = list2.Concat(list1).DistinctBy(item => item.Key);
1 голос
/ 10 июня 2009

LINQ не используется для выполнения реальных изменений в базовых источниках данных; это строго язык запросов. Конечно, вы можете выполнить внешнее соединение для List2 из List1 и выбрать сущность List2, если она не равна нулю, и сущность List1, если она есть, но это даст вам IEnumerable<> результатов; это фактически не изменит коллекцию. Вы можете сделать ToList() для результата и присвоить его List1, но это изменит ссылку; Я не знаю, повлияет ли это на остальную часть вашего заявления.

Если рассматривать ваш вопрос буквально, то есть вы хотите ЗАМЕНИТЬ элементы в List1 на элементы из List2, если они существуют, то вам придется сделать это вручную в цикле for над List1 проверка на существование соответствующей записи в List2 и замена записи List1 на индексную на запись из List2.

0 голосов
/ 10 июня 2009

Вот решение с GroupJoin.

        List<string> source = new List<string>() { "1", "22", "333" };
        List<string> modifications = new List<string>() { "4", "555"};
          //alternate implementation
        //List<string> result = source.GroupJoin(
        //    modifications,
        //    s => s.Length,
        //    m => m.Length,
        //    (s, g) => g.Any() ? g.First() : s
        //).ToList();

        List<string> result =
        (
            from s in source
            join m in modifications
            on s.Length equals m.Length into g
            select g.Any() ? g.First() : s
        ).ToList();

        foreach (string s in result)
            Console.WriteLine(s);

Хм, а как насчет метода повторного использования, пока я в нем:

    public static IEnumerable<T> UnionBy<T, U>
    (
        this IEnumerable<T> source,
        IEnumerable<T> otherSource,
        Func<T, U> selector
    )
    {
        return source.GroupJoin(
            otherSource,
            selector,
            selector,
            (s, g) => g.Any() ? g.First() : s
                );
    }

Который называется:

List<string> result = source
  .UnionBy(modifications, s => s.Length)
  .ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...