【发布时间】:2017-08-23 16:58:37
【问题描述】:
给定以下字符串列表:
string[] Itens = new string[] { "hi", " hi ", "HI", "hí", " Hî", "hi hi", " hí hí ", "olá", "OLÁ", " olá ", "", "ola", "hola", " holà ", "aaaa", "áâàa", " aâàa ", "áaàa", "áâaa ", "aaaa ", "áâaa", "áâaa", };
Distinct 操作的结果应该是:
hi, hi hi, olá, , hola, aaaa
可用于 IEnumerable 的 C# 的 Distinct 操作接受 IEqualityComparer 作为参数,因此我们可以个性化比较。
以下实现可以完成工作
class LengthHash : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (x == null || y == null) return x == y;
var xt = x.Trim();
var yt = y.Trim();
return xt.Length == yt.Length && Culture.CompareInfo.IndexOf(xt, yt, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) >= 0;
}
public int GetHashCode(string obj) => obj?.Trim().Length ?? 1;
}
如果 GetHashCode 不同,则 Equals 甚至不会执行,因此良好的实现很重要。
我已尝试将 GetHashCode 更改为其他 2 种不同的方法。
忽略哈希
public int GetHashCode(string obj) => 1;
标准化哈希
public int GetHashCode(string obj) => obj?.Trim().Normalize().ToUpperInvariant().GetHashCode() ?? 1;
// obs: This approach doesn't produce the same output.
除了使用个性化的 IEqualityComparer 之外,我还尝试在执行 StringComparer.InvariantCultureIgnoreCase 之前修剪列表,但它产生的输出与 Normalize 和 Upper 版本相同。
对纯 Distinct、StringComparer.InvariantCultureIgnoreCase 和 3 种个性化方法进行基准测试会产生以下结果:
Method | Mean | StdErr | StdDev | Median |
------------------------------------ |----------- |---------- |---------- |----------- |
RunDefault | 2.2224 us | 0.0242 us | 0.2391 us | 2.1414 us |
RunHashAsLength | 6.0765 us | 0.0515 us | 0.1857 us | 6.1235 us |
RunIgnoreHash | 6.4078 us | 0.0640 us | 0.6140 us | 6.1982 us |
RunNormalizedHash | 14.5941 us | 0.0742 us | 0.3556 us | 14.4983 us |
RunTrimAndCompareWithStringComparer | 14.4935 us | 0.0213 us | 0.0768 us | 14.5352 us |
输出是:
21 Default: hi, hi , HI, hí, Hî, hi hi, hí hí , olá, OLÁ, olá , , ola, hola, holà , aaaa, áâàa, aâàa , áaàa, áâaa , aaaa , áâaa
6 HashAsLength: hi, hi hi, olá, , hola, aaaa
6 IgnoreHash: hi, hi hi, olá, , hola, aaaa
15 NormalizedHash: hi, hí, Hî, hi hi, hí hí , olá, , ola, hola, holà , aaaa, áâàa, aâàa , áaàa, áâaa
15 RunTrimAndCompareWithStringComparer: hi, hí, Hî, hi hi, hí hí, olá, , ola, hola, holà, aaaa, áâàa, aâàa, áaàa, áâaa
你可以在https://gist.github.com/Flash3001/d50a6b43bba7bc61e3d85734e40dbed9找到完整的测试
问题是:有没有更好的方法来获得所需的最终名单?无论是不同的 GetHashCode、Equals 还是其他预定义的 IEqualityComparer。
【问题讨论】:
-
您的代码并没有真正删除变音符号,而是似乎依赖于文化敏感的比较。我不知道
Culture的定义是什么,但是我用CultureInfo.CurrentUICulture替换了它,并且在测试这个列表{"\u212B", "\u00C5", "\u0041\u030A"}时失败了;埃符号的三种不同的 Unicode 表示。它应该返回一个不同的项目,但它返回两个项目。也就是说,我看不到使用自定义 IEqualityComparer 的任何方法。 -
@TnTinMn 目标不是删除变音符号、空格和大小写,而是获得一个不同的列表,其中所有这些都被忽略,值的实际“版本”无关紧要。鉴于 {"\u212B", "\u00C5", "\u0041\u030A"} 问题,在执行 Distinct 之前对值进行标准化似乎是更好的选择。
-
稍微改进的版本:gist.github.com/Flash3001/9273494a8d94d7d9ae99cbcd8dac780a 在尝试区分之前应用了 Trim 和 Normalize,还删除了 NormalizedHash,因为它没有任何意义。