如何在 JavaScript 中对对象数组进行排序

若是须要按特定顺序对对象数组进行排序,咱们颇有可能会直接找个 JS 库来用。其实大可没必要,JS 原生中的 Array.sort就能直接一些复杂又漂亮的排序。前端

本文中,将介绍一些 Array.sort 的常规排序和一些骚操做。面试

基本数组排序

默认状况下,Array.sort函数将数组中须要排序的每一个元素转换为字符串,并按 Unicode 顺序对其进行比较。算法

const foo = [9, 1, 4, 'zebroid', 'afterdeck'];
foo.sort(); // returns [ 1, 4, 9, 'afterdeck', 'zebroid' ]
const bar = [5, 18, 32, new Set, { user: 'Eleanor Roosevelt' }];
bar.sort(); // returns [ 18, 32, 5, { user: 'Eleanor Roosevelt' }, Set {} ]

你可能会好奇为啥32排在5以前。 发生这种状况是由于数组中的每一个元素都首先转换为字符串,而且按照Unicode顺序,"32"在"5"以前。数组

须要注意的是,Array.sort会更改原数组。浏览器

const baz = ['My cat ate my homework', 37, 9, 5, 17];
baz.sort(); // baz数组被修改
console.log(baz); // shows [ 17, 37, 5, 9, 'My cat ate my homework' ]

为避免这种状况,咱们能够建立要排序的数组的新实例,而后在新的数组上进行修改。 这里可使用 Array.slice它返回是一个新的数组实例。网络

// 建立baz数组的新实例并对其进行排序
const sortedBaz = baz.slice().sort();

咱们还可使用 ES6 中的展开运算符来作:数据结构

const sortedBaz = [...baz].sort();

在两种状况下,输出是相同的:函数

console.log(baz); // ['My cat ate my homework', 37, 9, 5, 17];
console.log(sortedBaz); // [ 17, 37, 5, 9, 'My cat ate my homework' ]

单独使用Array.sort不能对对象数组进行排序。但没必要担忧,sort 的还提供一个参数,该参数使数组元素根据compare函数的返回值进行排序。学习

使用比较函数进行排序

假设foo和bar是compare函数要比较的两个元素,compare函数的返回值设置以下:prototype

小于0:foo在bar以前 大于0 :bar在foo以前 等于0:foo和bar彼此保持不变。 来看一个简单的示例:

const nums = [79, 48, 12, 4];
function compare(a, b) {
  if (a > b) return 1;
  if (b > a) return -1;

  return 0;
}

nums.sort(compare);
// => 4, 12, 48, 79

咱们能够稍微重构一下:

function compare(a, b) {
  return a - b;
}

使用在使用箭头函数进行重构:

nums.sort((a, b) => a - b);

如何对对象数组进行排序

如今,咱们来按一下对对象数组的排序。假设有下面的 singers 数组:

const singers = [
  { name: 'Steven Tyler', band: 'Aerosmith', born: 1948 },
  { name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 },
  { name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
  { name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
];

咱们可使用 compare函数,而后根据 singers 中的 band 字段来进行排序。

function compare(a, b) {
  // 使用 toUpperCase() 忽略字符大小写
  const bandA = a.band.toUpperCase();
  const bandB = b.band.toUpperCase();

  let comparison = 0;
  if (bandA > bandB) {
    comparison = 1;
  } else if (bandA < bandB) {
    comparison = -1;
  }
  return comparison;
}

singers.sort(compare);

/* returns [
  { name: 'Steven Tyler', band: 'Aerosmith',  born: 1948 },
  { name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
  { name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
  { name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 }
] */

若是要让上面的顺序相反,能够这么作:

function compare(a, b) {
  ...

  // 乘以-1来反转返回值
  return comparison * -1;
}

建立一个动态排序函数

最后,排序函数更具动态性。

咱们建立一个排序函数,可使用该函数对一组对象进行排序,这些对象的值能够是字符串或数字。 该函数有两个参数-咱们要排序的键和返回结果的顺序(即升序或降序):

const singers = [
  { name: 'Steven Tyler', band: 'Aerosmith', born: 1948 },
  { name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 },
  { name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
  { name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
];

function compareValues(key, order = 'asc') {
  return function innerSort(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
    );
  };
}

使用:

//数组按`band`排序,默认为升序
singers.sort(compareValues('band'));
// 数组按 `band` 降序排序
singers.sort(compareValues('band', 'desc'));
// 数组按 `name` 升序排序
singers.sort(compareValues('name'));
// 数 组born 降序排序
singers.sort(compareValues('born', 'desc'));

在上面的代码中,hasOwnProperty方法用于检查指定的属性是否在每一个对象上定义,且没有经过原型链继承。若是没有在两个对象上定义,函数返回0,排序顺序保持不变(即对象之间保持不变)。

typeof运算符还用于检查属性值的数据类型,这使函数能够肯定对数组进行排序的正确方法。 若是指定属性的值是一个字符串,则使用toUpperCase方法将其全部字符都转换为大写,所以排序时将忽略字符大小写

最后,你能够根据本身需求来调整上面的函数。

String.prototype.localeCompare()

在上面的示例中,咱们但愿可以对对象数组进行排序,其值能够是字符串或数字。 可是,若是咱们知道处理值是字符串的对象,则可使用 JS 的localeCompare方法

比较两个字符串,并返回下列值中的一个:

若是 字符串 在 字母 表中 应该 排在 字符串 参数 以前, 则 返回 一个 负数; 若是 字符串 等于 字符串 参数, 则 返回 0; 字符串 在 字母 表中 应该 排在 字符串 参数 以后, 则 返回 一个 正数;

['bjork', 'Bjork', 'Björk'].sort();
// [ 'Bjork', 'Björk', 'bjork' ]
['bjork', 'Bjork', 'Björk'].sort((a, b) => a.localeCompare(b));
//  [ 'bjork', 'Bjork', 'Björk' ]

根据compareValues函数,咱们能够这么写:

function compareValues(key, order = 'asc') {
  return function innerSort(a, b) {
    if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0;
    const comparison = a[key].localeCompare(b[key]);

    return (
      (order === 'desc') ? (comparison * -1) : comparison
    );
  };
}

总结

上面就是使用普通JS 函数对对象数组排序的简短的介绍。尽管许多库都提供了这种动态排序能力,但咱们本身实现这个方法其实也不信。另外,了解幕后发生了对咱们来讲并无坏处。

最后

说个题外话,我在一线互联网企业工做十余年里,指导过很多同行后辈。帮助不少人获得了学习和成长。

我意识到有不少经验和知识值得分享给你们,也能够经过咱们的能力和经验解答你们在IT学习中的不少困惑,因此在工做繁忙的状况下仍是坚持各类整理和分享。

我能够将最近整理的前端面试题免费分享出来,其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器、数据结构与算法等等,还在持续整理更新中,但愿你们都能找到心仪的工做。

有须要的朋友点击这里免费领取题目+解析PDF。

点击这里免费领取题目+解析PDF。