从第二个列表中存在条件的列表中选择

时间:2019-04-30 16:22:57

标签: c# linq generics collections

我有2个客户列表,而我试图获取两个列表之间没有匹配的Name属性的客户列表。我还需要从第二个列表中包括确实匹配但没有将source属性设置为“ migrated”的客户。基本上,我会列出要添加和更新的客户。我已经尝试了很多方法,但是当我为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 个答案:

答案 0 :(得分:3)

如果我们实现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函数比较名称。如果要使用特定的比较逻辑比较项目,则一次创建比较并重复使用比较容易。

如果我们这样做(假设firstListsecondList均为列表):

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 :(得分:1)

只需在您的Where子句中添加额外条件(即名称不匹配或源不等于“ migrated”):

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

答案 2 :(得分:1)

从字面上看,您只需添加一个逻辑OR并检查“已迁移”字符串即可。

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

由于已经包含任何不匹配的名称,因此无需对名称进行任何其他检查,因此额外的条件将仅是添加确实匹配但源为“ migrated”的名称。

答案 3 :(得分:1)

使用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创建的优雅的@Scott Hannen类。

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