C # Список <> Сортувати за x, потім y


88

Подібно до списку <> Порядок За алфавітним порядком ми хочемо сортувати за одним елементом, а потім за іншим. ми хочемо досягти функціонального еквівалента

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, не звернув особливої ​​уваги, будь ласка, відмовляйтеся.
Болу

В ПОРЯДКУ. Скасовано зміни.
Олексій Левенков

Це питання було оновлено, щоб охопити всі версії .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 все-таки переважніше за популярне рішення 3.5 Linq, воно виконує сортування на місці і не вимагає вимог щодо зберігання 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

Фокус полягає у реалізації стабільного сортування. Я створив клас віджетів, який може містити ваші тестові дані:

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 . . .

Можливо, це можна було б прибрати за допомогою анонімних делегатів, але я залишу це за вами.

EDIT : І 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)));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.