オブジェクトのプロパティでList <T>を並べ替える方法


1249

私はと呼ばれるクラスの持っているOrderような特性を有しているOrderIdOrderDateQuantity、とTotal。このOrderクラスのリストがあります:

List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders

次に、Orderオブジェクトの1つのプロパティに基づいてリストを並べ替えます。たとえば、注文日または注文IDで並べ替える必要があります。

C#でこれを行うにはどうすればよいですか?


9
お待ちください-注文日注文IDの両方で並べ替えますか?ほとんどの回答は、どちらか一方のみで並べ替えていると想定しているためです。
GalacticCowboy

@ GalacticCowboy、@ GenericTypeTea:「Orderオブジェクトの1つのプロパティに基づいてリストを並べ替えたい」という質問ですが、他の場所では完全に明確ではないことに同意します。どちらの方法でも、1つまたは複数のプロパティで並べ替える必要があるかどうかに大きな違いはありません。
LukeH 2010

@LukeH-ちょうど4回目の再読があり、同意します。「1つのプロパティに基づいてリストを並べ替えたい」と「注文日または注文ID」。すべての回答の間に、すべての基盤が網羅されています:)。
djdd87 2010

回答:


1807

私が考えることができる最も簡単な方法は、Linqを使用することです。

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();

22
これを降順で並べ替える方法を教えてください。
キュートベア

229
@BonusKun List <Order> SortedList = objListOrder。OrderByDescending(o => o.OrderDate).ToList();
javajavajavajavajava 2013

77
これにより、メモリ内のすべてのアイテムを含むまったく新しいリストが作成されますが、パフォーマンスの点で問題がある可能性があります。
staafl 2013年

14
@staaflはlistWithObjects = listWithObjects.OrderByDescending(o => o.Status).ToList();そのような努力に十分でしょうか?
Andrew Grinder 14

28
@staafl私たちはオブジェクト参照のリストを注文しています。私が知る限り、オブジェクト自体を複製するわけではありません。これは参照のリストで使用されるメモリを2倍にしますが、実際にすべてのオブジェクト自体を複製するほど悪くはないので、巨大なデータセットを処理してメモリに保持するシナリオ以外のほとんどのシナリオでは、すでに問題があります。それで十分です。
Lazarus

798

リストをインプレースでソートする必要がある場合は、Sortメソッドを使用してComparison<T>デリゲートを渡すことができます。

objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

OrderBy他の回答で述べたように、インプレースでソートするのではなく、新しいソートされたシーケンスを作成したい場合は、LINQの方法を使用できます。


30
はい、これは "正しい"回答であり、新しいIEnumerableを作成して新しいリストに変換するよりもはるかに効率的です。
ジョナサンウッド

45
もちろん、あなたは、ソートスワップを降順必要がある場合xy、矢印の右側に=>
Jeppe Stig Nielsen 2016

15
リストをインプレースで実際に並べ替える本当の答え
ミッチェルカリー

3
@PimBrouwersこれはより良いオプション期間です。使用しているメモリの量が問題ではない場合でも、このソリューションは、非常にコストのかかる不要なメモリ割り当てを回避します。このオプションは、コードごとに単純で、桁違いに高速です。
Cdaragorn 16

12
@JonSchneider For Nullable<>DateTime?例として)を使用.Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate))すると、nullをすべての非null値の前に置くことができます。まったく同じ.Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate)です。
Jeppe Stig Nielsen 2017

219

.Net2.0でLINQなしでこれを行うには:

List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
    delegate(Order p1, Order p2)
    {
        return p1.OrderDate.CompareTo(p2.OrderDate);
    }
);

あなたが.Net3.0を使っているなら、LukeHの答えはあなたが求めているものです。

複数のプロパティで並べ替えるには、デリゲート内で行うことができます。例えば:

orderList.Sort(
    delegate(Order p1, Order p2)
    {
        int compareDate = p1.Date.CompareTo(p2.Date);
        if (compareDate == 0)
        {
            return p2.OrderID.CompareTo(p1.OrderID);
        }
        return compareDate;
    }
);

これにより、orderId が降順の昇順の日付が得られます。

ただし、コードを再利用せずに多くの場所を意味するため、デリゲートを固定することはお勧めしません。を実装し、IComparerそれをSortメソッドに渡すだけです。こちらをご覧ください

