【问题标题】:Sorting Algorithm - C#排序算法 - C#
【发布时间】:2012-12-28 02:18:47
【问题描述】:

我有以下未排序的列表:

List<string> myUnsortedList = New List<string>();

myUnsortedList.Add("Alpha");
myUnsortedList.Add("(avg) Alpha");
myUnsortedList.Add("Zeta");
myUnsortedList.Add("Beta");
myUnsortedList.Add("(avg) Beta");
myUnsortedList.Add("(avg) Zeta");

我想按字母降序对列表进行排序,然后在正常值后面加上 (avg) 的值:

最终结果:Zeta, (avg) Zeta, Beta, (avg) Beta, Alpha, (avg) Alpha

我的应用程序是用C# 编写的,我想使用LINQ 来完成排序

【问题讨论】:

  • 拥有 22 个金色徽章,你已经是新的了:到目前为止你尝试了什么?
  • 为什么不把它设为“Alpha”和“Alpha (avg)”,然后只使用标准库排序?
  • @balexandre - 我还没有厌倦任何事情,因为我在上班前发布了这个问题,直到今天才能实现一些答案。
  • @jb。 - 原因是客户希望 (avg) 出现在名称之前。基本上是业务需求。

标签: c# linq sorting


【解决方案1】:

这应该可以满足您的需要,假设“(avg)”是唯一的特殊前缀

这将对不包括“(avg)”的所有字符串进行降序排序,然后它将按字符串长度排序,这样带有“(avg)”前缀的字符串将排在没有前缀的字符串之后

var result = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x => x.Length);

最终结果:

  • 泽塔
  • (平均)Zeta
  • 测试版
  • (平均)测试版
  • 阿尔法
  • (平均)阿尔法

【讨论】:

  • 我认为这不能保证“Zeta”出现在“(avg) Zeta”之前。
  • @jb,这将始终在“xxxx”之后返回“(avg) xxxx”
【解决方案2】:

这里有几种方法可以通过 LINQ 实现这一目标,同时如果它们以不同于您提供的顺序出现,也可以正确排序值。例如,如果“(avg) Zeta”出现在“Zeta”之前,那么后者在排序后仍应排在第一位。

这是示例列表,已重新排序以匹配我上面描述的内容:

var myUnsortedList = new List<string>
{
    "Alpha",
    "(avg) Alpha",
    "(avg) Zeta",
    "Zeta",
    "Beta",
    "(avg) Beta"
};

Lambda 语法

string prefix = "(avg)";
var result = myUnsortedList.Select(s => new
                           {
                               Value = s,
                               Modified = s.Replace(prefix, "").TrimStart(),
                               HasPrefix = s.StartsWith(prefix)
                           })
                           .OrderByDescending(o => o.Modified)
                           .ThenBy(o => o.HasPrefix)
                           .Select(o => o.Value);

压缩/聚合

string prefix = "(avg)";
var avg = myUnsortedList.Where(o => o.StartsWith(prefix))
                        .OrderByDescending(o => o);
var regular = myUnsortedList.Where(o => !o.StartsWith(prefix))
                            .OrderByDescending(o => o);
var result = regular.Zip(avg, (f, s) => new { First = f, Second = s })
                    .Aggregate(new List<string>(), (list, o) =>
                                   new List<string>(list) { o.First, o.Second });

查询语法和字符串拆分

这类似于 lambda 语法,除了我没有使用prefix 来确定哪个字符串有前缀。相反,我在一个空间上拆分,如果拆分结果有多个项目,那么我假设它有一个前缀。接下来,我根据值和前缀的可用性进行排序。

var result = from s in myUnsortedList
             let split = s.Split(' ')
             let hasPrefix = split.Length > 1
             let value = hasPrefix ? split[1] : s
             orderby value descending, hasPrefix
             select s;

