按带有数字和空格的字符串排序

本文关键字:字符串 排序 空格 数字 | 更新日期: 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;

我通常会继续在这个类上实现GetHashCodeEquals,以便能够将其用作字典中的键或用于分组目的。


要处理其他情况,我会考虑做一些简单的事情,像这样开始:

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