Menyortir IList di C #

87

Jadi saya menemukan masalah yang menarik hari ini. Kami memiliki layanan web WCF yang mengembalikan IList. Bukan masalah besar sampai saya ingin mengatasinya.

Ternyata antarmuka IList tidak memiliki metode pengurutan bawaan.

Saya akhirnya menggunakan ArrayList.Adapter(list).Sort(new MyComparer())metode ini untuk memecahkan masalah tetapi itu hanya tampak sedikit "ghetto" bagi saya.

Saya bermain-main dengan menulis metode ekstensi, juga dengan mewarisi dari IList dan menerapkan metode Sort () saya sendiri serta mentransmisikan ke Daftar tetapi tidak ada yang tampak terlalu elegan.

Jadi pertanyaan saya adalah, apakah ada yang punya solusi elegan untuk menyortir IList

lomaxx
sumber
Mengapa Anda mengembalikan IList sejak awal? Dari layanan WCF?
DaeMoohn

Jawaban:

55

Bagaimana jika menggunakan LINQ To Objects untuk mengurutkan untuk Anda?

Katakanlah Anda punya IList<Car>, dan mobil itu punya Engineproperti, saya yakin Anda bisa mengurutkan sebagai berikut:

from c in list
orderby c.Engine
select c;

Edit: Anda harus cepat mendapatkan jawaban di sini. Saat saya menyajikan sintaks yang sedikit berbeda dengan jawaban lain, saya akan meninggalkan jawaban saya - namun, jawaban lain yang disajikan sama-sama valid.

Brad Leach
sumber
3
Ini akan membuat enumerable baru, yang mungkin tidak diinginkan dalam beberapa skenario. Anda tidak dapat mengurutkan IList <T> di tempat melalui antarmuka kecuali dengan menggunakan metode ArrayList.Adapter dalam pengetahuan saya.
Tanveer Badar
69

Anda dapat menggunakan LINQ:

using System.Linq;

IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
Mark Cidade
sumber
61

Pertanyaan ini menginspirasi saya untuk menulis entri blog: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Saya pikir, idealnya, .NET Framework akan menyertakan metode penyortiran statis yang menerima IList <T>, tetapi hal terbaik berikutnya adalah membuat metode ekstensi Anda sendiri. Tidak terlalu sulit untuk membuat beberapa metode yang akan memungkinkan Anda mengurutkan IList <T> seperti yang Anda lakukan pada List <T>. Sebagai bonus, Anda dapat membebani metode ekstensi LINQ OrderBy menggunakan teknik yang sama, sehingga baik Anda menggunakan List.Sort, IList.Sort, atau IEnumerable.OrderBy, Anda dapat menggunakan sintaks yang sama persis.

public static class SortExtensions
{
    //  Sorts an IList<T> in place.
    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison));
    }

    // Sorts in IList<T> in place, when T is IComparable<T>
    public static void Sort<T>(this IList<T> list) where T: IComparable<T>
    {
        Comparison<T> comparison = (l, r) => l.CompareTo(r);
        Sort(list, comparison);

    }

    // Convenience method on IEnumerable<T> to allow passing of a
    // Comparison<T> delegate to the OrderBy method.
    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
    {
        return list.OrderBy(t => t, new ComparisonComparer<T>(comparison));
    }
}

// Wraps a generic Comparison<T> delegate in an IComparer to make it easy
// to use a lambda expression for methods that take an IComparer or IComparer<T>
public class ComparisonComparer<T> : IComparer<T>, IComparer
{
    private readonly Comparison<T> _comparison;

    public ComparisonComparer(Comparison<T> comparison)
    {
        _comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return _comparison(x, y);
    }

    public int Compare(object o1, object o2)
    {
        return _comparison((T)o1, (T)o2);
    }
}

Dengan ekstensi ini, sortir IList Anda seperti yang Anda lakukan pada Daftar:

IList<string> iList = new []
{
    "Carlton", "Alison", "Bob", "Eric", "David"
};

// Use the custom extensions:

// Sort in-place, by string length
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));

// Or use OrderBy()
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length));

Ada info lebih lanjut di pos: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

