C#でIListを並べ替える


86

それで、今日、私は興味深い問題に遭遇しました。IListを返すWCFWebサービスがあります。並べ替えるまでは大したことではありません。

IListインターフェイスにはsortメソッドが組み込まれていないことがわかりました。

私はArrayList.Adapter(list).Sort(new MyComparer())問題を解決するためにこの方法を使用することになりましたが、それは私には少し「ゲットー」のように見えました。

私は拡張メソッドを作成し、IListから継承し、独自のSort()メソッドを実装し、リストにキャストすることを楽しみましたが、これらはどれも過度にエレガントに見えませんでした。

だから私の質問は、誰かがIListをソートするためのエレガントな解決策を持っていますか?


そもそもなぜIListを返すのですか?WCFサービスから?
DaeMoohn 2011

回答:


54

LINQ To Objectsを使用して並べ替えてみませんか?

IList<Car>あり、車にEngineプロパティがあるとすると、次のように並べ替えることができると思います。

from c in list
orderby c.Engine
select c;

編集:ここで答えを得るには迅速である必要があります。他の回答とは少し異なる構文を提示したので、回答は残しておきますが、提示された他の回答も同様に有効です。


3
新しい列挙型が作成されますが、シナリオによっては望ましくない場合があります。私の知る限り、ArrayList.Adapterメソッドを使用しない限り、インターフェイスを介してIList <T>をインプレースで並べ替えることはできません。
Tanveer Badar 2014

67

LINQを使用できます:

using System.Linq;

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

61

この質問は私にブログ投稿を書くように促しました:http//blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

理想的には、.NETFrameworkにはIList <T>を受け入れる静的な並べ替えメソッドが含まれていると思いますが、次善の策は独自の拡張メソッドを作成することです。List <T>のようにIList <T>をソートできるようにするメソッドをいくつか作成するのはそれほど難しくありません。ボーナスとして、同じ手法を使用してLINQ OrderBy拡張メソッドをオーバーロードできるため、List.Sort、IList.Sort、またはIEnumerable.OrderByのいずれを使用していても、まったく同じ構文を使用できます。

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

これらの拡張機能を使用して、リストと同じようにIListを並べ替えます。

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

投稿に詳細があります:http//blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/


1
正しいアプローチは、実際にはISortableList<T>インターフェイスを提供し(特定の比較子を使用してリストの一部を並べ替えるメソッドを使用)、それを実装し、実装されているかどうかを確認してList<T>並べ替えることができる静的メソッドを用意するIList<T>ことISortableList<T>でした。それを配列にコピーし、それをソートし、をクリアしてIList<T>、アイテムを再度追加します。
スーパーキャット2012

4
素晴らしい答え!ただし、注意点:このアプローチは、IList<T> listが非ジェネリックIListインターフェイスにキャストできることを前提としています。IList<T>インターフェイスを実装する独自のクラスをコーディングする場合は、非ジェネリックIListインターフェイスも実装するようにしてください。そうしないと、クラスキャスト例外でコードが失敗します。
sstan 2015

1
@supercat:ISortableList<T>まだ入っていないものを提供できるIList<T>でしょうか?または、別の質問をすると、IList<T>想像した静的メソッドでアイテムを再度追加せずに、その場で並べ替えることができなかったのはなぜですか?
またはマッパー

@ORMapper:リストが配列をバッキングストアとして使用する場合(一般的ですが必須ではありません)、配列要素に直接アクセスする並べ替えルーチンは、IList<T>すべての要素にアクセスするためにインターフェイスを経由する必要があるものよりもはるかに高速です。速度の差は十分に大きいため、多くの場合、並べ替えルーチンでリストを適切に処理するよりも、リストを配列にコピーし、配列を並べ替えて、リストをコピーして戻す方が高速です。
スーパーキャット

1
ComparisonComparerクラスは必要ありません。Comparer<T>.Create(comparison)代わりに、標準の静的メソッドを使用できます。
linepogl

9

あなたは私が思うようなことをしなければならないでしょう(それをより具体的なタイプに変換してください)。

たぶん、それをArrayListではなくTのリストに入れて、型の安全性と、比較子の実装方法に関するより多くのオプションを取得できるようにします。


