Tip:如何在JavaScript中对对象数组排序
如果你有一个对象数组,需要按一定顺序排序,那么你可能会倾向于使用 JavaScript 库。但是,在你这样做之前,请记住,您可以使用本地的数组排序函数进行一些非常整洁的排序。
在本文中,我将向您展示如何在 JavaScript 中对对象数组进行排序,而不会引起任何麻烦。
要阅读本文,你需要了解基本的 JavaScript 概念,例如声明变量、编写函数和条件语句。我还将使用 ES6 语法。
基本数组排序
默认情况下是 JavaScript 的 Array.sort 函数将数组中要排序的每个元素转换为字符串,并按 Unicode 代码点顺序对它们进行比较
const foo = [8, 3, 5, 'whistle', 'fish']; foo.sort(); // 返回 [3, 5, 8, 'fish', 'whistle'] const bar = [4, 19, 30, function(){}, {key: 'value'}]; bar.sort(); // 返回 [ 19, 30, 4, { key: 'value' }, [Function] ]
你可能想知道为什么 30 在 4 点之前。不合乎逻辑的,嗯?事实上是这样的。这是因为数组中的每个元素首先被转换为字符串,并且按照 Unicode 顺序,所以“30”在“4”之前。
值得注意的是,与许多其他 JavaScript 数组函数不同,Array.sort 实际上会改变或改变它所排序的数组。
const baz = ['hello world', 31, 5, 9, 12]; baz.sort(); // baz 数组被修改 console.log(baz); // 展示 [12, 31, 5, 9, "hello world"]
为了避免这种情况,可以创建要排序的数组的新实例,并修改它。
const baz = ['hello world', 31, 5, 9, 12]; const newBaz = [...baz].sort(); // 创建并排序 baz 数组的新实例 console.log(baz); // "hello world", 31, 5, 9, 12] console.log(newBaz); // [12, 31, 5, 9, "hello world"]
注意,spread 操作符用于创建 baz 的新实例。
大家可以试试以下代码:
const baz = ['hello world', 31, 5, 9, 12]; const newBaz = baz.slice().sort(); console.log(baz); console.log(newBaz);
运行结果:
["hello world", 31, 5, 9, 12] [12, 31, 5, 9, "hello world"]
使用 Array.sort 单独排序对于对象数组的排序不是很有用。值得庆幸的是,该函数接受一个可选的 compareFunction 参数,该参数将根据 compare 函数的返回值对数组元素进行排序。
使用比较函数排序
假设 a 和 b 是被比较函数比较的两个元素。如果比较函数的返回值为:
- 小于 0 – a 在 b 之前
- 大于 0 – b 在 a 之前
- 等于 0 – a 和 b 保持不变
让我们看一个简单的数字数组例子:
const arr = [1, 2, 30, 4]; function compare(a, b){ if (a > b) return 1; if (b > a) return -1; return 0; } arr.sort(compare); // 1, 2, 4, 30
这个可以被重构,通过从 b 中减去 a 得到返回值:
function compare(a, b){ return a - b; }
这是一个很好的箭头函数:
arr.sort((a, b) => a - b);
如果您不熟悉箭头函数,可以在这里阅读更多关于它们的信息:ES6 箭头函数:JavaScript 中丰富而简洁的语法。
在 JavaScript 中对对象数组进行排序
现在让我们看看对象数组的排序。让我们拿一个带对象数组:
const bands = [ { genre: 'Rap', band: 'Migos', albums: 2}, { genre: 'Pop', band: 'Coldplay', albums: 4}, { genre: 'Rock', band: 'Breaking Benjamins', albums: 1} ];
我们可以使用下面的比较函数对这个对象数组按照类型进行排序:
function compare(a, b) { // 使用 toUpperCase()忽略字符大小写 const genreA = a.genre.toUpperCase(); const genreB = b.genre.toUpperCase(); let comparison = 0; if (genreA > genreB) { comparison = 1; } else if (genreA < genreB) { comparison = -1; } return comparison; } bands.sort(compare); /* returns [ { genre: 'Pop', band: 'Coldplay', albums: 4 }, { genre: 'Rap', band: 'Migos', albums: 2 }, { genre: 'Rock', band: 'Breaking Benjamins', albums: 1 } ] */
逆排序顺序,你可以简单地转化比较函数的返回值:
function compare(a, b) { ... //将返回值乘以 -1 return comparison * -1; }
大家可以试一下下面的例子:
const bands = [ { genre: 'Rap', band: 'Migos', albums: 2}, { genre: 'Pop', band: 'Coldplay', albums: 4}, { genre: 'Rock', band: 'Breaking Benjamins', albums: 1} ]; function compare(a, b) { const genreA = a.genre.toUpperCase(); const genreB = b.genre.toUpperCase(); let comparison = 0; if (genreA > genreB) { comparison = 1; } else if (genreA < genreB) { comparison = -1; } return comparison; } console.log(bands.sort(compare));
运行结果展示:
[[object Object] { albums: 4, band: "Coldplay", genre: "Pop" }, [object Object] { albums: 2, band: "Migos", genre: "Rap" }, [object Object] { albums: 1, band: "Breaking Benjamins", genre: "Rock" }]
创建一个动态排序函数
让我们创建一个排序函数,您可以使用它对数组对象进行排序,这些对象的值要么是字符串,要么是数字。这个函数有两个参数——我们想要排序的键和结果的排序(即升序或降序)。
const bands = [ { genre: 'Rap', band: 'Migos', albums: 2}, { genre: 'Pop', band: 'Coldplay', albums: 4, awards: 13}, { genre: 'Rock', band: 'Breaking Benjamins', albums: 1} ]; // 动态排序函数 function compareValues(key, order='asc') { return function(a, b) { if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) { // 属性在这两个对象上都不存在 return 0; } const varA = (typeof a[key] === 'string') ? a[key].toUpperCase() : a[key]; const varB = (typeof b[key] === 'string') ? b[key].toUpperCase() : b[key]; let comparison = 0; if (varA > varB) { comparison = 1; } else if (varA < varB) { comparison = -1; } return ( (order == 'desc') ? (comparison * -1) : comparison ); }; }
你可以这样使用它:
// array is sorted by band, in ascending order by default bands.sort(compareValues('band')); // array is sorted by band in descending order bands.sort(compareValues('band', 'desc')); // array is sorted by albums in ascending order bands.sort(compareValues('albums'));
举例说明:
const bands = [ { genre: 'Rap', band: 'Migos', albums: 2}, { genre: 'Pop', band: 'Coldplay', albums: 4}, { genre: 'Rock', band: 'Breaking Benjamins', albums: 1} ]; function compareValues(key, order='asc') { return function(a, b) { if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) { return 0; } const varA = (typeof a[key] === 'string') ? a[key].toUpperCase() : a[key]; const varB = (typeof b[key] === 'string') ? b[key].toUpperCase() : b[key]; let comparison = 0; if (varA > varB) { comparison = 1; } else if (varA < varB) { comparison = -1; } return ( (order == 'desc') ? (comparison * -1) : comparison ); }; } console.log( bands.sort(compareValues('band', 'desc')) );
运行结果:
[[object Object] { albums: 2, band: "Migos", genre: "Rap" }, [object Object] { albums: 4, band: "Coldplay", genre: "Pop" }, [object Object] { albums: 1, band: "Breaking Benjamins", genre: "Rock" }]
在上面的代码中,hasOwnProperty 方法用于检查指定的属性是否在每个对象上定义,并且没有通过原型链继承。如果没有在对象上定义它,函数返回 0,这将导致排序顺序保持不变。
类型运算符还用于检查属性值的数据类型。这允许函数确定正确的数组排序方法。例如,如果指定属性的值是字符串,则使用 toUpperCase 方法将其所有字符转换为大写,因此在排序时忽略字符大小写。
你可以调整上述函数以适应其他数据类型和脚本需要的任何其他特性。
String.prototype.localeCompare()
在上面的示例中,我们希望能够对对象数组进行排序,这些对象的值要么是字符串,要么是数字。但是,如果您知道您将只处理值为字符串的对象,那么您可以使用 JavaScript 的 localeCompare 方法稍微整理一下代码。
此方法返回一个数字,该数字指示字符串在排序顺序上是否与给定字符串相同。它支持不区分大小写的数组:
["motorhead", "Motorhead", "Mötorhead"].sort(); // ["Motorhead", "Mötorhead", "motorhead"] ["motorhead", "Motorhead", "Mötorhead"].sort((a, b) => a.localeCompare(b)); // ["motorhead", "Motorhead", "Mötorhead"]
compareValues 函数而言,这意味着我们可以写:
function compareValues(key, order='asc') { return function(a, b) { if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0; let comparison = a[key].localeCompare(b[key]); return ( (order == 'desc') ? (comparison * -1) : comparison ); }; }
结论
这就是对数组对象排序的简短介绍。尽管许多 JavaScript 库提供了这种动态排序能力(例如 Underscore.js,Lodash 和 Sugar)。正如所演示的那样,自己实现这种功能并不困难。
如果你有任何问题或意见,请随意在下面留言。
码云笔记 » Tip:如何在JavaScript中对对象数组排序
数组排序
有什么问题可以留言探讨