David Mills
sumber
1
Pendekatan yang tepat benar-benar akan menawarkan ISortableList<T>antarmuka (dengan metode untuk mengurutkan sebagian dari daftar menggunakan beberapa pembanding tertentu), telah List<T>menerapkannya, dan memiliki metode statis yang dapat mengurutkan apa pun IList<T>dengan memeriksa apakah diterapkan ISortableList<T>dan, jika tidak, menyalinnya ke array, menyortirnya, membersihkan IList<T>, dan menambahkan kembali item.
supercat
4
Jawaban yang luar biasa! Namun, peringatan: pendekatan ini mengasumsikan bahwa IList<T> listdapat dilemparkan ke IListantarmuka non-generik . Jika Anda membuat kode kelas Anda sendiri dengan mengimplementasikan IList<T>antarmuka, pastikan Anda juga menerapkan IListantarmuka non-generik , atau kode akan gagal dengan pengecualian cast kelas.
sstan
1
@supercat: Apa yang bisa ISortableList<T>ditawarkan yang belum ada IList<T>? Atau, jika ditanya secara berbeda, mengapa tidak IList<T>dapat disortir di tempat tanpa menambahkan kembali item dengan metode statis yang Anda bayangkan?
ATAU Mapper
@ORMapper: Jika sebuah daftar menggunakan sebuah array sebagai penyimpanan pendukung (umum, tetapi tidak wajib), rutinitas pengurutan yang mengakses elemen array secara langsung bisa jauh lebih cepat daripada yang harus melalui IList<T>antarmuka untuk mengakses setiap elemen. Perbedaan kecepatannya cukup besar sehingga dalam banyak kasus mungkin lebih cepat untuk menyalin daftar ke array, mengurutkan array, dan menyalin daftar kembali, daripada mencoba memiliki proses rutin pengurutan daftar di tempat.
supercat
1
The ComparisonComparerkelas tidak diperlukan. Anda dapat menggunakan metode statis standar Comparer<T>.Create(comparison)sebagai gantinya.
linepogl
9

Saya pikir Anda harus melakukan sesuatu seperti itu (mengubahnya menjadi tipe yang lebih konkret).

Mungkin membawanya ke dalam Daftar T daripada ArrayList, sehingga Anda mendapatkan keamanan tipe dan lebih banyak opsi untuk bagaimana Anda mengimplementasikan pembanding.

Leon Bambrick
sumber
4

Jawaban yang diterima oleh @DavidMills cukup bagus, tapi menurut saya bisa diperbaiki. Pertama, tidak perlu mendefinisikan ComparisonComparer<T>kelas ketika framework sudah menyertakan metode statis Comparer<T>.Create(Comparison<T>). Metode ini dapat digunakan untuk membuat IComparisonon the fly.

Selain itu, gips IList<T>untuk IListyang memiliki potensi untuk menjadi berbahaya. Dalam kebanyakan kasus yang saya lihat, List<T>implementasi IListyang digunakan di belakang layar untuk diterapkanIList<T> , tetapi ini tidak dijamin dan dapat menyebabkan kode rapuh.

Terakhir, List<T>.Sort()metode overload memiliki 4 tanda tangan dan hanya 2 yang diimplementasikan.

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

Kelas di bawah ini mengimplementasikan semua 4 List<T>.Sort()tanda tangan untuk IList<T>antarmuka:

using System;
using System.Collections.Generic;

public static class IListExtensions
{
    public static void Sort<T>(this IList<T> list)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort();
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort();
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparison);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparison);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparer);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparer);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, int index, int count,
        IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(index, count, comparer);
        }
        else
        {
            List<T> range = new List<T>(count);
            for (int i = 0; i < count; i++)
            {
                range.Add(list[index + i]);
            }
            range.Sort(comparer);
            Copy(range, 0, list, index, count);
        }
    }

    private static void Copy<T>(IList<T> sourceList, int sourceIndex,
        IList<T> destinationList, int destinationIndex, int count)
    {
        for (int i = 0; i < count; i++)
        {
            destinationList[destinationIndex + i] = sourceList[sourceIndex + i];
        }
    }
}

Pemakaian:

class Foo
{
    public int Bar;

    public Foo(int bar) { this.Bar = bar; }
}

