按带有数字和空格的字符串排序
本文关键字:字符串 排序 空格 数字 | 更新日期: 2023-09-27 18:05:32
我有一个以'Codes'命名的单元列表,显示如下:
- NP/417 416 <<li> NP/gh>
- NP/418 f
- NP/418 c
- NP111
- NP112
这些单位在以下对象中从数据库中查询:
var units = _unitService.GetBySiteId(site.SiteId);
可用于按'code'对单元进行排序:
units = units.OrderBy(u => u.Code);
然而,如果它们是相同的单位代码,我需要单位以数字和字母顺序显示,同时忽略任何空白或"/"字符扰乱了LINQ的排序。例如,上述单元列表的正确顺序应该是:
- NP111
- NP112 416 <<li> NP/gh>
- NP/417
- NP/418 c
- NP/418 f
如何使用LINQ以这种方式对单元进行排序?提前感谢。
我倾向于使用这样的类:
public class Code
{
private Match _match = null;
public Code(string raw)
{
_match = Regex.Match(raw, @"^([A-Z]*)([^0-9]*)('d+)(.*)$");
}
public string Prefix { get { return _match.Groups[1].Value; } }
public string Separator { get { return _match.Groups[2].Value; } }
public int Number { get { return int.Parse(_match.Groups[3].Value); } }
public string Suffix { get { return _match.Groups[4].Value; } }
public override string ToString()
{
return String.Format("{0}/{1:00000}{2}", this.Prefix, this.Number, this.Suffix);
}
}
然后这样做:
var codes = new []
{
"NP/417A",
"NP 416",
"NP/418F",
"NP/418C",
"NP111",
"NP112",
};
var ordered = codes.OrderBy(c => new Code(c).ToString()).ToArray();
给了:
NP111
NP112
NP 416
NP/417A
NP/418C
NP/418F
但是你也可以像这样使用这段代码:
var ordered =
from c in codes
let code = new Code(c)
orderby code.Prefix, code.Number, code.Suffix
select c;
我通常会继续在这个类上实现GetHashCode
和Equals
,以便能够将其用作字典中的键或用于分组目的。
要处理其他情况,我会考虑做一些简单的事情,像这样开始:
public class Code
{
public Code(string raw)
{
if (raw == "SUITE FIVE")
{
this.Prefix = raw;
this.Separator = "/";
this.Number = 0;
this.Suffix = "";
}
else
{
var match = Regex.Match(raw, @"^([A-Z]*)([^0-9]*)('d+)(.*)$");
this.Prefix = match.Groups[1].Value;
this.Separator = match.Groups[2].Value;
this.Number = int.Parse(match.Groups[3].Value);
this.Suffix = match.Groups[4].Value;
}
}
public string Prefix { get; private set; }
public string Separator { get; private set; }
public int Number { get; private set; }
public string Suffix { get; private set; }
public override string ToString()
{
return String.Format("{0}/{1:00000}{2}", this.Prefix, this.Number, this.Suffix);
}
}
一种可能的方法是使用排序集。你可以将你的值直接存储到一个排序的集合中,或者仅仅为了让这些值按照一些标准排序而创建一个排序的集合,作为你的方法的返回值。
例如:public class Site
{
public string Id { get; set; }
}
public class AscendingSiteComparer : IComparer<Site>
{
// Maybe it's not the best regular expression ever, but if your site
// ids start with letters either in capital or lower case, it will
// work!
private readonly static Regex replaceRegex = new Regex(@"[a-z]+'s*([0-9]+)$", RegexOptions.IgnoreCase);
public int Compare(Site x, Site y)
{
int a = int.Parse(replaceRegex.Replace(x.Id, "$1"));
int b = int.Parse(replaceRegex.Replace(y.Id, "$1"));
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
}
}
后面的代码:
var sites = new[] { new Site { Id = "NP 417" }, new Site { Id = "NP 318" }, new Site { Id = "NP 418" }, new Site { Id = "NP111" }, new Site { Id = "NP112" } };
SortedSet<Site> orderedSites = new SortedSet<Site>(sites, new AscendingSiteComparer());
现在您的站点默认将按升序排序
如上所述,您也可以直接构建排序集,而无需在构建过程中提供IEnumerable<T>
:
// While you add items in a disordered way, they're stored in the
// desired order!
SortedSet<Site> orderedSites = new SortedSet<Site>(new AscendingSiteComparer());
orderedSites.Add(new Site { Id = "NP 417" });
orderedSites.Add(new Site { Id = "NP 318" });
orderedSites.Add(new Site { Id = "NP111" });
在一天结束时,如果需要使用固定的顺序提供您的网站集合,我不会返回List<T>
,但SortedSet<T>
,因为其他方法可能会添加新项目,它们将存储在订单中,并且您不需要.OrderBy(...)
每当您想重新订购集合时(直到有一个情况,您需要一个不同的订单,当然……)。
LINQ方式…
如果您觉得排序集可能是多余的,您也可以使用LINQ与OrderBy
扩展方法:
public class StringAscendingComparer : IComparer<string>
{
private readonly static Regex replaceRegex = new Regex(@"[a-z]+'s*([0-9]+)$", RegexOptions.IgnoreCase);
public int Compare(string x, string y)
{
int a = int.Parse(replaceRegex.Replace(x, "$1"));
int b = int.Parse(replaceRegex.Replace(y, "$1"));
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
}
}
var orderedSites2 = sites.OrderBy(site => site.Id, new StringAscendingComparer());
根据需要选择以下任意选项:
/* Remove spaces and sort */
.OrderBy(u=>u.Replace(" ",""))
/* Sort based on last 3 characters */
.OrderBy(u=>u.Substring(u.Length-3))
/* Sort based on last 3 characters as integer */
.OrderBy(u=>int.Parse(u.Substring(u.Length-3)))
/* Remove all non-digits and sort as string */
.OrderBy(u=>Regex.Replace(u,"[^0-9]",""));
/* Remove all non-digits and sort as integer */
.OrderBy(u=>int.Parse(Regex.Replace(u,"[^0-9]","")));
/* Pull out all the digits and sort as integer */
.OrderBy(u=>int.Parse(Regex.Match(u,"([0-9]+)").Groups[1].Value));