public class StockQuantity { public StockQuantity(string status, DateTime dateTime, int quantity) { Status = status; DateTime = dateTime; Quantity = quantity; } public string Status { get; set; } public DateTime DateTime { get; set; } public int Quantity { get; set; } }
該對象,主要有三個字段,如今的業務需求是,取到了一個類型爲List<StockQuantity>集合StockQuantities,須要對該集合進行三次排序,排序規則及優先級以下:
1. Status爲空的排在後面,不爲空的排在前面,不關心Status的內容,只關心Status是否爲空。
2. DateTime升序排序。
3. Quantity升序排序。算法
我只知道能夠對集合用OderBy排序,對以上三條規則,因此設計思路以下。優化
1. StockQuantities.OrderBy(u=>u.Status)
錯誤,
該排序得規則不單單會考慮Status是否爲空,還會考慮Status的內容。
若是Status是[「b」,」c」,null,」d」],那麼排序結果是[null,「b」,」c」,」d」]。
而咱們要的結果是[「b」,」c」,」d」 ,null] (直接把null的丟到最後,別的不動)
怎麼辦?spa
暫時不知道,先無論設計
2. 對DateTime進行升序排序,這簡單
StockQuantities.OrderBy(u=>u.DateTime)
半對!
爲何半對,看下面code
3. 在排序2的前提下,用OrderBy,也就是StockQuantities.OrderBy(u=>u.DateTime).OrderBy(u=>u.Quantity)
錯誤!
以上表達式等同於下面兩條的表達式:對象
StockQuantities = StockQuantities.OrderBy(u=>u.DateTime)
StockQuantities = StockQuantities.OrderBy(u=>u.Quantity)
因此第一條代碼就是廢代碼,最終排序仍是以Quantity進行排序的。
雖然我是小白,但我仍是明白這樣是錯誤的,因此個人作法是blog
stockQuantities = stockQuantities.OrderBy(u => u.DateTime).ToList(); foreach (var dateOrder in stockQuantities) { var datetimeOrderBy = stockQuantities.Where(u => u.DateTime.Date == dateOrder.DateTime.Date) .OrderBy(u => u.Count); foreach (var countOrder in datetimeOrderBy) { if (countOrder.OutPut == false) { Console.WriteLine($"{countOrder.Status}-{countOrder.DateTime}-{countOrder.Count}"); countOrder.OutPut = true; } } } Console.ReadKey();
採用雙層循環,先取到按時間排序的數據 dateOrder,再去和該條數據在同一天的全部數據並對Quantity進行排序,爲了防止重複的輸出,我同時給StockQuantity對象加上了Output屬性,當該屬性爲false爲,則輸出該對象的內容,並把Output屬性設爲true,這樣就不會重複輸出了,並且實現了先對DateTime排序,再對Quantity進行排序。
So Easy!!
然而,當開心地把這樣的代碼提交以後,卻被同事狠狠地鄙視了,說到:「什麼爛代碼啊!」然道還有比這更好的代碼? 排序
給同事倒了一杯茶,點了一根菸,虛心請教。get
同事給我講了兩招,分別是條件排序、多級排序。string
1. StockQuantities.OrderBy(u=>u.Status==null)
這就是條件排序,但是咋一看,給人一種是把Status爲空的排前面,不爲空的排後面的錯覺。
其實否則,咱們看到OrderBy裏面的一個返回值爲bool類型的表達式,該排序先排結果爲0(false)的,再排結果爲1(true)的。這種排序只考慮返回的bool值,不考慮參數的具體值,因此姑且稱它爲條件排序。
徹底符合排序規則1的要求。
2. 利用我上面個人代碼排序雖然能夠實現先排DateTime,再排Quantity,可是該算法的時間複雜度的n*n,並且給StockQuantity添加了output字段,明顯是不科學的。
然而,連續地使用多個OrderBy最終只會生效最後一個OrderBy,天無絕人之路,因此這個時候應該使用ThenBy!!
使用ThenBy能夠講以上的三條排序規則簡化以下:
stockQuantities = stockQuantities.OrderBy(u => u.Status==null).ThenBy(u => u.DateTime).ThenBy(u => u.Quantity).ToList();
便可完美地實現再前一個排序前提下進行二級排序。
優化後的完整代碼以下:
using System; using System.Collections.Generic; using System.Linq; namespace OrderBy { class Program { static void Main(string[] args) { var stockQuantities = new List<StockQuantity>() { new StockQuantity("正常品",new DateTime(2017,4,16),12 ), new StockQuantity("正常品",new DateTime(2017,4,17),15 ), new StockQuantity("殘次品",new DateTime(2017,4,16),10 ), new StockQuantity("殘次品",new DateTime(2017,4,17),8 ), new StockQuantity(null,new DateTime(2017,4,18),8 ), }; stockQuantities = stockQuantities.OrderBy(u => u.Status==null).ThenBy(u => u.DateTime).ThenBy(u => u.Quantity).ToList(); foreach (var stockQuantity in stockQuantities) { Console.WriteLine($"{stockQuantity.Status}-{stockQuantity.DateTime}-{stockQuantity.Quantity}"); } Console.ReadKey(); } } public class StockQuantity { public StockQuantity(string status, DateTime dateTime, int quantity) { Status = status; DateTime = dateTime; Quantity = quantity; } public string Status { get; set; } public DateTime DateTime { get; set; } public int Quantity { get; set; } } }
簡單的一個排序優化,就把程序的時間複雜度從N*N下降到了N,因此在這裏把這兩種排序技巧分享出來,但願對不會的同窗有所幫助。