C # List <> Urutkan berdasarkan x lalu y

88

Mirip dengan List <> OrderBy Alphabetical Order , kami ingin mengurutkan berdasarkan satu elemen, lalu elemen lainnya. kami ingin mencapai kesetaraan fungsional

SELECT * from Table ORDER BY x, y  

Kami memiliki kelas yang berisi sejumlah fungsi pengurutan, dan kami tidak memiliki masalah pengurutan berdasarkan satu elemen.
Sebagai contoh:

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

List<MyClass> MyList;

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

Dan kami memiliki yang berikut ini dalam daftar:

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

Jenis yang stabil akan lebih disukai, tetapi tidak diharuskan. Solusi yang berfungsi untuk .Net 2.0 diterima.

Byron Ross
sumber
@Bolu Saya telah secara eksplisit menghapus tag untuk membuat versi posting agnostik dan memperbarui jawaban untuk mencocokkannya. Pertimbangkan untuk membuat pengeditan klarifikasi pada pertanyaan daripada memulihkan tag jika menurut Anda 4.0 / 2.0 tidak cukup menonjol.
Alexei Levenkov
Maaf @AlexeiLevenkov, tidak terlalu memperhatikan, silakan putar kembali.
Bolu
BAIK. Mengembalikan perubahan.
Alexei Levenkov
Pertanyaan ini telah diperbarui untuk mencakup semua versi .Net dari aslinya hanya 2.0 - berisi beberapa jawaban alternatif untuk kerangka kerja dan persyaratan yang berbeda - periksa semua untuk melihat mana yang lebih sesuai dengan kebutuhan Anda.
Alexei Levenkov

Jawaban:

98

Ingatlah bahwa Anda tidak memerlukan jenis yang stabil jika Anda membandingkan semua anggota. Solusi 2.0, seperti yang diminta, dapat terlihat seperti ini:

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

Perhatikan bahwa solusi 2.0 ini masih lebih disukai daripada solusi 3.5 Linq yang populer, ia melakukan pengurutan di tempat dan tidak memiliki persyaratan penyimpanan O (n) dari pendekatan Linq. Kecuali jika Anda lebih suka objek List asli tidak tersentuh tentunya.

Hans Passant
sumber
156

Untuk versi .Net tempat Anda dapat menggunakan LINQ OrderBydan ThenBy(atau ThenByDescendingjika perlu):

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

Catatan: untuk .Net 2.0 (atau jika Anda tidak dapat menggunakan LINQ) lihat jawaban Hans Passant untuk pertanyaan ini.

Toby
sumber
2
Dari jawaban lain dikirim oleh phoog di sini: stackoverflow.com/questions/9285426/… Ini membuat daftar lain dengan item asli dalam urutan baru. Ini hanya berguna jika Anda perlu mempertahankan pemesanan asli untuk tujuan lain; ini lebih membuang-buang memori daripada menyortir daftar pada tempatnya
dreamerkumar
5

Triknya adalah dengan menerapkan semacam stabil. Saya telah membuat kelas Widget yang dapat berisi data pengujian Anda:

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

Saya menerapkan IComparable, sehingga dapat diurutkan secara tidak stabil berdasarkan List.Sort ().

Namun, saya juga menerapkan metode Bandingkan statis, yang dapat dikirimkan sebagai delegasi ke metode pencarian.

Saya meminjam metode penyisipan semacam ini dari 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;
            }
    }

Anda akan memasukkan ini ke dalam kelas penolong yang Anda sebutkan dalam pertanyaan Anda.

Sekarang, untuk menggunakannya:

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

Dan itu menghasilkan:

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

Ini mungkin bisa dibersihkan dengan beberapa delegasi anonim, tapi saya serahkan pada Anda.

EDIT : Dan NoBugz mendemonstrasikan kekuatan metode anonim ... jadi, pertimbangkan sekolah saya yang lebih tua: P

FlySwat
sumber
1

Saya mengalami masalah di mana OrderBy dan ThenBy tidak memberi saya hasil yang diinginkan (atau saya hanya tidak tahu cara menggunakannya dengan benar).

Saya pergi dengan daftar. Solusi sortir seperti ini.

    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)));
mofoo
sumber