オブジェクトの配列を特定の順序でソートしたいとき、すぐに思い浮かぶのは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パラメーターで、配列要素を比較関数の戻り値によってソートできます。
比較関数を使ったソート
aとbを比較関数によって比較される2つの要素です。比較関数の戻り値とaとbの関係は以下になります。
- 0より小さい − aはbの前に来る
- 0より大きい − bはaの前に来る
- 0に等しい − aとbは変更されない
数字の配列の簡単な例で確認します。
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.js、Lodash、Sugar)では、この種の動的ソート機能は提供されていますが、自分で実装するのは難しくありません。
※本記事はMoritz Kröger、Giulio Mainardi、Vildan Softic、Rob Simpsonが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Quick Tip: How to Sort an Array of Objects in JavaScript)
[翻訳:萩原伸悟/編集:Livit]