4

@DavidMillsが受け入れた回答はかなり良いですが、改善できると思います。1つはComparisonComparer<T>、フレームワークに静的メソッドがすでに含まれている場合、クラスを定義する必要はありませんComparer<T>.Create(Comparison<T>)。このメソッドはIComparison、オンザフライで作成するために使用できます。

また、危険な可能性のあるキャストにキャストIList<T>IListます。私が見たほとんどの場合、List<T>実装IListは舞台裏で使用されて実装されますIList<T>が、これは保証されておらず、脆弱なコードにつながる可能性があります。

最後に、オーバーロードされたList<T>.Sort()メソッドには4つのシグニチャがあり、そのうち2つだけが実装されています。

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

以下のクラスList<T>.Sort()は、IList<T>インターフェースの4つの署名すべてを実装します。

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

使用法:

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

ここでの考え方は、基礎となる機能を活用して、List<T>可能な限りソートを処理することです。繰り返しますが、IList<T>私が見たほとんどの実装はこれを使用しています。基になるコレクションが異なるタイプの場合はList<T>、入力リストの要素を使用しての新しいインスタンスの作成にフォールバックし、それを使用して並べ替えを行い、結果を入力リストにコピーして戻します。これは、入力リストがIListインターフェイスを実装していない場合でも機能します。


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

1

元の投稿で説明されている正確な問題の解決策を探していたときに、このスレッドを見つけました。しかし、どの答えも私の状況に完全には適合しませんでした。ブロディの答えはかなり近いものでした。これが私の状況と私が見つけた解決策です。

NHibernateから返された同じタイプのIListが2つあり、2つのIListが1つになっているため、並べ替えが必要です。

Brodyが言ったように、IListのタイプであるオブジェクト(ReportFormat)にICompareを実装しました。

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

次に、マージされたIListを同じタイプの配列に変換します。

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

次に、配列を並べ替えます。

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

1次元配列はインターフェイスを実装してSystem.Collections.Generic.IList<T>いるため、元のIListと同じように配列を使用できます。


1

このメソッドは、プロパティ名に基づいてリストを並べ替えるグリッド並べ替えに役立ちます。例に従ってください。

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

0

これは、より強力なタイピングを使用した例です。しかし、それが必ずしも最善の方法であるかどうかはわかりません。

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

Cast関数は、通常の静的メソッドとして記述された3.5に付属する拡張メソッドの単なる再実装です。残念ながら、これは非常に醜く冗長です。


0

VS2008では、サービス参照をクリックして[サービス参照の構成]を選択すると、クライアントがサービスから返されたリストを逆シリアル化する方法を選択するオプションがあります。

特に、System.Array、System.Collections.ArrayList、System.Collections.Generic.Listから選択できます。


0
using System.Linq;

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

それはかなり!ゲットーです。


0

これに関する良い投稿を見つけて、私が共有したいと思いました。こちらをご覧ください

基本的に。

次のクラスと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);
}
}

次に、IListがある場合は、次のように並べ替えることができます。

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

しかし、詳細についてはこのサイトをチェックアウトしてください...ここでそれをチェックしてください


0

これは有効な解決策ですか?

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

結果は次のとおりです。IListBAC

リストAB C

再びIListA B C


1
それが本当にList <T>である場合に有効です。場合によっては、ダウンキャストが機能しないIList <T>を実装する他のタイプ(たとえば、プレーン配列)があります。Sort()メソッドがIList <T>の拡張メソッドではないのは残念です。
Cygon 2012年

0

あなたが私に尋ねれば、これははるかに単純に見えます。これは私にとって完璧に機能します。

Cast()を使用してIListに変更してから、OrderBy()を使用できます。

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

ここで、Tはタイプです。Model.EmployeeまたはPlugin.ContactService.Shared.Contact

次に、forループとそのDONEを使用できます。

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

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }

-1

あなたの変換IListList<T>またはいくつかの他の一般的なコレクションは、その後、あなたは簡単にクエリ/ソートそれは使用することができますSystem.Linq(これは拡張メソッドの束を供給します)名前空間を


9
IList<T>を実装IEnumerable<T>するため、Linq操作を使用するために変換する必要はありません。
Steve Guidi 2010
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.