JavaScript - 超级排序
JavaScript 中的 Array.sort() 方法对于任何处理数据集的开发者来说都是必备工具。虽然对数字或单词列表进行排序很简单,但基于多种条件对对象数组进行排序则变得更加复杂。在本章中,我们将介绍充分利用 Array.sort() 进行复杂、多属性排序的各种方法。
排序基础知识
在介绍高级方法之前,我们将先了解一下 Array.sort() 的基本操作。sort() 方法接受一个可选的回调函数,用于指定排序顺序。此比较函数接受两个参数(a 和 b)并返回一个数值 -
如果返回值为负数,则将 a 排序在 b 之前。
如果返回值为正数,则将 b 排序在 a 之前。
如果返回结果为 0,则 a 和 b 的顺序保持不变。
以下是一段非常简单的 JavaScript 示例代码,用于对数字数组进行升序排序 -
const numbers = [12, 7, 19, 3, 8, 5, 10, 15]; // 对数组进行降序排序 numbers.sort((a, b) => b - a); // 打印排序后的数组 console.log(numbers);
输出
这将生成以下结果 -
[ 19, 15, 12, 10, 8, 7, 5, 3 ]
比较器 (a, b) => a - b,当 a < b 时返回负值,当 a > b 时返回正值,当 a === b 时返回 0,这满足了 Array.sort() 所要求的契约。
链接多个排序条件
当我们需要根据多个属性对对象数组进行排序时,事情会变得更加有趣。以一个包含项目的数组为例。
const products = [
{ name: 'Smartphone', price: 699.99, rating: 4.7 },
{ name: 'Headphones', price: 129.99, rating: 4.3 },
{ name: 'Tablet', price: 399.99, rating: 4.1 },
{ name: 'Smartwatch', price: 199.99, rating: 4.5 },
];
假设我们想按价格升序排列这些商品,然后按评分降序排列,作为价格相同商品的决胜条件。我们可以使用布尔或运算符 (||) 将比较器连接在一起 -
products.sort((a, b) =>
a.price - b.price || b.rating - a.rating
);
工作原理如下 -
首先计算第一个比较器 a.price 减 b.price。如果价格不同,则返回非零值,并且 || 短路,从而按价格排序。
如果价格相同,则 a.price - b.price 等于 0。||然后评估第二个比较器 b.rating - a.rating,并按评级降序排序。
此模式允许我们根据需要链接任意数量的比较器 −
products.sort((a, b) => a.price - b.price || b.rating - a.rating || a.name.localeCompare(b.name) );
现在,价格和评分相同的商品将按名称字母顺序排序,以确定最终的决胜者。
创建可复用的"sortBy"函数
虽然链式比较器方法很有效,但如果我们需要多次按相同的条件排序,则会导致代码难以阅读且重复。我们可以为每个属性引入通用的"sortBy"函数,从而使代码更加模块化。-
const byPrice = (a, b) => a.price - b.price; const byRating = (a, b) => b.rating - a.rating; const byName = (a, b) => a.name.localeCompare(b.name); products.sort((a, b) => byPrice(a, b) || byRating(a, b) || byName(a, b) );
这提高了排序链的可读性,并允许每个比较器重复使用。但我们仍然需要为每个属性编写一些重复的代码。让我们看看能否改进。
高阶"sortBy"函数
为了提高可重用性,我们可以定义一个高阶"sortBy"函数,它接受一个属性名称并返回一个比较器函数 -
function sortBy(prop) {
return (a, b) =>
a[prop] < b[prop] ? -1 :
a[prop] > b[prop] ? 1 :
0;
}
const byPrice = sortBy('price');
const byRating = (a, b) => sortBy('rating')(b, a);
const byName = sortBy('name');
sortBy 函数被认为是高阶函数,因为它返回另一个函数。它使用闭包方法在返回的比较器作用域内捕获 prop 参数。
这种方法使我们能够轻松地为任何属性开发比较器。为了按降序排序,byRating 比较器会反转通常的 (a, b) 顺序。
这是借助箭头函数的完整 ES6 版本 -
const sortBy = (prop) => (a, b) =>
a[prop] < b[prop] ? -1 :
a[prop] > b[prop] ? 1 :
0;
products.sort(
(a, b) => sortBy('price')(a, b) ||
sortBy('rating')(b, a) ||
sortBy('name')(a, b)
);
总结
在本章中,我们介绍了多种提升 JavaScript 排序技能的方法。例如,将多个排序条件与布尔"或"逻辑相结合;将比较器重构为可重用的"sortBy"函数;开发高阶"sortBy"函数以最大限度地提高功能性。理解并遵循这些模式,将使您能够根据多种属性高效地对对象数组进行排序,同时编写简洁易维护的代码。