void TestSort()
{
    IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 };
    IList<Foo> foos = new List<Foo>()
    {
        new Foo(1),
        new Foo(4),
        new Foo(5),
        new Foo(3),
        new Foo(2),
    };

    ints.Sort();
    foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar));
}

Idenya di sini adalah untuk memanfaatkan fungsionalitas yang mendasari List<T>untuk menangani penyortiran bila memungkinkan. Sekali lagi, sebagian besar IList<T>implementasi yang saya lihat menggunakan ini. Jika koleksi yang mendasari adalah jenis yang berbeda, mundur untuk membuat instance baru List<T>dengan elemen dari daftar masukan, gunakan untuk melakukan penyortiran, lalu salin hasilnya kembali ke daftar masukan. Ini akan bekerja bahkan jika daftar masukan tidak mengimplementasikan IListantarmuka.

dana
sumber
3
try this  **USE ORDER BY** :

   public class Employee
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

 private static IList<Employee> GetItems()
        {
            List<Employee> lst = new List<Employee>();

            lst.Add(new Employee { Id = "1", Name = "Emp1" });
            lst.Add(new Employee { Id = "2", Name = "Emp2" });
            lst.Add(new Employee { Id = "7", Name = "Emp7" });
            lst.Add(new Employee { Id = "4", Name = "Emp4" });
            lst.Add(new Employee { Id = "5", Name = "Emp5" });
            lst.Add(new Employee { Id = "6", Name = "Emp6" });
            lst.Add(new Employee { Id = "3", Name = "Emp3" });

            return lst;
        }

**var lst = GetItems().AsEnumerable();

            var orderedLst = lst.OrderBy(t => t.Id).ToList();

            orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));**
Dhanasekar
sumber
Bagaimana jika Anda menginginkannya menjadi DESC, bukan ASC?
sam byte
1

Menemukan utas ini ketika saya mencari solusi untuk masalah sebenarnya yang dijelaskan dalam posting asli. Namun, tidak ada jawaban yang sesuai dengan situasi saya sepenuhnya. Jawaban Brody cukup dekat. Inilah situasi saya dan solusi yang saya temukan untuk itu.

Saya memiliki dua IList dengan tipe yang sama yang dikembalikan oleh NHibernate dan telah memunculkan dua IList menjadi satu, oleh karena itu perlu penyortiran.

Seperti yang dikatakan Brody, saya menerapkan ICompare pada objek (ReportFormat) yang merupakan jenis IList saya:

 public class FormatCcdeSorter:IComparer<ReportFormat>
    {
       public int Compare(ReportFormat x, ReportFormat y)
        {
           return x.FormatCode.CompareTo(y.FormatCode);
        }
    }

Saya kemudian mengonversi IList yang digabungkan menjadi array dengan tipe yang sama:

ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList

Kemudian urutkan array:

Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer

Karena array satu dimensi mengimplementasikan antarmuka System.Collections.Generic.IList<T>, array tersebut dapat digunakan seperti IList asli.

John
sumber
1