public class MyOrderingClass : IComparer<Order>
{
    public int Compare(Order x, Order y)
    {
        int compareDate = x.Date.CompareTo(y.Date);
        if (compareDate == 0)
        {
            return x.OrderID.CompareTo(y.OrderID);
        }
        return compareDate;
    }
}

次に、このIComparerクラスを使用するには、インスタンス化してSortメソッドに渡すだけです。

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);

1
優れた答えであり、元のリストの再初期化(LINQバージョンが常に行う)を保護してより良いカプセル化を提供するため、正しい答えになるはずです。
2014

@ウォントン、いいえ。このアイデアは、さまざまなIComparer実装を可能にして、多態的な動作を提供することです。
レーダーボブ2014年

私のために働いた。並べ替えオプション付きのこのバージョンを投稿しました。
ジャックグリフィン

109

リストを注文する最も簡単な方法は、 OrderBy

 List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ToList();

次のSQLクエリのように複数の列で並べ替える場合。

ORDER BY OrderDate, OrderId

これを達成するにはThenBy、次のように使用できます。

  List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();

32

あなたが言ったようにLinqなしでそれを行う:

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

次に、注文のリストで.sort()を呼び出します


3
最初nullasキャストでテストする必要があります。as(ha、ha)(Order)objが失敗すると例外がスローされるため、これがの要点です。if(orderToCompare == null) return 1;
レーダーボブ2014年

インターフェースを使用するための+1。これにより、コードの保守が容易になり、オブジェクトの機能が明確に公開されます。
AeonOfTime 2015年

ビルとテッドが現れた場合は、2014年10月16日まで連れて行ってくれるようお願いします。そうすれば、上記の間違いを修正できます。キャストが失敗した場合はas戻りnullます。しかし、少なくともnullテストは正しいです。
radarbob

@radarbobええ..おっと。:) だが!この関数は、リストの並べ替えによって自動的に使用されることを目的としています。つまりList<Order>、型が一致することが保証されている必要があるasため、2014年にバグを記述しなかった可能性があり、不必要なガードステートメントを回避できます:)
ジミーホファ

興味深い点を保証する必要があります。うまくカプセル化され、を渡す以外に呼び出せない場合List<Order>。しかし、あなたも私も、暗黙のうちに「私はこのコードを書いているので、間違って使用されない」という自己カプセル化されたプログラマーに会いました
–radarbob

26

古典的なオブジェクト指向ソリューション

最初に、LINQの素晴らしさを手放さなければなりません...。

JimmyHoffaの回答のバリエーション。ジェネリックスをCompareTo使用すると、パラメーターは型安全になります。

public class Order : IComparable<Order> {

    public int CompareTo( Order that ) {
        if ( that == null ) return 1;
        if ( this.OrderDate > that.OrderDate) return 1;
        if ( this.OrderDate < that.OrderDate) return -1;
        return 0;
    }
}

// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort(); 

もちろん、このデフォルトのソート機能は再利用できます。つまり、各クライアントが並べ替えロジックを冗長に書き直す必要はありません。「1」と「-1」(または選択した論理演算子)を入れ替えると、ソート順が逆になります。


リスト内のオブジェクトを並べ替えるためのシンプルなアプローチ。しかし、(== nullの場合)1を返す理由がわかりません。
Loc Huynh 2017年

4
thisオブジェクトがnullより大きいことを意味します。並べ替えの目的で、nullオブジェクト参照は「より小さい」thisオブジェクトです。これが、nullの並べ替え方法を定義することを決めた方法です。
レーダーボブ2017年

18

// gridviewで使用するための完全に汎用的な並べ替え

public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
    {

        List<T> data_sorted = new List<T>();

        if (sortDirection == "Ascending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) ascending
                              select n).ToList();
        }
        else if (sortDirection == "Descending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) descending
                              select n).ToList();

        }

        return data_sorted;

    }

    public object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }

4
または、ご存知でしょうdata.OrderBy()。ホイールの再発明よりも少し簡単です。
gunr2171 2013年

4
これは、どのタイプのオブジェクトでも機能するという考えです。OrderBy()は厳密に型指定されたオブジェクトでのみ機能します。
ロジャー

1
私の意見では、グリッドデータをソートする最もシンプルで使用可能なソリューションです!
kostas ch。

6

以下は、リストの余分なコピーを作成しない一般的なLINQ拡張メソッドです。

