linq 在Umbraco Razor中使用数字排序字符串

sycxhyv7  于 2023-03-27  发布在  其他
关注(0)|答案(1)|浏览(126)

在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等。

5vf7fwbs

5vf7fwbs1#

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);

它按预期对示例街道地址进行排序。
注意:使用围绕ReadOnlySpan<char>的.Net 7 Regex增强功能,您可能会编写一个WordsAndNumbersComparer版本,该版本采用string并在内部使用增强功能来减少比较的对象(字符串)创建。但是由于OrderBy缓存键,因此将不变键计算拉出来可能比在排序时不断切片地址更有效。

相关问题