C # List< > Sortuj wg x potem y

Podobnie jak Lista Kolejność alfabetyczna, chcemy sortować według jednego elementu, a następnie innego. chcemy osiągnąć funkcjonalny odpowiednik

SELECT * from Table ORDER BY x, y  

Mamy klasę, która zawiera wiele funkcji sortujących i nie mamy problemów sortujących według jednego elementu.
Na przykład:

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

List<MyClass> MyList;

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

I mamy na liście:

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

Stabilne sortowanie byłoby preferowane, ale nie wymagane. Rozwiązanie, które działa dla. Net 2.0 jest mile widziane.

Author: Community, 2008-11-14

6 answers

Pamiętaj, że nie potrzebujesz stabilnego sortowania, jeśli porównasz wszystkich członków. Rozwiązanie 2.0, zgodnie z życzeniem, może wyglądać tak:

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

Należy pamiętać, że to rozwiązanie 2.0 jest nadal preferowane w porównaniu z popularnym rozwiązaniem 3.5 Linq, wykonuje sortowanie w miejscu i nie ma wymogu O (n) przechowywania podejścia Linq. Chyba że wolisz, aby oryginalny obiekt listy był oczywiście nietknięty.

 98
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-05 01:56:59

Dla wersji. Net gdzie można użyć LINQ OrderBy i ThenBy (lub ThenByDescending w razie potrzeby):

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

Uwaga: dla. Net 2.0 (lub jeśli nie możesz użyć LINQ) Zobacz Hans Passant ODPOWIEDŹ na to pytanie.

 153
Author: Toby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:31

Musisz zaimplementować interfejs IComparer . Oto dobry post z przykładowym kodem.

 7
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-14 01:57:49

Sztuką jest zaimplementowanie stabilnego sortowania. Stworzyłem klasę widżetu, która może zawierać Twoje dane testowe:

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

Zaimplementowałem IComparable, więc można go nieusegregować według listy.Sort ().

Zaimplementowałem jednak również statyczną metodę Compare, którą można przekazać jako delegat do metody wyszukiwania.

Pożyczyłem tę metodę sortowania wstawiania od 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;
            }
    }

Umieściłbyś to w klasie pomocników, o której wspomniałeś w swoim pytanie.

Teraz, aby go użyć:

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

I wychodzi:

0:1
0:2
1:1
1:2
Press any key to continue . . .
To może być posprzątane przez anonimowych delegatów, ale zostawię to tobie.

EDIT : A NoBugz demonstruje moc anonymous methods...so, moim bardziej oldschoolowym: P

 5
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-14 02:27:12
 4
Author: Bala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-02 06:52:03

Miałem problem, w którym OrderBy i ThenBy nie dały mi pożądanego rezultatu (lub po prostu nie wiedziałem, jak je poprawnie wykorzystać).

Poszedłem z listą.Sortuj rozwiązanie coś takiego.
    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)));
 1
Author: mofoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-16 16:09:07