Sortowanie IList w C#

Więc natknąłem się dziś na ciekawy problem. Mamy usługę WCF web, która zwraca IList. Nic wielkiego, dopóki nie chciałem tego uporządkować.

Okazuje się, że interfejs IList nie ma wbudowanej metody sortowania.

Skończyłem używając metody ArrayList.Adapter(list).Sort(new MyComparer()) do rozwiązania problemu, ale wydawało mi się to trochę "Getto".

Bawiłem się pisaniem metody rozszerzenia, również dziedziczeniem z IList i implementowaniem własnej metody Sort (), a także rzutowaniem na listę, ale żaden z nich nie wydawał się zbyt elegancki.

Więc moje pytanie brzmi, czy ktoś ma eleganckie rozwiązanie do sortowania IList

Author: lomaxx, 2008-08-19

14 answers

Może użyjesz LINQ do sortowania obiektów?

Powiedzmy, że masz IList<Car>, a samochód miał Engine własność, myślę, że można sortować w następujący sposób:

from c in list
orderby c.Engine
select c;

Edit: musisz być szybki, aby uzyskać odpowiedzi tutaj. Ponieważ przedstawiłem nieco inną składnię niż pozostałe odpowiedzi, pozostawiam swoją odpowiedź - jednak pozostałe odpowiedzi są równie ważne.

 52
Author: Brad Leach,
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-08-19 01:41:02

Możesz użyć LINQ:

using System.Linq;

IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
 58
Author: Mark Cidade,
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-08-19 01:34:07

To pytanie zainspirowało mnie do napisania posta na blogu: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Myślę, że najlepiej byłoby, gdyby. NET Framework zawierał statyczną metodę sortowania, która akceptuje IList, ale następną najlepszą rzeczą jest stworzenie własnej metody rozszerzenia. Nie jest zbyt trudno stworzyć kilka metod, które pozwolą Ci posortować IListtak, jak lista. Jako bonus możesz przeciążać metodę rozszerzenia LINQ OrderBy używając ta sama technika, więc czy używasz List.Sortuj, IList.Sort, or IEnumerable.OrderBy, możesz użyć dokładnie tej samej składni.

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

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

Z tymi rozszerzeniami posortuj listę IList tak, jak lista:

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

Więcej info w poście: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

 52
Author: David Mills,
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
2011-02-18 15:39:47

Będziesz musiał zrobić coś takiego jak myślę (przekonwertować to na bardziej konkretny typ).

Może weź to do listy T, a nie ArrayList, aby uzyskać Bezpieczeństwo typu i więcej opcji, jak zaimplementować comparer.

 9
Author: Leon Bambrick,
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-08-19 01:29:10

Przyjęta odpowiedź przez @ DavidMills jest całkiem dobra, ale myślę, że można ją poprawić. Po pierwsze, nie ma potrzeby definiowania klasy ComparisonComparer<T>, gdy framework zawiera już statyczną metodę Comparer<T>.Create(Comparison<T>). Ta metoda może być używana do tworzenia IComparison w locie.

Również rzuca IList<T> do IList, co może być niebezpieczne. W większości przypadków, które widziałem, {[7] } który implementuje IList jest używany za kulisami do implementacji IList<T>, ale nie jest to gwarantowane i może prowadzić do łamliwy Kodeks.

Wreszcie, przeciążona metoda List<T>.Sort() mA 4 sygnatury i tylko 2 z nich są zaimplementowane.

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

Poniższa klasa implementuje wszystkie 4 List<T>.Sort() sygnatury dla IList<T> interfejsu:

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

Użycie:

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

Chodzi o wykorzystanie funkcjonalności bazowej List<T> do obsługi sortowania w miarę możliwości. Znowu większość IList<T> implementacji, które mam / align = "left" / W przypadku, gdy bazowa kolekcja jest innego typu, należy utworzyć nową instancję List<T> z elementami z listy wejściowej, użyć jej do sortowania, a następnie skopiować wyniki z powrotem do listy wejściowej. Będzie to działać, nawet jeśli lista wejściowa nie implementuje interfejsu IList.

 4
Author: dana,
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
2018-05-05 19:49:58

Przekonwertuj IList Na List<T> lub inną kolekcję generyczną, a następnie możesz łatwo odpytywać/sortować ją za pomocą przestrzeni nazw System.Linq (dostarczy kilka metod rozszerzenia)

 2
Author: lubos hasko,
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
2015-09-20 12:41:19

Znalazłem ten wątek, gdy szukałem rozwiązania dokładnego problemu opisanego w oryginalnym poście. Żadna z odpowiedzi nie spotkała się jednak całkowicie z moją sytuacją. Odpowiedź Brody ' ego była bliska. Oto moja sytuacja i rozwiązanie, które znalazłem.

Mam dwie Ilisty tego samego typu zwrócone przez NHibernate i powstały dwie Ilisty w jedną, stąd potrzeba sortowania.

Jak powiedział Brody zaimplementowałem ICompare na obiekcie (ReportFormat) , który jest typem mojego IList: {]}

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

Następnie konwertuję scaloną IList do tablicy tego samego typu:

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

Następnie posortuj tablicę:

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

Ponieważ tablica jednowymiarowa implementuje interfejs System.Collections.Generic.IList<T>, tablica może być używana tak jak oryginalna IList.

 1
Author: John,
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-07-13 22:20:19

Przydatne do sortowania siatkowego ta metoda sortuje listę według nazw właściwości. Jak za przykładem.

    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();
            }
        }
    }
 1
Author: Bruno,
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-08 13:55:04

Oto przykład użycia silniejszego typowania. Nie wiem, czy to najlepszy sposób.

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

Funkcja Cast jest tylko reimplementacją metody rozszerzenia, która pochodzi z 3.5 zapisaną jako normalna metoda statyczna. Jest dość brzydki i gadatliwy niestety.

 0
Author: ICR,
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-08-19 11:38:30

W VS2008, kiedy klikam na referencję usługi i wybieram "Configure Service Reference", jest opcja wyboru sposobu, w jaki klient de-serializuje listy zwrócone z usługi.

W szczególności mogę wybierać pomiędzy systemem./ Align = "Left" / Kolekcje.ArrayList i System.Kolekcje.Ogólne.Lista

 0
Author: Amy B,
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-09-17 13:33:55
using System.Linq;

var yourList = SomeDAO.GetRandomThings();
yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );
To jest piękne !Getto.
 0
Author: ,
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
2009-07-06 13:50:17

Znalazłem dobry post na ten temat i pomyślałem, że się podzielę. Sprawdź to tutaj

W zasadzie.

Możesz utworzyć następujące klasy I klasy 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);
}
}

Wtedy, jeśli masz Ilistę, możesz to sortować w ten sposób.

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

Ale sprawdź tę stronę, aby uzyskać więcej informacji... Sprawdź to tutaj

 0
Author: Mizipzor,
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-08-23 09:14:26

Czy jest to poprawne rozwiązanie?

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

Wynik był: IList B A C

Lista A B C

IList again A B C

 0
Author: Yoav,
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-09-11 14:10:14
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));**
 0
Author: Dhanasekar,
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
2012-08-31 08:02:56