在 Umbraco Razor 中用数字对字符串进行排序

问题描述 投票:0回答:1

在 Umbraco 站点中,我有一个字符串形式的地址,因此该字符串将包含一个数字,例如:

Street Road 2, Street Road 14, Street Road 22b

我正在按地址(以及其他值)对列表进行排序

var apartments = Umbraco.Content(Guid.Parse("f3c9c894-16e5-491e-b39f-77bf2cb13c6f"))
.ChildrenOfType("ApartmentItem")
.Where(x => x.IsVisible()).OrderBy(x => x.Value("apartmentItemStatus")
.ToString().Replace(" ", "")).ThenBy(x => x.Name.ToString().Replace(" ", ""));

预期的结果是

Street Road 2
Street Road 14
Street Road 22b

但实际结果是

Street Road 14
Street Road 2
Street Road 22b

我不知道如何正确排序。我想避免拆分字符串、取数字、转换为 int 等

c# linq razor umbraco
1个回答
0
投票

String
上使用一些扩展方法,您可以创建一个
IComparer<IEnumerable<string>>
用于对
IEnumerable<string>
进行排序,这样非数字字符串按正常排序,数字字符串按长度排序,然后按字符串值排序。然后你可以将地址拆分成单词,并使用新的比较器进行排序。

public static class StringExt {
    static Regex wordRE = new(@"\w+", RegexOptions.Compiled | RegexOptions.NonBacktracking);
    public static IEnumerable<string> Words(this string s) => s.Matches(wordRE);
    public static IEnumerable<string> Matches(this string s, Regex re) => re.Matches(s).Cast<Match>().Select(m => m.Value);

    public static bool IsAllDigits(this string s) => s.All(ch => Char.IsDigit(ch)); // faster than Regex
}

public class WordsAndNumbersComparer : IComparer<IEnumerable<string>> {
    StringComparer cmp;

    protected WordsAndNumbersComparer(StringComparer c) => cmp = c;

    public WordsAndNumbersComparer Create(CultureInfo ci, bool ignoreCase)
        => new WordsAndNumbersComparer(StringComparer.Create(ci, ignoreCase));

    static WordsAndNumbersComparer currentCultureComparer;
    public static WordsAndNumbersComparer CurrentCulture
        => currentCultureComparer ??= new WordsAndNumbersComparer(StringComparer.CurrentCulture);

    static WordsAndNumbersComparer currentCultureIgnoreCaseComparer;
    public static WordsAndNumbersComparer CurrentCultureIgnoreCase
        => currentCultureIgnoreCaseComparer ??= new WordsAndNumbersComparer(StringComparer.CurrentCultureIgnoreCase);

    static WordsAndNumbersComparer invariantCultureComparer;
    public static WordsAndNumbersComparer InvariantCulture
        => invariantCultureComparer ??= new WordsAndNumbersComparer(StringComparer.InvariantCulture);

    static WordsAndNumbersComparer invariantCultureIgnoreCaseComparer;
    public static WordsAndNumbersComparer InvariantCultureIgnoreCase
        => invariantCultureIgnoreCaseComparer ??= new WordsAndNumbersComparer(StringComparer.InvariantCultureIgnoreCase);

    static WordsAndNumbersComparer ordinalComparer;
    public static WordsAndNumbersComparer Ordinal
        => ordinalComparer ??= new WordsAndNumbersComparer(StringComparer.Ordinal);

    static WordsAndNumbersComparer ordinalIgnoreCaseComparer;
    public static WordsAndNumbersComparer OrdinalIgnoreCase
        => ordinalIgnoreCaseComparer ??= new WordsAndNumbersComparer(StringComparer.OrdinalIgnoreCase);

    public int Compare(IEnumerable<string> firstWords, IEnumerable<string> secondWords) {
        return firstWords.Zip(secondWords)
                     .Select(fst => {
                         if (fst.First.IsAllDigits() && fst.Second.IsAllDigits()) {
                             var lenCmp = fst.First.Length.CompareTo(fst.Second.Length);
                             if (lenCmp != 0)
                                 return lenCmp;
                         }
                         return cmp.Compare(fst.First, fst.Second);
                     })
                     .FirstOrDefault(c => c != 0, 0);
    }
}

有了这些定义,你可以像这样使用它们:

var ans = src.OrderBy(s => s.Words(), WordsAndNumbersComparer.CurrentCultureIgnoreCase);

按预期对您的示例街道地址进行排序。

注意:使用 .Net 7

Regex
围绕
ReadOnlySpan<char>
的增强功能,您可能会编写一个
WordsAndNumbersComparer
的版本,它采用
string
并在内部使用增强功能来减少比较对象(字符串)的创建。

© www.soinside.com 2019 - 2024. All rights reserved.