Выбрать из списка, где существует условие во втором списке - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть 2 списка клиентов, и я пытаюсь получить список клиентов, которые НЕ имеют совпадающего свойства Name между двумя списками. Мне также нужно включить клиентов из второго списка, которые совпадают, но у которых для свойства источника не установлено значение «перенесено». По сути, я бы хотел добавить и обновить список клиентов. Я пробовал несколько способов, но когда я добавляю условие для Source, я получаю неправильные результаты. Я делаю это таким образом, чтобы иметь возможность мигрировать партиями.

var legacyCustomers = new List<Customer>{ 
    new Customer() { Name = "Customer 1" },
    new Customer() { Name = "Customer 2" },
    new Customer() { Name = "Customer 3" },
    new Customer() { Name = "Customer 4" } 
};

var currentCustomers = new List<Customer>{ 
    new Customer() { Name = "Customer 1", Source = "migrated" },
    new Customer() { Name = "Customer 2", Source = "migrated" },
    new Customer() { Name = "Customer 3", Source = "" }  
};

В этом сценарии мне нужно, чтобы «Клиент 3» и «Клиент 4» были добавлены в новый список клиентов.

Вот скрипка, которую я использовал https://dotnetfiddle.net/Z0RoFe

Любая помощь очень ценится.

Ответы [ 4 ]

3 голосов
/ 30 апреля 2019

Код становится немного проще, если мы реализуем IEqualityComparer<Customer>. Это означает, что мы создаем класс, который использует собственную логику, чтобы определить, равны ли два клиента.

public class CustomerNameEqualityComparer : IEqualityComparer<Customer>
{
    public bool Equals(Customer x, Customer y)
    {
        return string.Equals(x?.Name, y?.Name, StringComparison.CurrentCultureIgnoreCase);
    }

    public int GetHashCode(Customer obj)
    {
        return obj.Name?.GetHashCode() ?? 0;
    }
}

Согласно этому классу, два клиента равны, если у них одинаковое имя. Удобство состоит в том, что мы можем использовать это сравнение без фактического изменения класса Customer, поскольку это не может всегда быть способом, которым мы хотим сравнивать клиентов.

Мы можем сделать это без этого, но это приводит к множеству сложных Where функций, которые сравнивают имена. Если вы собираетесь сравнивать элементы, используя особую логику сравнения, то проще создать сравнение один раз и использовать его повторно.

Если мы сделали это (учитывая, что firstList и secondList оба List):

var customersFromFirstListNotInSecondList = firstList.Except(secondList);

Это не сработало бы, потому что для сравнения двух списков использовалось бы равенство ссылок вместо поиска совпадающих имен. Но если мы сделаем это:

var customersFromFirstListNotInSecondList = 
    firstList.Except(secondList, new CustomerNameEqualityComparer());

Он будет сравнивать клиентов в двух списках, просто сопоставляя их имена.

Этот класс сравнения также облегчает реализацию второго шага:

var matchingButNotMigrated = 
    firstList.Intersect(secondList, new CustomerNameEqualityComparer())
        .Where(customer => customer.Source != "migrated");

Возвращает элементы из обоих списков (пересечение), снова сравнивая их по именам. Если у него есть элементы в обоих списках, он исключает те, которые были перенесены.

1 голос
/ 30 апреля 2019

Использование метода Except дает решение O (n).

var comparer = new CustomerNameEqualityComparer();
var results = legacyCustomers
    .Except(currentCustomers.Where(customer => customer.Source == "migrated"), comparer);
Console.WriteLine($"Result: {String.Join(", ", results.Select(c => c.Name))}");

Вывод:

Заказчик 3, Заказчик 4

Я использую элегантный класс CustomerNameEqualityComparer, созданный @ Скотт Ханнен .101

public class CustomerNameEqualityComparer : IEqualityComparer<Customer>
{
    public bool Equals(Customer x, Customer y)
    {
        return string.Equals(x?.Name, y?.Name, StringComparison.OrdinalIgnoreCase);
    }

    public int GetHashCode(Customer obj)
    {
        return obj.Name?.GetHashCode() ?? 0;
    }
}
1 голос
/ 30 апреля 2019

Вам буквально нужно просто добавить логическое ИЛИ и проверить «перенесенную» строку.

var migrateList = legacyCustomers.Where(c => currentCustomers.All(c2 =>
    !string.Equals(c2.Name, c.Name, StringComparison.CurrentCultureIgnoreCase) 
    || c2.Source != "migrated")).ToList();

Нет необходимости выполнять дополнительную проверку имени, так как любые несовпадающие имена уже включенытаким образом, дополнительным условием будет добавление только имен, которые соответствуют, но имеют Источник "перенесено".

1 голос
/ 30 апреля 2019

Просто добавьте дополнительное условие в ваше предложение Where (т. Е. Когда имена не совпадают или источник не равен "перенесено"):

var migrateList = legacyCustomers
    .Where(c => currentCustomers.All(c2 =>
        !string.Equals(c2.Name, c.Name, StringComparison.CurrentCultureIgnoreCase) ||
        !string.Equals(c2.Source, "migrated", StringComparison.CurrentCultureIgnoreCase)
        ))
    .ToList();
...