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,因此在这里把这两种排序技巧分享出来,但愿对不会的同窗有所帮助。