Berguna untuk pengurutan grid, metode ini mengurutkan daftar berdasarkan nama properti. Seperti contohnya.

    List<MeuTeste> temp = new List<MeuTeste>();

    temp.Add(new MeuTeste(2, "ramster", DateTime.Now));
    temp.Add(new MeuTeste(1, "ball", DateTime.Now));
    temp.Add(new MeuTeste(8, "gimm", DateTime.Now));
    temp.Add(new MeuTeste(3, "dies", DateTime.Now));
    temp.Add(new MeuTeste(9, "random", DateTime.Now));
    temp.Add(new MeuTeste(5, "call", DateTime.Now));
    temp.Add(new MeuTeste(6, "simple", DateTime.Now));
    temp.Add(new MeuTeste(7, "silver", DateTime.Now));
    temp.Add(new MeuTeste(4, "inn", DateTime.Now));

    SortList(ref temp, SortDirection.Ascending, "MyProperty");

    private void SortList<T>(
    ref List<T> lista
    , SortDirection sort
    , string propertyToOrder)
    {
        if (!string.IsNullOrEmpty(propertyToOrder)
        && lista != null
        && lista.Count > 0)
        {
            Type t = lista[0].GetType();

            if (sort == SortDirection.Ascending)
            {
                lista = lista.OrderBy(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
            else
            {
                lista = lista.OrderByDescending(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
        }
    }
Bruno
sumber
0

Berikut contoh penggunaan pengetikan yang lebih kuat. Tidak yakin apakah itu cara terbaik.

static void Main(string[] args)
{
    IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 };
    List<int> stronglyTypedList = new List<int>(Cast<int>(list));
    stronglyTypedList.Sort();
}

private static IEnumerable<T> Cast<T>(IEnumerable list)
{
    foreach (T item in list)
    {
        yield return item;
    }
}

Fungsi Cast hanyalah implementasi ulang dari metode ekstensi yang dilengkapi dengan 3.5 yang ditulis sebagai metode statis normal. Sayangnya, ini cukup jelek dan bertele-tele.

ICR
sumber
0

Di VS2008, ketika saya mengklik referensi layanan dan memilih "Konfigurasi Referensi Layanan", ada opsi untuk memilih bagaimana klien membatalkan serialisasi daftar yang dikembalikan dari layanan.

Khususnya, saya dapat memilih antara System.Array, System.Collections.ArrayList dan System.Collections.Generic.List

Amy B
sumber
0
using System.Linq;

var yourList = SomeDAO.GetRandomThings();
yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );

Cantik sekali! Ghetto.


sumber
0

Menemukan postingan yang bagus tentang ini dan saya pikir saya akan membagikannya. Lihat disini

Pada dasarnya.

Anda dapat membuat kelas berikut dan Kelas IComparer

public class Widget {
    public string Name = string.Empty;
    public int Size = 0;

    public Widget(string name, int size) {
    this.Name = name;
    this.Size = size;
}
}

public class WidgetNameSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
        return x.Name.CompareTo(y.Name);
}
}

public class WidgetSizeSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
    return x.Size.CompareTo(y.Size);
}
}

Kemudian Jika Anda memiliki IList, Anda dapat mengurutkannya seperti ini.

List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

widgets.Sort(new WidgetNameSorter());
widgets.Sort(new WidgetSizeSorter());

Tapi lihat situs ini untuk informasi lebih lanjut ... Lihat DI SINI

Mizipzor
sumber
0

Apakah ini solusi yang valid?

        IList<string> ilist = new List<string>();
        ilist.Add("B");
        ilist.Add("A");
        ilist.Add("C");

        Console.WriteLine("IList");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

        List<string> list = (List<string>)ilist;
        list.Sort();
        Console.WriteLine("List");
        foreach (string val in list)
            Console.WriteLine(val);
        Console.WriteLine();

        list = null;

        Console.WriteLine("IList again");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

Hasilnya adalah: IList B A C

Daftar A B C

Nyalakan lagi A B C

Yoav
sumber
1
Valid jika itu benar-benar List <T>. Dalam beberapa kasus, Anda memiliki tipe lain yang mengimplementasikan IList <T> (misalnya, larik biasa) di mana downcast tidak akan berfungsi. Sayang sekali bahwa metode Sort () bukan merupakan metode ekstensi untuk IList <T>.
Cygon
0

Ini terlihat JAUH LEBIH SEDERHANA jika Anda bertanya kepada saya. Ini bekerja dengan SEMPURNA untuk saya.

Anda bisa menggunakan Cast () untuk mengubahnya menjadi IList kemudian menggunakan OrderBy ():

    var ordered = theIList.Cast<T>().OrderBy(e => e);

WHERE T adalah tipe mis. Model.Employee atau Plugin.ContactService.Shared.Contact

Kemudian Anda dapat menggunakan for loop dan SELESAI.

  ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }
Momodu Deen Swarray
sumber
-1

Ubah Anda IListmenjadi List<T>atau beberapa koleksi umum lainnya dan kemudian Anda dapat dengan mudah menanyakan / mengurutkan menggunakan System.Linqnamespace (ini akan menyediakan banyak metode ekstensi)

lubos hasko
sumber
9
IList<T>mengimplementasikan IEnumerable<T>dan oleh karena itu tidak perlu dikonversi untuk menggunakan operasi Linq.
Steve Guidi