LINQ 練習題 -- 自訂排序

  • 596
  • 0
  • 2023-03-03

LINQ 自訂排序的小練習

題目

前陣子有人問了我一個關於 LINQ 自訂排序的作法,先來看一下原始資料結構:

OrderIdSequencePrice
A00101000
A002110
A00222000
A00301200
A0040800
A00518000
A005250
A0060300

資料結構是這樣的,如果一個 OrderId 只有一張單,那麼它的 Sequence 一定是 0;如果 一個 OrderId 有兩張單,那麼 Sequence 一定是 1, 2。在兩張單的狀況下 Sequence 1 和 2 和所對應的 Price 大小沒有關係  (別問我為什麼,我也不知道,總之朋友給的條件是這樣)。

排序規則基本是依據 Price 排序,附加條件是 (1) 如果 OrderId 只有一張單,那沒問題,只有一個 Price (2) 如果是兩張單的,要以 Sequence 1 的 Price 為排序依據,在同一個 OderId 的單必須連在一起。

以上面的原始資料由小到大的排列結果為例:

OrderIdSequencePrice
A002110
A00222000
A0060300
A0040800
A00101000
A00301200
A00518000
A005250
思路

若是直接 Order by Price,那鐵定會錯。既然同一個 OrderId 的單要連在一起,看來先 Group 可能是一種解法。然後再利用自訂排序。想像一下 Group by OrderId 後大概會是這個樣子:

Group A001
A00101000
Group A002
A002110
A00222000
Group A003
A00301200
Group A004
A0040800
Group A005
A00518000
A005250
Group A006
A0060300

接著我們要拿 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 下載。