JavaScript多字段排序结果多次不一致的原因探究
原生JavaScript toSorted多次排序结果不一致的原因
问题场景
使用JavaScript原生toSorted方法对数组按date和createdAt字段降序排序时,同一数组多次排序得到的结果顺序不同,不符合“相同输入应输出一致排序结果”的预期。
代码示例
const arr = [ {"id": 0, "date": new Date("2023-06-02T00:00:00.000Z"), "createdAt": new Date("2023-06-05T01:48:37.586Z") }, { "id": 1, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T01:31:35.901Z"), }, { "id": 2, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T17:19:14.733Z"), }, { "id": 3, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:10:26.454Z"), }, { "id": 4, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:11:53.192Z"), }, { "id": 5, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:13:13.879Z"), }, { "id": 6, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:15:46.841Z"), }, { "id": 7, "date": new Date("2023-06-03T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:16:45.006Z"), }, { "id": 8, "date": new Date("2023-06-04T00:00:00.000Z"), "createdAt": new Date("2023-06-04T17:21:19.906Z"), }, { "id": 9, "date": new Date("2023-06-04T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:01:39.286Z"), }, { "id": 10, "date": new Date("2023-06-04T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:09:48.776Z"), }, { "id": 11, "date": new Date("2023-06-04T00:00:00.000Z"), "createdAt": new Date("2023-06-04T21:27:45.701Z"), } ] function sortItems(items) { return items.toSorted((a, b) => { // do comparison if (a.date > b.date) { return -1 } else if (a < b.date) { return 1 } else { if (a.createdAt > b.createdAt) { return -1 } else if (b.createdAt < b.createdAt) { return 1 } else { return 0 } } }) } const sort_1 = sortItems(arr) console.log(sort_1) const sort_2 = sortItems(sort_1) console.log(sort_2)
问题原因
你的排序比较函数存在两处逻辑错误,导致排序逻辑失效,进而引发结果不稳定:
a < b.date的错误比较:此处应该是a.date < b.date,你错误地将整个对象a和b.date进行比较,对象与Date类型的比较会得到不可预期的结果,导致date字段的比较逻辑混乱。b.createdAt < b.createdAt的无效判断:这是明显的笔误,应该是a.createdAt < b.createdAt,当前写法中左右都是b.createdAt,判断永远为false,导致date相同时,createdAt字段的比较逻辑完全失效。
当比较函数无法正确区分元素的顺序时,即使使用稳定排序算法(ES2019之后,toSorted是稳定排序),也会因为逻辑错误导致元素的相对位置无法被正确约束,多次排序时就会出现结果不一致的情况。
修复方案
修正比较逻辑的版本
function sortItems(items) { return items.toSorted((a, b) => { // 先按date降序比较 if (a.date > b.date) { return -1; } else if (a.date < b.date) { return 1; } else { // date相同时按createdAt降序比较 if (a.createdAt > b.createdAt) { return -1; } else if (a.createdAt < b.createdAt) { return 1; } else { return 0; } } }); }
简化版本(利用时间戳相减)
可以直接使用Date对象的getTime()方法获取时间戳,通过数值相减来简化比较逻辑,代码更简洁且不易出错:
function sortItems(items) { return items.toSorted((a, b) => { // date降序,date相同则createdAt降序 return b.date.getTime() - a.date.getTime() || b.createdAt.getTime() - a.createdAt.getTime(); }); }
修复后,多次排序同一数组会得到完全一致的结果,符合预期的降序排序逻辑。
内容的提问来源于stack exchange,提问作者Jamie Marshall
相关产品推荐
相关产品推荐