【讨论】:

    【解决方案3】:

    将列表分成两个列表,一个正常,一个平均。 对它们进行排序。

    然后,进行手动“拉链合并”。

    【讨论】:

      【解决方案4】:

      您可能应该创建自己的自定义IComparer&lt;T&gt;

      class MyCustomComparer : IComparer<string>
      {
          private readonly StringComparison StringComparer;
      
          public static readonly MyCustomComparer Ordinal =
              new MyCustomComparer(StringComparison.Ordinal);
          public static readonly MyCustomComparer OrdinalIgnoreCase =
              new MyCustomComparer(StringComparison.OrdinalIgnoreCase);
          // etc.
      
          private MyCustomComparer(StringComparison stringComparer)
          {
              StringComparer = stringComparer;
          }
      
          public int Compare(string x, string y)  
          {  
              bool isMatchedX = IsMatchedPattern(x);
              bool isMatchedY = IsMatchedPattern(y);
      
              if (isMatchedX&& !isMatchedY ) // x matches the pattern.
              {
                  return String.Compare(Strip(x), y, StringComparer);
              }
              if (isMatchedY && !isMatchedX) // y matches the pattern.
              {
                  return String.Compare(Strip(y), x, StringComparer);
              }
      
              return String.Compare(x, y, StringComparison.Ordinal);
          }
      
          private static bool isMatchedPattern(string str)
          {
              // Use some way to return if it matches your pattern.
              // StartsWith, Contains, Regex, etc.
          }
      
          private static string Strip(string str)
          {
              // Use some way to return the stripped string.
              // Substring, Replace, Regex, etc.
          }
      }
      

      检查 xy 是否与您的模式匹配。如果两者都不做,则使用标准比较操作。基本上,如果一个(并且只有一个)匹配模式,您只需要自定义比较操作。

      如果 x 匹配模式而 y 不匹配,则剥离 x 并检查剥离后的 x i> 对 y 使用String.Compare(...) 操作。 如果 y 匹配模式而 x 不匹配,则剥离 y 并检查剥离版本的 y x 使用String.Compare(...) 操作。

      我更新了我的答案,以展示如何通过为大小写/区域性选项公开自定义比较器的静态只读实例来复制 StringComparison 的工作方式。

      最后,将 LINQ 与您的自定义比较器一起使用:myList.OrderBy(x =&gt; x, MyCustomComparer.Ordinal);


      最后一点...如有必要,请随时优化。这是我心血来潮的未经测试的代码。逻辑在那里,我希望。但是,可能出现了拼写错误。

      希望对您有所帮助。

      【讨论】:

        【解决方案5】:

        另一种方法是实现一些比较器,比如实现MyComparerIComparer&lt;string&gt;,然后:

        var result = myUnsortedList.OrderBy(x => x, new MyComparer());
        

        【讨论】:

          【解决方案6】:

          我觉得您为此使用了错误的数据结构。为什么不使用 SortedDictionary 并将其设为“name => avg”

          未经测试,可能工作的代码:

          SortedDictionary<string, int> dict = new SortedDictionary<string, int>();
          dict.Add("Alpha", 10);
          dict.Add("Beta", 20);
          dict.Add("Zeta", 30);
          
          foreach(string key in dict.Keys.Reverse())
          {
             int avg = dict[key];
          }
          

          【讨论】:

            【解决方案7】:

            要在 linq 排序中使用您自己的逻辑,您应该实现自己的比较器并将其实例用作 OrderByOrderByDescending linq 方法中的第二个参数,如下所示:

            namespace ConsoleApplication71
            {
                public class AVGComparer : IComparer<string>
                {
                    public int Compare(string x, string y)
                    {
                        // Null checkings are necessary to prevent null refernce exceptions
                        if((x == null) && (y == null)) return 0;
                        if(x == null) return -1;
                        if(y == null) return 1;
            
                        const string avg = @"(avg) ";
            
                        if(x.StartsWith(avg) || y.StartsWith(avg))
                        {
                            return x.Replace(avg, string.Empty).CompareTo(y.Replace(avg, string.Empty));
                        }
            
                        return x.CompareTo(y);
                    }
                }
            
                class Program
                {
                    static void Main(string[] args)
                    {
                        List<string> myUnsortedList = new List<string>();
            
                        myUnsortedList.Add("Alpha");
                        myUnsortedList.Add("(avg) Alpha");
                        myUnsortedList.Add("Zeta");
                        myUnsortedList.Add("Beta");
                        myUnsortedList.Add("(avg) Beta");
                        myUnsortedList.Add("(avg) Zeta");
            
                        var mySortedList = myUnsortedList.OrderByDescending(s => s, new AVGComparer());
            
                        foreach (string s in mySortedList)
                        {
                            Console.WriteLine(s);
                        }
                    }
                }
            }
            

            输出是:

            Zeta
            (avg) Zeta
            Beta
            (avg) Beta
            Alpha
            (avg) Alpha
            

            【讨论】:

              【解决方案8】:

              在一行中:

              var sorted = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x=> x.Contains("(avg)")).ToList();
              

              这是一个通过测试(nunit):

              [Test]
              public void CustomSort()
              {
                  var myUnsortedList = new List<string> { "Zeta", "Alpha", "(avg) Alpha", "Beta", "(avg) Beta", "(avg) Zeta" };
                  var EXPECTED_RESULT = new List<string> { "Zeta", "(avg) Zeta", "Beta", "(avg) Beta", "Alpha", "(avg) Alpha" };
              
                  var sorted = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x=> x.Contains("(avg)")).ToList();
              
                  for (int i = 0; i < myUnsortedList.Count; i++)
                  {
                      Assert.That(sorted[i], Is.EqualTo(EXPECTED_RESULT[i]));
                  }
              }
              

              【讨论】:

              • 如果例如“(avg) Zeta”在“Zeta”之前的列表中,这不会以正确的顺序返回它们,它还会编辑结果中的字符串(删除空格)跨度>
              • +1 谢谢,是的,这是预期结果的复制粘贴错误。现已修复
              猜你喜欢
              • 2013-01-12
              • 2012-12-08
              • 2018-05-17
              • 2019-04-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多