C#List <>按x然后按y排序


88

类似于List <> OrderBy按字母顺序Order,我们要按一个元素排序,然后再按另一个元素排序。我们想要达到以下功能

SELECT * from Table ORDER BY x, y  

我们有一个包含许多排序功能的类,并且按一个元素排序没有问题。
例如:

public class MyClass {
    public int x;
    public int y;
}  

List<MyClass> MyList;

public void SortList() {
    MyList.Sort( MySortingFunction );
}

我们在列表中有以下内容:

Unsorted     Sorted(x)     Desired
---------    ---------    ---------
ID   x  y    ID   x  y    ID   x  y
[0]  0  1    [2]  0  2    [0]  0  1
[1]  1  1    [0]  0  1    [2]  0  2
[2]  0  2    [1]  1  1    [1]  1  1
[3]  1  2    [3]  1  2    [3]  1  2

稳定的排序将是可取的,但不是必需的。欢迎使用适用于.Net 2.0的解决方案。


@Bolu我已经明确删除了标签,以使发布版本不可知,并更新了答案以匹配该标签。如果您认为4.0 / 2.0不够突出,请考虑在问题中进行清晰的编辑,而不是还原标签。
阿列克谢·莱文科夫2014年

抱歉@AlexeiLevenkov,没有引起足够的重视,请随时回滚。
博卢2014年

好。恢复了更改。
Alexei Levenkov 2014年

这个问题已更新,涵盖了最初仅2.0版本的.Net的所有版本-包含针对不同框架和要求的几种替代答案-检查所有内容以查看哪种更适合您的要求。
阿列克谢·莱文科夫2014年

Answers:


98

请记住,如果比较所有成员,则不需要稳定的排序。根据要求,2.0解决方案可以如下所示:

 public void SortList() {
     MyList.Sort(delegate(MyClass a, MyClass b)
     {
         int xdiff = a.x.CompareTo(b.x);
         if (xdiff != 0) return xdiff;
         else return a.y.CompareTo(b.y);
     });
 }

请注意,此2.0解决方案仍然比流行的3.5 Linq解决方案更可取,它执行就地排序,并且不具有Linq方法的O(n)存储要求。除非您当然希望原始的List对象保持不变。


156

对于可以使用LINQ OrderBy和.NET的版本ThenByThenByDescending如果需要):

using System.Linq;
....
List<SomeClass>() a;
List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList();

注意:对于.Net 2.0(或如果您不能使用LINQ),请参阅Hans Passant对这个问题的回答。


2
来自phoog的另一个答案文章位于:stackoverflow.com/questions/9285426/… 它创建了另一个列表,其中的原始项目以新的顺序排列。仅在需要出于其他目的保留原始顺序时,此功能才有用。比对列表进行排序更浪费内存
dreamerkumar 2013年


5

诀窍是实现稳定的排序。我创建了一个Widget类,其中可以包含您的测试数据:

public class Widget : IComparable
{
    int x;
    int y;
    public int X
    {
        get { return x; }
        set { x = value; }
    }

    public int Y
    {
        get { return y; }
        set { y = value; }
    }

    public Widget(int argx, int argy)
    {
        x = argx;
        y = argy;
    }

    public int CompareTo(object obj)
    {
        int result = 1;
        if (obj != null && obj is Widget)
        {
            Widget w = obj as Widget;
            result = this.X.CompareTo(w.X);
        }
        return result;
    }

    static public int Compare(Widget x, Widget y)
    {
        int result = 1;
        if (x != null && y != null)                
        {                
            result = x.CompareTo(y);
        }
        return result;
    }
}

我实现了IComparable,因此它可能会被List.Sort()不稳定地排序。

但是,我还实现了静态方法Compare,可以将其作为委托传递给搜索方法。

我从C#411借用了这个插入排序方法:

 public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison)
        {           
            int count = list.Count;
            for (int j = 1; j < count; j++)
            {
                T key = list[j];

                int i = j - 1;
                for (; i >= 0 && comparison(list[i], key) > 0; i--)
                {
                    list[i + 1] = list[i];
                }
                list[i + 1] = key;
            }
    }

您可以将其放入您在问题中提到的排序助手类。

现在,使用它:

    static void Main(string[] args)
    {
        List<Widget> widgets = new List<Widget>();

        widgets.Add(new Widget(0, 1));
        widgets.Add(new Widget(1, 1));
        widgets.Add(new Widget(0, 2));
        widgets.Add(new Widget(1, 2));

        InsertionSort<Widget>(widgets, Widget.Compare);

        foreach (Widget w in widgets)
        {
            Console.WriteLine(w.X + ":" + w.Y);
        }
    }

它输出:

0:1
0:2
1:1
1:2
Press any key to continue . . .

可以使用一些匿名代表来清除此问题,但我将由您自己决定。

编辑:和NoBugz演示了匿名方法的力量...所以,请考虑我更老派:P



1

我遇到一个问题,即OrderBy和ThenBy没有给我想要的结果(或者我只是不知道如何正确使用它们)。

我列出了一个清单,对这样的解决方案进行排序。

    var data = (from o in database.Orders Where o.ClientId.Equals(clientId) select new {
    OrderId = o.id,
    OrderDate = o.orderDate,
    OrderBoolean = (SomeClass.SomeFunction(o.orderBoolean) ? 1 : 0)
    });

    data.Sort((o1, o2) => (o2.OrderBoolean.CompareTo(o1.OrderBoolean) != 0
    o2.OrderBoolean.CompareTo(o1.OrderBoolean) : o1.OrderDate.Value.CompareTo(o2.OrderDate.Value)));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.