public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
    where U : IComparable<U>
{
    list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
}

それを使用するには:

myList.Sort(x=> x.myProperty);

ICompare<U>比較をカスタマイズできるように、私は最近、これを受け入れる追加の1つを作成しました。これは、Natural文字列ソートを実行する必要があるときに役立ちました。

public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
    where U : IComparable<U>
{    
    list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
}

私はこれを実装しましたが、うまくいきます。「isAscending = true」パラメーターを追加しました。降順で並べ替えるには、2つのInvoke()メソッド内でxとyを入れ替えます。ありがとう。
Rob L

式をコンパイルするだけの場合は、最初にデリゲートを受け入れることもできます。さらに、セレクターが副作用を引き起こす場合、計算にコストがかかる場合、または決定論的でない場合、このアプローチには大きな問題があります。選択した式に基づく適切な並べ替えでは、リスト内の項目ごとに1回だけセレクタを呼び出す必要があります。
サービー2014

@Servy-これらはすべて有効なポイントですが、正直に言って、提案の一部を実装する方法がわかりません(特に、セレクターが副作用を引き起こすのを防ぐ...?)。それらをデリゲートに変更しました。これらの変更を行う方法を知っている場合は、私のコードを編集していただければ幸いです。
Peter 14

3
@ピーターデリゲートが副作用を引き起こすのを防ぐことはできません。あなたができることは、それらがオブジェクトごとに複数回呼び出されないようにすることです。これは、各オブジェクトの値を計算し、オブジェクト/投影値のペアを保存し、それをソートすることを意味します。 OrderByこれらすべてを内部で行います。
サービー2014

voidこの精神で拡張メソッドを記述するもう1つの良い方法:メソッドでstatic class Extensions { public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector) { self.SortBy(keySelector, Comparer<TKey>.Default); } public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) { self.Sort((x, y) => comparer.Compare(keySelector(x), keySelector(y))); } }補足できますSortByDescending。@Servyのコメントは私のコードにも適用されます!
Jeppe Stig Nielsen 2017


3
//Get data from database, then sort list by staff name:

List<StaffMember> staffList = staffHandler.GetStaffMembers();

var sortedList = from staffmember in staffList
                 orderby staffmember.Name ascending
                 select staffmember;

3

ロジャーのバージョンの改良。

GetDynamicSortPropertyの問題は、プロパティ名のみを取得することですが、GridViewでNavigationPropertiesを使用するとどうなりますか?nullが見つかったため、例外が送信されます。

例:

"Employee.Company.Name;"はクラッシュします...パラメータとして "Name"のみが値を取得できるためです。

これは、ナビゲーションプロパティで並べ替えることができるように改良されたバージョンです。

public object GetDynamicSortProperty(object item, string propName)
    {
        try
        {                 
            string[] prop = propName.Split('.'); 

            //Use reflection to get order type                   
            int i = 0;                    
            while (i < prop.Count())
            {
                item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
                i++;
            }                     

            return item;
        }
        catch (Exception ex)
        {
            throw ex;
        }


    } 

3

プロパティの選択についてより一般的なことを行うことができますが、「注文」の場合は、選択元のタイプに固有です。

関数を一般的なものとして記述します。

public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
        {
            return (from order in orders
                    orderby propertySelector(order)
                    select order).ToList();
        } 

そしてそれを次のように使用します:

var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);

さらに汎用的にして、注文するもののオープンタイプを定義できます。

public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
        {
            return (from item in collection
                    orderby propertySelector(item)
                    select item).ToList();
        } 

同じように使用します:

var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);

これは、LINQスタイルの 'OrderBy'を実行するための愚かで不必要で複雑な方法ですが、一般的な方法で実装する方法の手掛かりになるかもしれません


3

いくつかのサンプルコードを使用して@LukeHの回答を完成させてください。

public class Order
{
    public string OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public int Quantity { get; set; }
    public int Total { get; set; }

    public Order(string orderId, DateTime orderDate, int quantity, int total)
    {
        OrderId = orderId;
        OrderDate = orderDate;
        Quantity = quantity;
        Total = total;
    }
}

public void SampleDataAndTest()
{
    List<Order> objListOrder = new List<Order>();

    objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
    objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
    objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
    objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
    objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
    objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));


    Console.WriteLine("Sort the list by date ascending:");
    objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by date descending:");
    objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by OrderId ascending:");
    objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    //etc ...
}



