排序算法- c#

本文关键字:算法 排序 | 更新日期: 2023-09-27 17:49:39

我有以下未排序的列表:

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来完成排序

排序算法- c#

假设"(avg)"是唯一的特殊前缀

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

的字符串之后。
var result = myUnsortedList.OrderByDescending(x => x.Replace("(avg) ", "")).ThenBy(x => x.Length);

最终结果:

  • ζ
  • (avg)ζ
  • β
  • (avg)β
  • α
  • (avg)α

这里有几种方法可以用LINQ实现这一点,同时如果它们以不同于您所呈现的顺序出现,也可以正确地对值进行排序。例如,如果"(avg) Zeta"出现在"Zeta"之前,则在排序后,后者仍应优先出现。

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

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

λ语法
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);

Zip/Aggregate

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;

将列表分成两个列表,一个是正常列表,一个是平均列表。把它们都排序。

然后,执行手动"拉链合并"。

您应该创建自己的自定义IComparer<T>:

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并使用String.Compare(...)操作检查x的剥离版本与y的对比。如果y匹配模式,而x不匹配,则剥离y,并使用String.Compare(...)操作对照x检查剥离后的y版本。

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

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


最后一点…如果有必要,可以随意对其进行优化。这是我心血来潮的未经测试的代码。我希望逻辑是这样的。但是,可能出现了拼写错误。

希望对你有帮助。

另一种方法是实现一些比较器,比如MyComparer,它实现IComparer<string>,然后:

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

我觉得你使用了错误的数据结构。为什么不使用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];
}

要在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

一行:

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