如何比较两个属性组合的两个列表和 select 在第三个 属性 中不匹配的行?

How to compare two lists on a combination of two properties and select a row which has mismatch in the third property?

我想根据两个属性(国家/地区和城市)的组合比较以下两个列表。

比较时,印度-钦奈出现在两个列表中并且具有相同的值 (1)。同样,UK-London 也出现在两个列表中并且具有相同的值 (3)。但是,尽管美国-纽约同时出现在两个列表中,但它们的值并不匹配(列表 1 中有 2 个,列表 2 中有 5 个)。

请帮我将最短的 linq 表达式写到 select list1 中的 '2-USA-New York',因为它的值与 list2 ('5-USA-New York') 不匹配。

void Main()
{
    List<A> list1 = new List<A> {
        new A { Country = "India", City = "Chennai", Value = 1 },
        new A { Country = "USA", City = "New York", Value = 2 },
        new A { Country = "UK", City = "London", Value = 3 }
    };

    List<A> list2 = new List<A> {
        new A { Country = "India", City = "Chennai", Value = 1 },
        new A { Country = "USA", City = "New York", Value = 5 },
        new A { Country = "UK", City = "London", Value = 3 }
    };

    list1.Dump();
    list2.Dump();
}

class A
{
    public int Value { get; set; }
    public string Country { get; set; }
    public string City { get; set; }
}

假设您的列表中没有重复的 { Country, City } 对:

var list1Missmatched = list1
    .Join(list2, 
          left => new { left.Country, left.City },
          right => new { right.Country, right.City }, 
          (left, right) => new { left, right })
    .Where(x => x.left.Value != x.right.Value)
    .Select(x => x.left)
    .ToList();

这是可行的,因为在 leftList.Join(rightList, leftMatchBy, rightMatchBy, matchedPairResultSelector) 中我们使用 'anonymous object' 作为要匹配的键。匿名对象的相等性(和哈希码)的行为与值类型相同,即 new { Foo = 1 }new { Foo = 1 } 相等并且具有相同的哈希码,即使它们是两个不同的实例。

Join 从 (matchByKey, listItem) 对中构建散列 table,这允许几乎线性的算法复杂度 - O(n)(与 Where(Any()) 解决方案对比,具有二次复杂度 - O(n^2))。 如果你有兴趣,最近我写了一篇比较这两种方法的小文章performance test

我知道这个问题已经得到了令人满意的回答,但这里有一个替代解决方案。

对于某些人来说,这可能更直观/更容易理解,因为它避免了整个 join-概念,而只是寻找 任何匹配的行当前行 代替。

但是,您可以预期类似此解决方案的解决方案比上述解决方案慢,尤其是对于更长/更复杂的列表,因此请记住这一点,并且仅将其用于更简单的情况。

var result = list1
              .Where(rowFromFirst => 
                       list2.Any(rowFromSecond => 
                                    rowFromSecond.Country == rowFromFirst.Country &&
                                    rowFromSecond.City == rowFromFirst.City && 
                                    rowFromSecond.Value != rowFromFirst.Value));