OrderBy 忽略重音字母

Posted

技术标签:

【中文标题】OrderBy 忽略重音字母【英文标题】:OrderBy ignoring accented letters 【发布时间】:2016-05-05 19:53:57 【问题描述】:

我想要一个像OrderBy() 这样的方法,它总是命令忽略重音字母,并像无重音一样看待它们。我已经尝试覆盖OrderBy(),但似乎我不能这样做,因为这是一个静态方法。

所以现在我想为OrderBy() 创建一个自定义 lambda 表达式,如下所示:

public static IOrderedEnumerable<TSource> ToOrderBy<TSource, TKey>(
    this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

    if(source == null)
        return null;

    var seenKeys = new HashSet<TKey>();

    var culture = new CultureInfo("pt-PT");
    return source.OrderBy(element => seenKeys.Add(keySelector(element)), 
                          StringComparer.Create(culture, false));
 

但是,我收到了这个错误:

错误 2 方法的类型参数 'System.Linq.Enumerable.OrderBy(System.Collections.Generic.IEnumerable, System.Func, System.Collections.Generic.IComparer)' 不能从 用法。尝试明确指定类型参数。

似乎不喜欢StringComparer。我该如何解决这个问题?

注意:

我已经尝试使用here 中的RemoveDiacritics(),但我不知道在这种情况下如何使用该方法。所以我尝试做类似this 之类的东西,看起来也不错。

【问题讨论】:

你使用的是 Linq2Sql 还是 LinqObjects ? HashSet 是干什么用的? 【参考方案1】:

解决了!我收到这个错误是因为要使用 StringComparer 元素在 OrderBy() 表达式中排序,该元素需要是 string

因此,当我知道该元素是一个字符串时,我将其转换为一个字符串,并使用RemoveDiacritics() 方法忽略重音字母并将它们视为非重音字母。

public static IOrderedEnumerable<TSource> ToOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)

    if(!source.SafeAny())
        return null;

    return source.OrderBy(element => Utils.RemoveDiacritics(keySelector(element).ToString()));

为了保证RemoveDiacritics() 工作正常,我添加了htmlDecode() 行。

public static string RemoveDiacritics(string text)

    if(text != null)
        text = WebUtility.HtmlDecode(text);

    string formD = text.Normalize(NormalizationForm.FormD);
    StringBuilder sb = new StringBuilder();

    foreach (char ch in formD)
    
        UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
        if (uc != UnicodeCategory.NonSpacingMark)
        
            sb.Append(ch);
        
    

    return sb.ToString().Normalize(NormalizationForm.FormC);

【讨论】:

绝对没有必要实现你的ToOrderBy。如果你有一个名为mystrings 的字符串枚举,你可以简单地调用mystrings.OrderBy(RemoveDiacritics, StringComparer.Create(culture, false)) @RenéVogt 我知道,但我想表现出来,因为我的项目很大,我不想在所有项目中进行修改 @RenéVogt 在我的解决方案中使用 ToOrderBy() 我只需要执行 Ctrl + F 并将 .OrderBy( 替换为 .ToOrderBy( 并且将来如果我需要更改此逻辑中的某些内容,我只需需要在一个地方而不是在整个项目中进行更改。这是我的表演。对于例外情况,我有一个 SafeAny() 方法,我检查它是否不为空并且有任何元素。【参考方案2】:

OrderBykeySelector 作为第一个参数。这个keySelector 应该是Func&lt;string,T&gt;。所以你需要一个方法,它接受一个字符串并返回一个值,你的枚举应该按照这个值进行排序

不幸的是,我不确定如何确定一个字符是否是“重音字母”。 RemoveDiacritics 不适用于我的 é

假设您有一个名为IsAccentedLetter 的方法来确定字符是否为重音字母:

public bool IsAccentedLetter(char c)

    // I'm afraid this does NOT really do the job
    return CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.NonSpacingMark;

所以你可以像这样对你的列表进行排序:

string[] myStrings = getStrings(); // whereever your strings come from
var ordered = myStrings.OrderBy(s => new string(s.Select(c => 
    IsAccentedLetter(c) ? ' ' : c).ToArray()), StringComparer.Create(culture, false));

lambda 表达式接受一个字符串并返回相同的字符串,但将重音字母替换为一个空格。OrderBy 现在按这些字符串对您的枚举进行排序,因此“忽略”重音字母。

更新:如果您有一个工作方法RemoveDiacritics(string s) 可以根据需要返回带有重音字母的字符串,您可以像这样简单地调用OrderBy

string[] mystrings = getStrings();
var ordered = myStrings.OrderBy(RemoveDiacritics, StringComparer.Create(culture, false));

【讨论】:

这个逻辑给了我错误'TSource' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'TSource' could be found (are you missing a using directive or an assembly reference?)。此外,您正在用空格替换重音字母,我希望将重音字母视为非重音字母。 @Ninita 哦,这是一个误会:我的 source´ was meant to be your original list. I did not intend to implement my own ToOrderBy` 扩展名,这没有必要!在您的解决方案中,您不需要您的ToOrderBy,您只需致电mystrings.OrderBy(RemoveDiacritics, StringComparer,Create(culture, false)

以上是关于OrderBy 忽略重音字母的主要内容,如果未能解决你的问题,请参考以下文章

实体框架查询忽略我的 orderby

带有瑞典字母的 OrderBy

ORDERBY "human" 使用 SQL 字符串操作的字母顺序

自定义orderBy字母函数

Spring Data JPA OrderBy不起作用[小写字母] [大写字母]类型参数

为啥主键上的“order by”会更改查询计划,从而忽略有用的索引?