C# - 有没有更好的方法来删除List中多个属性的重复值?

sup*_*fly 0 c# performance list morelinq

尝试从列表中删除重复项,其中重复项是第一个,第二个或两个属性相等时(在列表中出现多次).使用MoreLINQ,下面的代码工作正常:

var list = new List<LinqTest> // LinqTest: object containing 2 strings
{
    // are ok
    new LinqTest { Str1 = "a1", Str2 = "b1"},
    new LinqTest { Str1 = "a2", Str2 = "b2"},
    new LinqTest { Str1 = "a3", Str2 = "b3"},
    new LinqTest { Str1 = "a5", Str2 = "b5"},
    new LinqTest { Str1 = "a6", Str2 = "b6"},
    new LinqTest { Str1 = "x1", Str2 = "y1"},
    new LinqTest { Str1 = "y1", Str2 = "x1"},

    // must be removed
    new LinqTest { Str1 = "d1", Str2 = "b4"},
    new LinqTest { Str1 = "d1", Str2 = "d2"},
    new LinqTest { Str1 = "d1", Str2 = "d2"},
    new LinqTest { Str1 = "a4", Str2 = "d2"},
    new LinqTest { Str1 = "d3", Str2 = "b7"},
    new LinqTest { Str1 = "d3", Str2 = "b8"},
    new LinqTest { Str1 = "d3", Str2 = "b8"},
};

var duplicatesStr1 = list
    .GroupBy(x => x.Str1)
    .Where(x => x.Count() > 1)
    .SelectMany(x => x)
    .ToList();

var duplicatesStr2 = list
    .GroupBy(x => x.Str2)
    .Where(x => x.Count() > 1)
    .SelectMany(x => x)
    .ToList(); ;

var res = list
    .ExceptBy(duplicatesStr1, x => x.Str1)
    .ExceptBy(duplicatesStr2, x => x.Str2);

var rem = duplicatesStr1
    .Union(duplicatesStr2)
    .DistinctBy(x => new { x.Str1, x.Str2})
    .ToList();

Console.WriteLine("----------");
foreach (var linqTest in res)
{
    Console.WriteLine("keep> " + linqTest.Str1 + "-" + linqTest.Str2);
}

Console.WriteLine("----------");
foreach (var linqTest in rem)
{
    Console.WriteLine("remove> " + linqTest.Str1 + "-" + linqTest.Str2);
}
Run Code Online (Sandbox Code Playgroud)

问题: 是否有更有效和/或更短的方法来实现这一目标?

Typ*_*eIA 5

您可以使用.DistinctLINQ方法执行此操作.您必须定义一个自定义IEqualityComparer来决定何时将两个元素视为不同.

public class MyComparer : IEqualityComparer<LinqTest>
{
    public bool Equals(LinqTest x, LinqTest y)
    {
        return x.Str1 == y.Str1 || x.Str2 == y.Str2;
    }

    public int GetHashCode(LinqTest obj)
    {
        return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

List<LinqTest> noDuplicates = originalList.Distinct(new MyComparer()).ToList();
Run Code Online (Sandbox Code Playgroud)

棘手的部分是IEqualityComparer正确实施(我没有第一次!).GetHashCode()必须为两个被认为相等的对象返回相同的值.由于我们的平等概念不具有传递性,因此满足此要求的唯一方法是返回一个常量值.这是允许的,但是破坏了哈希码的目标,这是加速相等性检查的一种方式:如果哈希码不同,则对象必须不同而不需要可能更昂贵的"深度"比较.

因此,此代码有效,但无法实现最高性能.