

数据处理过程中,我们经常需要对数据进行排序,但有时候,常规的“升序”或“降序”无法满足业务需求。我们需要根据指定的规则,对数据进行自定义排序。
今天用一个经典实操案例,带大家吃透 Power Query 里List.Sort自定义比较器,掌握这套通用模板,往后所有非常规排序需求都能直接套用。
需求
有一个表格,每一行包含若干个范围在 1 至 999 之间的正整数:

目标是:将这些数字任意顺序连接,使得拼接后的结果数值最大。
最终效果格式下:

处理步骤
将数据导入到PQ,并且将数据都转换为文本格式,因为我们的比较逻辑是基于字符串拼接的,而非数值大小。

创建自定义排序规则
需求是要求最大数,其实就是需要将数据进行排序,再拼接,使其结果为最大的数。
但这个需求常规的排序是无法解决的。举个例子:数字
9和316,单纯按数值大小看 316 更大,但拼接对比9316>3169,所以 9 必须排在 316 前面。排序规则应该是:任意两数转字符串 a、b,若
a+b > b+a→ a 放左边;否则 b 放左边。它本质是字典序比较拼接结果,不是单纯比数字本身大小,这也是为什么上一步要将数字转为文本。
比如第一行数据:{44,8,9,316,43,45} 逐个两两对比:
"9"+"8" = "98">"89"→ 9 在前"9"+"45" = "945">"459"→ 9 在 45 前"45"+"44"="4544">"4445"→ 45 在 44 前"44"+"43"="4443">"4344"→44 在 43 前"43"+"316"="43316">"31643"→43 在 316 前排序顺序: 9,8,45,44,43,316拼接结果为:98454443316,这也就是能拼接出的最大数。
上面的规则,我们可以用一个自定义函数来表示:

单行数据转为列表,套用自定义排序规则 先将每一行数据,转为列表 
再用List.sort进行排序,PQ 的列表排序支持传入自定义比较函数。
将刚才定义的比较逻辑作为第二个参数传递给 List.Sort。
List.Sort 在排序时,会自动从列表里随便挑两个元素丢进 fx 做对比:
排序流程:
取出一行所有文本: {"44","8","9","316","43","45"}Sort 会不断两两丢进 fx 对比,根据 fx 返回的 - 1/0 自动调换位置 PQ 内置排序引擎自动完成全列表重排 最终排好的顺序: {"9","8","45","44","43","316"}

合并文本 排序完成后,使用 Text.Combine将列表中的文本合并,即可得到最终的最大数。
最终代码
let 源 = Excel.CurrentWorkbook(){[Name="表1"]}[Content], 更改的类型 = Table.TransformColumns(源,{},each Text.From(_)), 已添加自定义 = Table.AddColumn(更改的类型, "最大数", each [ fx=(x,y)=>if x&y >y&x then -1 else 0, r=Text.Combine(List.Sort(Record.ToList(_),fx)) ][r])in 已添加自定义很多人使用 Power Query 只会基础排序,忽略了List.Sort自定义比较器这个万能工具。
今天的数字拼接案例看似是算法小题,实则提供了一套通用解决方案:
只要原生排序不满足业务逻辑,我们就能自己定义元素对比规则,交给 PQ 自动批量运算,不用手动分步处理,大大的简化复杂数据流程。
完
PowerQuery 案例 75:不用转置、不用拆分!笛卡尔积展开,轻松搞定复杂双层表头
PowerQuery 案例 74:还在用辅助列算库存抵扣?用 PQ 一行代码,彻底告别手动拉表!
PowerQuery 案例 73:被这个分组需求逼疯了!连续相同款号不能拆,还要卡数量?!
PowerQuery 案例 72 | 别再手动对齐层级了!这套偷懒绝招治好了我的强迫症
PowerQuery 案例 71:“既要又要还要”?遇到这种数据,千万别再用嵌套公式折磨自己了


夜雨聆风