在 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 等
在
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
并在内部使用增强功能来减少比较对象(字符串)的创建。