1

基づいてGenericTypeTeaさんはComparer:
私たちは、ソートフラグを追加することによって、より多くの柔軟性を得ることができます。

public class MyOrderingClass : IComparer<Order> {  
    public int Compare(Order x, Order y) {  
        int compareDate = x.Date.CompareTo(y.Date);  
        if (compareDate == 0) {  
            int compareOrderId = x.OrderID.CompareTo(y.OrderID);  

            if (OrderIdDescending) {  
                compareOrderId = -compareOrderId;  
            }  
            return compareOrderId;  
        }  

        if (DateDescending) {  
            compareDate = -compareDate;  
        }  
        return compareDate;  
    }  

    public bool DateDescending { get; set; }  
    public bool OrderIdDescending { get; set; }  
}  

このシナリオでは 、並べ替えプロパティを設定するために、それを(IComparerではなく)MyOrderingClassとして明示的にインスタンス化する必要があります。

MyOrderingClass comparer = new MyOrderingClass();  
comparer.DateDescending = ...;  
comparer.OrderIdDescending = ...;  
orderList.Sort(comparer);  

1

上記の答えのどれも私にとって十分に一般的ではなかったので、私はこれを作りました:

var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
var SortedData = DataToBeSorted
                   .OrderBy(m => m.GetType()
                                  .GetProperties()
                                  .First(n => 
                                      n.Name == someUserInputStringValue)
                   .GetValue(m, null))
                 .ToList();

ただし、大量のデータセットには注意してください。簡単なコードですが、コレクションが膨大で、コレクションのオブジェクトタイプに多数のフィールドがある場合、問題が発生する可能性があります。実行時間はNxMです。

N =コレクション内の要素の数

M =オブジェクト内のプロパティの数


1

null許容型Valueを使用するユーザーはを使用する必要がありますCompareTo

objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));


0

パフォーマンスの観点からは、ソートされたリストを使用して、データが結果に追加されるときにデータがソートされるようにするのが最適です。他のアプローチでは、データに対して少なくとも1つの追加の反復が必要であり、ほとんどがデータのコピーを作成するため、パフォーマンスだけでなくメモリ使用量も影響を受けます。数百の要素の問題ではない可能性がありますが、特に、多数の同時要求が同時にソートを実行する可能性があるサービスでは、数千の問題が発生する可能性があります。System.Collections.Generic名前空間を見て、Listではなく並べ替えのクラスを選択します。

また、可能な場合はリフレクションを使用した一般的な実装を避けます。これにより、パフォーマンスの問題が発生する可能性があります。


これはどのようにしてパフォーマンスを向上させることができますか?並べ替えられたリストへのデータの挿入はO(n)なので、nアイテムの追加はO(n ^ 2)です。追加しようとしている同時アイテムの数はいくつですか?アイテムの追加中に追加のCPUサイクルを書き込む必要がある状況がありますが、これは良い一般的な解決策ではありません。
John La Rooy 2017

0

次のコードがあるとします。このコードには、並べ替えに使用するいくつかのプロパティを持つPassengerクラスがあります。

public class Passenger
{
    public string Name { get; }
    public string LastName { get; }
    public string PassportNo { get; }
    public string Nationality { get; }

    public Passenger(string name, string lastName, string passportNo, string nationality)
    {
        this.Name = name;
        this.LastName = lastName;
        this.PassportNo = passportNo;
        this.Nationality = nationality;
    }

    public static int CompareByName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Name, passenger2.Name);
    }

    public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.LastName, passenger2.LastName);
    }

    public static int CompareNationality(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Nationality, passenger2.Nationality);
    }
}

public class TestPassengerSort
{
    Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
    Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
    Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");

    public void SortThem()
    {
        Passenger[] passengers = new Passenger[] { p1, p2, p3 };
        List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };

        Array.Sort(passengers, Passenger.CompareByName);
        Array.Sort(passengers, Passenger.CompareByLastName);
        Array.Sort(passengers, Passenger.CompareNationality);

        passengerList.Sort(Passenger.CompareByName);
        passengerList.Sort(Passenger.CompareByLastName);
        passengerList.Sort(Passenger.CompareNationality);

    }
}

したがって、コンポジションデリゲートを使用してソート構造を実装できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.