LINQ 自訂排序的小練習
題目
前陣子有人問了我一個關於 LINQ 自訂排序的作法,先來看一下原始資料結構:
OrderId | Sequence | Price |
A001 | 0 | 1000 |
A002 | 1 | 10 |
A002 | 2 | 2000 |
A003 | 0 | 1200 |
A004 | 0 | 800 |
A005 | 1 | 8000 |
A005 | 2 | 50 |
A006 | 0 | 300 |
資料結構是這樣的,如果一個 OrderId 只有一張單,那麼它的 Sequence 一定是 0;如果 一個 OrderId 有兩張單,那麼 Sequence 一定是 1, 2。在兩張單的狀況下 Sequence 1 和 2 和所對應的 Price 大小沒有關係 (別問我為什麼,我也不知道,總之朋友給的條件是這樣)。
排序規則基本是依據 Price 排序,附加條件是 (1) 如果 OrderId 只有一張單,那沒問題,只有一個 Price (2) 如果是兩張單的,要以 Sequence 1 的 Price 為排序依據,在同一個 OderId 的單必須連在一起。
以上面的原始資料由小到大的排列結果為例:
OrderId | Sequence | Price |
A002 | 1 | 10 |
A002 | 2 | 2000 |
A006 | 0 | 300 |
A004 | 0 | 800 |
A001 | 0 | 1000 |
A003 | 0 | 1200 |
A005 | 1 | 8000 |
A005 | 2 | 50 |
思路
若是直接 Order by Price,那鐵定會錯。既然同一個 OrderId 的單要連在一起,看來先 Group 可能是一種解法。然後再利用自訂排序。想像一下 Group by OrderId 後大概會是這個樣子:
Group A001 | ||
A001 | 0 | 1000 |
Group A002 | ||
A002 | 1 | 10 |
A002 | 2 | 2000 |
Group A003 | ||
A003 | 0 | 1200 |
Group A004 | ||
A004 | 0 | 800 |
Group A005 | ||
A005 | 1 | 8000 |
A005 | 2 | 50 |
Group A006 | ||
A006 | 0 | 300 |
接著我們要拿 Group 後的資料來排序,依據是每個群組裡面 Sequence 最小的那個 Price。用個懶惰的方法來做就是每個 Group 的內部的元素先依照 Sequence 排序再取出第一個 Price 就能拿來比對了。
建立原始資料類別
public class Data
{
public string OrderId { get; set; }
public int Sequence { get; set; }
public int Price { get; set; }
}
建立自訂排序
LINQ 要自訂排序需要額外的自訂排序類別,這個類別必須實作 IComparer<T> 介面,我們要排序的對象不是 Data 而是 Group 後的 IGrouping<string, Data>:
public class DataComparer : IComparer<IGrouping<string, Data>>
{
public int Compare(IGrouping<string, Data> x, IGrouping<string, Data>
{
var xPrice = x.OrderBy(d => d.Sequence).First().Price;
var yPrice = y.OrderBy(d => d.Sequence).First().Price;
return xPrice - yPrice;
}
}
實際使用
var list = new List<Data>
{
new Data { OrderId ="A001" , Sequence =0 , Price = 1000 },
new Data { OrderId ="A002" , Sequence =1 , Price = 10 },
new Data { OrderId ="A002" , Sequence =2 , Price = 2000 },
new Data { OrderId ="A003" , Sequence =0 , Price = 1200 },
new Data { OrderId ="A004" , Sequence =0 , Price = 800 },
new Data { OrderId ="A005" , Sequence =1 , Price = 8000 },
new Data { OrderId ="A005" , Sequence =2 , Price = 50 },
new Data { OrderId ="A006" , Sequence =0 , Price = 300 },
};
var result = list.GroupBy(x => x.OrderId).OrderBy(x => x, new DataComparer()).SelectMany(x => x);
範例可在我的 github 下載。