【问题标题】:Sort an list of lengths in C#在 C# 中对长度列表进行排序
【发布时间】:2022-11-13 14:56:06
【问题描述】:

我有一个List<string> 的数组列表,其中包含按以下顺序排列的值["1m", "1cm", "4km","2cm"](厘米、米和公里)

当我想对这个数组进行排序时,我得到了错误的答案。我使用 OrderBy:

List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result= data.OrderBy(x => x).ToList();

结果是:

{ "1cm", "1m", "2cm", "4km"}

但我希望答案是这个顺序-:{ "1cm", "2cm", "1m", "4km"}

【问题讨论】:

  • 这按字母顺序排序。 .NET 不知道测量单位,您必须自己转换它们,例如通过将每个长度都带到毫米。
  • 这是因为 C# 不了解您的字符串包含的内容。它将按字母顺序排序。如果您需要在排序中添加逻辑,我建议首先实现一个包含“解析”方法的“长度”结构,然后深入 IComparable 接口以添加排序
  • 2.5m和2m50cm呢?有这么多的问题,你还没有尝试过。很明显,.NET 无法神奇地理解您的排序逻辑。
  • 您可能可以创建某种包含value 字段的类或结构(以保存距离的数值(以您想要的任何单位和display 字段)(保存您要显示的字符串版本。您必须将display 字段解析为数字value 字段。然后您可以创建一个List&lt;yourClass&gt; 并根据value 对其进行排序,然后打印display 字段。
  • OrderBy 有一个重载,它接受一个实现 IComparer 接口的类的实例。您应该编写这样的类,它能够实现将字符串 m km cm 和基本度量单位中的其他内容转换的逻辑,然后返回比较的逻辑结果。

标签: c# .net .net-core


【解决方案1】:

您已按字母顺序对数据进行了排序。首先比较first character。然后second character 和...

需要根据cm(or m)对数据进行归一化,然后排序。

   List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
   var result = data.OrderBy(x => lenghtCM(x));

    public int lenghtCM(string lenghtStr)
    {
        if (lenghtStr.Contains("cm"))
        {
            string num = lenghtStr.Split("cm")[0];
            return int.Parse(num);
           
        } 
        else if (lenghtStr.Contains("km"))
        {
            string num = lenghtStr.Split("km")[0];
            return int.Parse(num) * 100*1000;
        }
        else if (lenghtStr.Contains("m"))
        {
            string num = lenghtStr.Split('m')[0];
            return int.Parse(num) * 100;
        }
        return 0;
    }

然后result:

{ "1cm", "2cm", "1m", "4km"}

【讨论】:

  • 谢谢你。由于声誉低,我不能投赞成票,但我在这里谢谢你
【解决方案2】:

您需要使用 IComparable 进行自定义排序

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication49
{
    
    class Program
    {
        static void Main(string[] args)
        {
            List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
            List<string> results = data.Select(x => new SortDistance(x)).OrderBy(x => x).Select(x => x.value).ToList();

        }
    }
    public class SortDistance : IComparable<SortDistance>
    {
        const string pattern = @"(?'number'd+)(?'multiplier'.*)";
        List<string> distanceOrder = new List<string>() { "cm", "m", "km" };
        public string value { get; set; }
        public int distance { get; set; }
        public string multiplier { get; set; }

        public SortDistance(string value)
        {
            this.value = value;
            Match match = Regex.Match(value, pattern);
            this.distance = int.Parse(match.Groups["number"].Value);
            this.multiplier = match.Groups["multiplier"].Value;
        }
        public int CompareTo(SortDistance other)
        {
            if (this.multiplier == other.multiplier)
                return this.distance.CompareTo(other.distance);
            else
                return distanceOrder.IndexOf(this.multiplier).CompareTo(distanceOrder.IndexOf(other.multiplier));
        }
    }
}

【讨论】:

    【解决方案3】:

    您不能使用 OrderBy 进行排序。

    您必须首先定义从所有单位到最小单位的转换。例如米到厘米,公里到厘米......

    所以 1m 等于 100 cm

    然后你必须遍历你的列表并检查每个项目的单位,得到它与最小单位的等价物。

    创建另一个列表。

    您可以实现插入排序来对项目进行排序,并根据比较添加继续插入项目。

    【讨论】:

      【解决方案4】:

      如果可以,请先解析字符串:

      enum Unit { cm, m, km }
      
      record Measurment(int Length, Unit Unit)
      {
          public override string ToString() => $"{Length}{Enum.GetName(typeof(Unit), Unit)}";
      
          public double NormalizedLength => Unit switch
          {
              Unit.cm => Length * 0.001,
              Unit.m => Length * 1.0,
              Unit.km => Length * 1000.0,
              _ => throw new NotImplementedException()
          };
      
          public static Measurment Parse(string source)
          {
              var digits = source.TakeWhile(char.IsDigit).Count();
              var length = int.Parse(source.AsSpan(0, digits));
      
              // switches with source.AsSpan(digits) in preview
              var measure = source[..digits] switch
              {
                  "cm" => Unit.cm,
                  "m" => Unit.m,
                  "km" => Unit.km,
                  _ => throw new NotImplementedException(),
              };
              return new Measurment(length, measure);
          }
      }
      

      .

      var result = data.Select(Measurment.Parse).OrderBy(x => x.NormalizedLength).ToList();
      

      这使您可以按NormalizedLength 对测量值进行排序,ToString 会取回原始字符串。应该非常快,易于使用新单元进行扩展,如果将 Parse 转换为 TryParse 模式,则可以使其具有容错性。

      【讨论】:

        【解决方案5】:

        有一个名为 UnitsNet 的 NuGet 包来管理解析和操作 SI 单位。

        如果你安装那个包(通过 Add | NuGet Package,搜索并选择UnitsNet 并安装它),那么你可以编写以下代码:

        (您需要先在代码文件顶部添加using UnitsNet;

        这也适用于nm 等。

        List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
        
        var result = data.OrderBy(Length.Parse).ToList();
        
        Console.WriteLine(string.Join(", ", result));
        

        这将输出"1cm, 2cm, 1m, 4km"

        【讨论】:

        • 沃森谢谢你。由于声望低,我不能投赞成票,但我在这里谢谢你
        最近更新 更多