C # List <> Сортировать по x, затем y

88

Подобно List <> OrderBy Alphabetical 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 недостаточно заметны, попробуйте внести уточняющие изменения в вопрос вместо восстановления тега.
Алексей Левенков
Извините, @AlexeiLevenkov, не обратил особого внимания, пожалуйста, откатывайтесь назад.
Bolu
ОК. Отменил изменение.
Алексей Левенков
Этот вопрос был обновлен, чтобы охватить все версии .Net, начиная с оригинальной версии 2.0 - содержит несколько альтернативных ответов для различных фреймворков и требований - просмотрите все, чтобы узнать, какая из них лучше соответствует вашим требованиям.
Алексей Левенков

Ответы:

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 по-прежнему предпочтительнее популярного решения Linq 3.5, оно выполняет сортировку на месте и не требует хранения O (n), как подход Linq. Если, конечно, вы не хотите, чтобы исходный объект List оставался нетронутым.

Ганс Пассан
источник
156

Для версий .Net, где вы можете использовать LINQ OrderByи ThenBy(или ThenByDescendingпри необходимости):

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

Примечание: для .Net 2.0 (или если вы не можете использовать LINQ) см. Ответ Ханса Пассанта на этот вопрос.

Тоби
источник
2
Из другого ответного сообщения от phoog здесь: stackoverflow.com/questions/9285426/… Он создает еще один список с исходными элементами в новом порядке. Это полезно только в том случае, если вам нужно сохранить исходный порядок для других целей; это гораздо более расточительно памяти, чем сортировка списка на месте
dreamerkumar
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

FlySwat
источник
1

У меня была проблема, из-за которой OrderBy и ThenBy не давали мне желаемого результата (или я просто не знал, как их правильно использовать).

Я пошел с решением list.Sort примерно так.

    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)));
тупица
источник