这是一个非常简单的问题,肯定已经被问过并已经回答了......但是我找不到它。
我想使用LINQ从值列表列表中删除重复项。我尝试过以下方法:
List<List<int>> a = new List<List<int>>() { new List<int>() { 1, 2, 3 }, new List<int>() { 1, 2, 3 }, new List<int>() { 2, 3, 4 } };
// remove duplicates from a
List<List<int>> b = a.Distinct().ToList(); // this doesn't do it
List<List<int>> c = a.Distinct(new ListKeyComparer<int>()).ToList(); // nor does this
internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
public bool Equals(List<TKey> key1, List<TKey> key2)
{
return String.Join("_", key1).Equals(String.Join("_", key2));
}
public int GetHashCode(List<TKey> key)
{
return key.GetHashCode();
}
}
欢迎所有解决方案!
答案 0 :(得分:2)
你想要的是序列的IEqualityComparer
。这并不是特别困难。 (请注意,您可以简单地将示例概括为通用,而不是特定于int
,并使用IEnumerable
而不是List
,因为您不需要功能上的列表。
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
private IEqualityComparer<T> comparer;
public SequenceComparer(IEqualityComparer<T> comparer = null)
{
comparer = comparer ?? EqualityComparer<T>.Default;
}
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y, comparer);
}
public int GetHashCode(IEnumerable<T> sequence)
{
unchecked
{
int hash = 19;
foreach (var item in sequence)
hash = hash * 79 + comparer.GetHashCode(item);
return hash;
}
}
}
Equals
以SequenceEqual
的形式免费为您提供。剩下的唯一有趣的事情是基于序列中的值创建一个有意义的哈希,而不是使用序列提供的GetHashCode
方法,因为它通常会赢得&#t; t这样做(大多数IEnumerable
,包括List
,都会将他们的哈希码基于对类的引用,而不是其中的值。)
在这种情况下,不需要为SequenceComparer
提供项目类型的内部比较器(在本例中为int
),因为默认的相等应该是您需要的。如果您有一个List<List<string>>
,并且想要比较列表的相等性并对字符串进行不区分大小写的比较,那么您可以使用new SequenceComparer<string>(StringComparer.InvariantCultureIgnoreCase)
。
请注意,连接项的字符串值不是比较两个序列的特别安全的方法。对象可能没有有意义的ToString
方法。 (任何不覆盖ToString
的类型都会打印出类型名称,这意味着一切都将等于其他所有类型。)您还需要处理碰撞案例。例如,如果您有一个生成字符串值"1_2"
的项目,则该项目将被视为等于两个不同的项目,每个项目生成"1"
和"2"
。
答案 1 :(得分:0)
您的实施问题在于它使用straight
列表的GetHashCode
key
。您可以通过将数字与下划线连接起来构建的“密钥字符串”的哈希码替换它,或者通过动态计算哈希代码来修复它:
// Here is a fix to your method. It would work if TKey values
// cannot have underscores. In any event, it will be very slow.
internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
// Make a method that produces the key to avoid repeating yourself:
private string MakeKey(List<TKey> key) {
return String.Join("_", key);
}
public bool Equals(List<TKey> key1, List<TKey> key2)
{
return MakeKey(key1).Equals(MakeKey(key2));
}
public int GetHashCode(List<TKey> key)
{
return MakeKey(key).GetHashCode();
}
}
这是一个更好的实现:
internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
public bool Equals(List<TKey> key1, List<TKey> key2)
{
return key1.SequenceEqual(key2);
}
public int GetHashCode(List<TKey> key)
{
return key.Aggregate((p, v) => 31*p + v.GetHashCode());
}
}
这个实现更好有三个原因:
TKey
字符串包含下划线,此实现也能正常运行。该实现使用LINQ方法SequenceEqual
和Aggregate
来缩短Equals
和GetHashCode
的代码。