如何在 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。

相關文章
相關標籤/搜索