近日,JavaScript 社区围绕数组分组操作后的排序问题展开了一场热烈讨论。随着 ECMAScript 2023 正式纳入 Array.prototype.groupByArray.prototype.groupByToMap 方法,开发者终于可以原生地将数组元素按属性分组。然而,一个看似简单却令许多开发者头疼的问题浮出水面:分组后的键默认按字母顺序排列,但实际业务中往往需要按照非字母顺序(如优先级、时间顺序、地理层级等)进行排序。这一技术痛点催生了多种解决方案,也折射出语言设计在贴近现实需求时的微妙权衡。

原生 groupBy 的“字母序陷阱”

Array.prototype.groupBy 的引入本是为了简化数据聚合。例如,给定一组订单数据,按城市分组后得到 { '北京': [...], '广州': [...], '上海': [...] }——但原生方法返回的是一个普通对象,其键的遍历顺序在大多数引擎中遵循字符串的 Unicode 码点顺序,因此“北”“广”“上”三个汉字会按拼音首字母或 Unicode 编码排序,结果往往变成 { '北京': ..., '广州': ..., '上海': ... },即“北”、“广”、“上”的顺序。这在中文语境下尚可接受,但如果分组键是优先级标识(如“高”“中”“低”),或是一周天数(“周一”“周三”“周二”),字母顺序就会彻底打乱业务逻辑。

更棘手的是,一些场景要求完全自定义的排序规则。例如,电商促销活动中,商品按“爆款”“常规款”“清仓款”分组展示;或是在报表中,地区分组需按华东、华北、华南的地理顺序排列。这些非字母、非数字的排序需求,原生 groupBy 无法直接满足。

现有解决方案:从 Map 到自定义排序

面对这一局限,社区迅速给出了多种应对策略。最直接的做法是使用 Array.prototype.groupByToMap,该方法返回一个 Map 对象,而 Map 的迭代顺序严格遵循键的插入顺序。开发者只需先构建一个有序的键列表,然后按该列表映射分组结果即可。例如:

const orderedKeys = ['高', '中', '低'];
const groupedByPriority = new Map();
orderedKeys.forEach(key => groupedByPriority.set(key, data.filter(item => item.priority === key)));

这种方法简单直观,但需要预先知道所有可能的键及其顺序,对于动态数据不够灵活。

另一种更通用的方案是结合 Object.fromEntriesArray.sort。先将 groupBy 返回的对象转换为条目数组,再传入自定义比较函数进行排序,最后重新构建对象。例如:

const grouped = Object.groupBy(data, item => item.category);
const sortedGrouped = Object.fromEntries(
  Object.entries(grouped).sort((a, b) => customOrder.indexOf(a[0]) - customOrder.indexOf(b[0]))
);

其中 customOrder 是一个预定义的键顺序数组。这种方式灵活性高,适用于大多数自定义排序场景。

从工具到语言特性:未来的可能

目前,ECMAScript 标准委员会尚未将排序纳入 groupBy 的设计之中,因为分组和排序被认为是两个独立的关注点。但开发者社区的反馈已促使一些提案开始考虑更高级的聚合操作,例如允许传入排序函数作为 groupBy 的第二个参数,或者引入类似 SQL 中 ORDER BY 子句的语法糖。

值得注意的是,Lodash 等实用工具库早已提供了 _.groupBy_.sortBy 的组合,通过链式调用实现分组后排序。但由于原生 API 的普及,开发者需要在使用库和原生方案之间做出选择。对于性能敏感的大型应用,原生 groupBy 通常更快,但需要额外代码处理排序;而库方案则更加简洁,但增加了依赖。

结语:分组不排序,排序不分组

“Array groupBy property but then sort by a non alphabetical order”这一标题背后,反映了现代 Web 开发中对数据操作精细化的普遍需求。分组与排序本是两个常见的数组操作维度,但将二者结合时,默认的字母序往往与业务语义相悖。当前,最务实的做法是接受原生 groupBy 的“单纯”,然后利用 Map 的插入顺序或自定义排序函数来达成目标。而随着语言标准的演进,我们或许能迎来更优雅的内置解决方案——到那时,开发者只需一道声明式指令,即可实现“按属性分组,再按非字母顺序排序”的理想效果。