意外と簡単にできる、JavaScriptでオブジェクトの配列を並べ替える方法

2017/04/03

Olayinka Omole

90

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA ASCII Research Laboratories, Inc. Japanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

あれ、どうやってやるんだっけ…?というときのためにメモしておくと便利な、ちょっとしたTips。

オブジェクトの配列を特定の順序でソートしたいとき、すぐに思い浮かぶのはJavaScriptライブラリーを使う方法です。しかし、その前に覚えておいて欲しいことは、ネイティブのArray.sort関数を使っても、きれいに並べ替えができることです。この記事では、JavaScriptで簡単にオブジェクトの配列を並べ替える方法について説明します。

この記事を読むには、変数の宣言、関数の記述、条件文など、基本的なJavaScriptの概念を理解している必要があります。またES6の構文も使用します。ES6の構文については、こちらで復習できます。

基本的な配列のソート

JavaScriptのArray.sort関数はデフォルトでは、配列内の各要素を文字列に変換し、Unicodeの符号位置の順に比較してソートします。

const foo = [9, 2, 3, 'random', 'panda'];
foo.sort(); // returns [ 2, 3, 9, 'panda', 'random' ]

const bar = [4, 19, 30, function(){}, {key: 'value'}];
bar.sort(); // returns [ 19, 30, 4, { key: 'value' }, [Function] ]

なぜ4の前に30が来るのだろうか、論理的ではないのでは? と疑問に思うかもしれません。正解です。これは、配列内の各要素が最初に文字列に変換されて"30"がUnicode順で"4"より前に来るために起こることです。

またArray.sortは、ほかの多くのJavaScriptの配列関数とは違って、実際の配列を変更してソート順を変えてしまうことにも注意する必要があります。

const baz = ['hello world', 31, 5, 9, 12];
baz.sort(); // baz array is modified
console.log(baz); // shows [12, 31, 5, 9, "hello world"]

この現象を避けるために、並べ替える配列の新しいインスタンスを生成し、代わりに操作します。

const baz = ['hello world', 31, 5, 9, 12];
const newBaz = baz.slice().sort(); // new instance of baz array is created and sorted
console.log(baz); // "hello world", 31, 5, 9, 12]
console.log(newBaz); // [12, 31, 5, 9, "hello world"]

デモ

デモはこちらで確認してください。

オブジェクトの配列をソートするのにArray.sortを単独で使ってもはあまり便利ではありません。しかし、幸いにも、この関数はオプションのcompareFunctionパラメーターで、配列要素を比較関数の戻り値によってソートできます。

比較関数を使ったソート

abを比較関数によって比較される2つの要素です。比較関数の戻り値とabの関係は以下になります。

  1. 0より小さい − abの前に来る
  2. 0より大きい − baの前に来る
  3. 0に等しい − abは変更されない

数字の配列の簡単な例で確認します。

const arr = [1,2,30,4];

function compare(a, b){
  let comparison = 0;

  if (a > b) {
    comparison = 1;
  } else if (b > a) {
    comparison = -1;
  }

  return comparison;
}

arr.sort(compare);
// => 1, 2, 4, 30

これはaからbを減算した戻り値を使うことで、多少リファクタリングできます。

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

アロー関数を使って以下のように書けます。

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

JavaScriptでオブジェクトの配列をソートする

次にオブジェクトの配列をソートしていきます。以下のbandオブジェクトの配列を使います。

const bands = [ 
  { genre: 'Rap', band: 'Migos', albums: 2},
  { genre: 'Pop', band: 'Coldplay', albums: 4},
  { genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];

次の比較関数を使って、オブジェクトの配列をgenreごとにソートできます。

function compare(a, b) {
  // Use toUpperCase() to ignore character casing
  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) {
  ...

  //invert return value by multiplying by -1
  return comparison * -1; 
}

デモ

デモはこちらで確認してください。

動的ソート機能の作成

最後に動的ソート関数を作成して終わりにします。文字列または数値を含んだオブジェクトの配列をソートできるソート関数を作ります。この関数には、ソートするキーと結果の順序(昇順または降順)の2つのパラメータがあります。

const bands = [ 
  { genre: 'Rap', band: 'Migos', albums: 2},
  { genre: 'Pop', band: 'Coldplay', albums: 4, awards: 13},
  { genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];

// function for dynamic sorting
function compareValues(key, order='asc') {
  return function(a, b) {
    if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
      // property doesn't exist on either object
        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')); 

デモ

デモはこちらで確認してください。

デモのコードは、hasOwnPropertyメソッドを使用して、指定されたプロパティが各オブジェクトに定義されていることと、プロトタイプチェーンによって継承されていないかどうかを確認しています。オブジェクトに定義されていない場合、関数は0を返してソート順がそのまま維持されます(つまり、オブジェクトは残り、互いに変更されません)。

typeof演算子は、プロパティ値のデータ型を確認するために使われます。これにより、関数は正しい方法で配列をソートできます。たとえば、指定されたプロパティの値がstringの場合toUpperCaseメソッドを使用してすべての文字は大文字に変換されるので、ソート時に文字の大小は無視されます。

上記の関数を調整して、ほかのデータ型や独自のスクリプトにも対応できます。

最後に

この記事では、オブジェクトの配列を簡単にソートする方法を紹介しました。多くのJavaScriptライブラリー(Underscore.jsLodashSugar)では、この種の動的ソート機能は提供されていますが、自分で実装するのは難しくありません。

※本記事はMoritz KrögerGiulio MainardiVildan SofticRob Simpsonが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。

(原文:Quick Tip: How to Sort an Array of Objects in JavaScript

[翻訳:萩原伸悟/編集:Livit

Copyright © 2017, Olayinka Omole All Rights Reserved.

Olayinka Omole

Olayinka Omole

ソフトウェア開発、デザイン、エレクトロニクス工学、人工知能に興味があるナイジェリア・ラゴスのエンジニアです。ツイートしたり、学術研究をしたり、写真を撮ったり、デザインしたりコードを書いたりして、日常を楽しんでいます。

Loading...