背景:
- 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 的丢到最后, 别的不动)
怎么办?
暂时不知道, 先不管
2. 对 DateTime 进行升序排序, 这简单
StockQuantities.OrderBy(u=>u.DateTime)
半对!
为什么半对, 看下面
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 进行排序的.
虽然我是小白, 但我还是明白这样是错误的, 所以我的做法是
- 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!!
然而, 当开心地把这样的代码提交之后, 却被同事狠狠地鄙视了, 说到:"什么烂代码啊!" 然道还有比这更好的代码?
给同事倒了一杯茶, 点了一根烟, 虚心请教.
大佬做法:
同事给我讲了两招, 分别是条件排序, 多级排序.
什么是条件排序, 怎么用?
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, 所以在这里把这两种排序技巧分享出来, 希望对不会的同学有所帮助.
来源: https://www.cnblogs.com/CoderAyu/p/8858447.html