简体   繁体   English

排序算法 - C#

[英]Sorting Algorithm - C#

I have the following unsorted list: 我有以下未排序的列表:

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

I want to sort the list descending alphabetical order, then have the value with (avg) right after the normal value: 我想按字母顺序降序排序,然后在正常值之后使用(avg)值:

Final Result: Zeta, (avg) Zeta, Beta, (avg) Beta, Alpha, (avg) Alpha 最终结果:Zeta,(平均)Zeta,Beta,(平均)Beta,Alpha,(平均)Alpha

My application is written in C# and I want to use LINQ to accomplish the sorting 我的应用程序是用C#编写的,我想使用LINQ来完成排序

This should work ok for what you need, assuming "(avg)" is the only special prefix 这应该适用于你需要的东西,假设“(avg)”是唯一的特殊前缀

This will order all the stings descending not including the "(avg) " then it will order by the strings length this way the string with the "(avg)" prefix will come after the one without 这将命令所有下降的顺序不包括“(平均)”然后它将按字符串长度排序这样具有“(avg)”前缀的字符串将在没有

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

Final Result: 最后结果:

  • Zeta 泽塔
  • (avg) Zeta (平均)泽塔
  • Beta Beta版
  • (avg) Beta (平均)Beta
  • Alpha Α
  • (avg) Alpha (平均)阿尔法

Here are a couple of ways to pull this off with LINQ, while also correctly sorting the values should they occur in an order other than the one you've presented. 这里有几种方法可以使用LINQ来解决这个问题,同时如果值按照您提供的顺序排列,也可以正确排序。 For example, if "(avg) Zeta" occurs before "Zeta" then the latter should still come first once sorted. 例如,如果“(avg)Zeta”出现在“Zeta”之前,则后者应该在排序后首先出现。

Here's the sample list, reordered to match what I described above: 这是样本列表,重新排序以匹配我上面描述的内容:

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

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

Zip / Aggregate 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 });

Query syntax and string splitting 查询语法和字符串拆分

This one is similar to the lambda syntax, except I'm not using the prefix to determine which string has a prefix. 这个类似于lambda语法,除了我没有使用prefix来确定哪个字符串有前缀。 Instead, I am splitting on a space, and if the split result has more than one item then I'm assuming that it has a prefix. 相反,我正在拆分空间,如果拆分结果有多个项目,那么我假设它有一个前缀。 Next, I order based on the value and the prefix's availability. 接下来,我根据值和前缀的可用性进行排序。

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;

Split the lists into two lists, one normal, one average. 将列表拆分为两个列表,一个是普通列表,一个是平均值。 Sort them both. 对它们进行排序。

Then, do a manual "Zipper Merge". 然后,做一个手册“Zipper Merge”。

You should probably create your own custom IComparer<T> : 你应该创建自己的自定义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.
    }
}

Check to see if x and y match your pattern. 检查xy是否与您的模式匹配。 If neither or both do, then use a standard comparison operation. 如果两者都没有,则使用标准比较操作。 Basically, you only need the custom comparison operation if one (and only one) matches the pattern. 基本上,如果一个(并且只有一个)匹配模式,则只需要自定义比较操作。

If x matches the pattern and y doesn't, then strip x and check the stripped version of x against y using the String.Compare(...) operation. 如果x模式匹配和y不,然后剥去x和使用所述检查x的剥离版本针对Ý String.Compare(...)操作。 If y matches the pattern and x doesn't, then strip y and check the stripped version of y against x using the String.Compare(...) operation. 如果y的模式匹配,并且x没有,那么剥离y和使用所述检查y的剥离版本针对X String.Compare(...)操作。

I updated my answer to show how you can copy the way StringComparison works by exposing static readonly instances of the custom comparer for case/culture options. 我更新了我的答案,以展示如何通过为case / culture选项公开自定义比较器的静态只读实例来复制StringComparison工作方式。

Finally, use LINQ with your custom comparer: myList.OrderBy(x => x, MyCustomComparer.Ordinal); 最后,将LINQ与自定义比较器一起使用: myList.OrderBy(x => x, MyCustomComparer.Ordinal);


One final note... feel free to optimize this if necessary. 最后一点说明......如有必要,请随意优化。 This is untested code just off the whim of my mind. 这是我心中想到的未经测试的代码。 The logic is there, I hope. 我希望,逻辑在那里。 But, typos might have occurred. 但是,可能已经发生过拼写错误。

Hope that helps. 希望有所帮助。

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

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

I feel like you're using the wrong data structure for this. 我觉得你正在使用错误的数据结构。 Why don't you use a SortedDictionary and make it be "name => avg" 为什么不使用SortedDictionary并使其成为“name => avg”

untested, probably working code: 未经测试的,可能正在运行的代码:

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

To use Your own logic in linq ordering You should implement Your own Comparer and use it's instance as second parameter in OrderBy or OrderByDescending linq method like below: 在linq排序中使用您自己的逻辑您应该实现自己的Comparer并将其实例用作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);
            }
        }
    }
}

The output is: 输出是:

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

In a line: 在一行:

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

Here is a passing test (nunit): 这是一个通过测试(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]));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM