Order
、OrderId
、OrderDate
、Quantity
などのプロパティを持つTotal
というクラスがあります。このOrder
クラスのリストがあります。
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
今、私はOrder
オブジェクトの1つのプロパティに基づいてリストをソートしたいです。例えば、注文日または注文IDでそれをソートする必要があります。
どのように私はC#でこれを行うことができますか?
私が考えることができる最も簡単な方法はLinqを使うことです:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
リストをその場でソートする必要がある場合は、 Sort
メソッドを使用し、 Comparison<T>
delegateを渡します。
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
インプレースソートではなく、ソートされた新しいシーケンスを作成したい場合は、他の回答で述べたようにLINQの OrderBy
メソッドを使用できます。
.NET 2.0でLINQを使わずにこれを行うには、次のようにします。
List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
delegate(Order p1, Order p2)
{
return p1.OrderDate.CompareTo(p2.OrderDate);
}
);
もしあなたが.Net3.0を使っているのなら、LukeHの answer があなたが求めているものです。
複数のプロパティを並べ替えるには、それでもデリゲート内で実行できます。例えば:
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;
}
);
これにより、 昇順 日付と 降順 orderIdsが得られます。
しかし、コードを再利用せずに多くの場所にアクセスすることになるため、デリゲートを貼り付けることはお勧めしません。 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);
リストを並べる最も簡単な方法は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();
あなたが言ったように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()を呼んでください
古典的なオブジェクト指向ソリューション
最初に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"(または論理演算子の選択)を交換すると、ソート順が逆になります。
// 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);
}
これは、リストの余分なコピーを作成しない一般的な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>
を受け入れるこの追加のものを作りました。これは、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)));
}
LINQを使う
objListOrder = GetOrderList()
.OrderBy(o => o.OrderDate)
.ToList();
objListOrder = GetOrderList()
.OrderBy(o => o.OrderId)
.ToList();
//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;
ロジャーのバージョンの改良。
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;
}
}
あなたはあなたの場合 'Order'で、あなたが選択したタイプについてより具体的であるけれども、プロパティ選択に関してもっと一般的なことをすることができます:
あなたの関数を一般的なものとして書いてください:
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'を行うための愚かで不必要な複雑な方法ですが、一般的な方法でそれを実装する方法の手がかりを与えるかもしれません
私はそれをテストしたので、私はそれがいくつかのために有用であると信じているので、私はいくつかのサンプルコードで@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 ...
}
var obj = db.Items.Where...
var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
上記の答えのどれも私にとって十分に一般的ではなかったので、私はこれを作りました:
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();
大量のデータセットには注意が必要です。これは簡単なコードですが、コレクションが巨大で、コレクションのオブジェクト型に多数のフィールドが含まれている場合は問題を起こす可能性があります。実行時間はN×Mです。
N =コレクション内の要素数
M =オブジェクト内のプロパティ数
Null許容型を扱う人は誰でも、Value
はCompareTo
を使うために必要です。
objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
LiNQを利用する OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
GenericTypeTea の比較基準に基づきます。
ソートフラグを追加することでより柔軟性が得られます。
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; }
}
このシナリオでは、 MyOrderingClass として明示的にインスタンス化する必要があります( IComparer ではなく)。
そのソートプロパティを設定するために:
MyOrderingClass comparer = new MyOrderingClass();
comparer.DateDescending = ...;
comparer.OrderIdDescending = ...;
orderList.Sort(comparer);
パフォーマンスの観点からすると、データは結果に追加されるときにソートされるようにソート済みリストを使用するのが最善です。他のアプローチでは、データに対して少なくとも1回の追加の反復が必要で、ほとんどの場合データのコピーが作成されるため、パフォーマンスだけでなくメモリ使用量も影響を受けます。特に数百の要素が同時にソートを実行する可能性があるサービスでは、数百の要素で問題になることはないかもしれませんが、数千で問題になるでしょう。 System.Collections.Generic名前空間を見て、Listではなくソートを使用してクラスを選択してください。
可能な場合はリフレクションを使用した一般的な実装を避けてください。これもパフォーマンスの問題を引き起こす可能性があります。