吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】配列について Vol.3(配列の要素をソートするメソッド)

2018年2月19日 / category : javascript

Arrayオブジェクトのソート系のメソッドの解説をします。まずは配列の要素の順番を逆にするメソッドreverseを解説します。

配列の要素の順番を逆にする

array.reverse():配列の要素の順番を逆にする
戻り値(return) : 反転された配列(array)
構文

[1,2,3,4].reverse();

sample.js
const data = [100,200,300,400,500,600,700,800,900];
const newData = data.reverse();
console.log( data ); // (9) [900, 800, 700, 600, 500, 400, 300, 200, 100]
console.log( newData ); // (9) [900, 800, 700, 600, 500, 400, 300, 200, 100]

上のコードを見ていただくとわかりますが、メソッドreverseの対象となった元の配列dataの要素の順番も逆になっています。
続いて、ソートするメソッドsortを解説します。

配列の要素をソートする

array.sort():配列をソートする
引数 compare(function) : ソート順を処理する関数(任意)
 a : 比較する配列の要素
 b : 比較する配列の要素
戻り値(return) : ソートされた配列(array)
構文

[1,2,3,4].sort(compare);

sample.js
const data = [100,900,300,800,600,500,700,400,200];
const newData = data.sort();
console.log( data ); // (9) [100, 200, 300, 400, 500, 600, 700, 800, 900]
console.log( newData ); // (9) [100, 200, 300, 400, 500, 600, 700, 800, 900]

上のコードでは、メソッドsortに引数を設定していません。引数を設定しない場合は、要素の文字列比較に基づいて辞書順にソートされます。
そのため、次のコードでは意図しないソート結果になります。

sample.js
const data = [2000,10,300];
data.sort();
console.log( data ); // (3) [10, 2000, 300]

上の結果ですが、期待していたソート結果が降順であれば[2000, 300, 10]、昇順であれば[10, 300, 2000]になるはずですが、実際の結果は上の通りです。これは先ほど解説した要素の文字列比較に基づいて辞書順にソートされるためです。この結果を降順または昇順にしたい場合は、引数に関数を指定する必要があります。

sample.js
const data = [2000,10,300,40,500,6,70,800,9];
function compare(a, b){
	console.log(a, b, (a - b));
	return a - b;
};
data.sort(compare);
console.log( data ); // (9) [6, 9, 10, 40, 70, 300, 500, 800, 2000]

コンソール画面

2000 10 1990
2000 300 1700
10 300 -290
2000 40 1960
300 40 260
10 40 -30
2000 500 1500
300 500 -200
2000 6 1994
500 6 494
300 6 294
40 6 34
10 6 4
2000 70 1930
500 70 430
300 70 230
40 70 -30
2000 800 1200
500 800 -300
2000 9 1991
800 9 791
500 9 491
300 9 291
70 9 61
40 9 31
10 9 1
6 9 -3

上の結果を見ると、配列dataは昇順にソートされています。比較関数である関数compareでは何が起こっているでしょうか。関数内のコンソールを見てみます。
returnの値はa-bの返り値です。初回の実行では、インデックス0の要素2000とインデックス1の要素10を比較し、a-bを計算した結果1990が出ます。この結果が0よりも大きかった場合、bをaより小さい値にソートします。

上の図が、初回の関数compareの実行結果です。続いて、2回目の実行を図にしてみます。

初回の実行で入れ替わったインデックス1の要素2000とインデックス2の要素300を比較しています。これも先ほどと同様、結果が0よりも大きいためbをaより小さい値にソートしています。
続いて、3回目の実行を見てみます。

2回目の実行でインデックス1の要素が入れ替わったため3回目の関数の実行ではインデックス0とインデックス1の要素が比較対象になっています。また、a-bを計算した結果-290になりました。0よりも小さかった場合は、aをbより小さい値にソートします。上の場合は、入れ替える必要がないため実行前と実行後で入れ替えは行われません。
この仕組みを必要分、関数が実行されます。その結果、昇順にソートされた配列になります。
降順にソートする場合は、比較関数を次のように変更します。

sample.js
const data = [2000,10,300,40,500,6,70,800,9];
function compare(a, b){
	return b - a;
};
data.sort(compare);
console.log( data ); // (9) [2000, 800, 500, 300, 70, 40, 10, 9, 6]

上のように比較関数compareのreturnの値の条件を変更しました。
ソートの条件を次にまとめます。

結果が0よりも大きい 結果が0よりも小さい 結果が0と等しい
aをbより後ろにソート aをbより前にソート a,bともに変化なし

オブジェクトの特定の値を基準に配列の要素をソートする

要素にオブジェクトが代入された配列を扱うことがあります。その配列をオブジェクトの特定の値を基準にしてソートしたい場合は、次のようにします。

sample.js
let data = [
	{name: '田中', kana: 'たなか', age: 50},
	{name: '佐藤', kana: 'さとう', age: 20},
	{name: '藤原', kana: 'ふじわら', age: 42},
	{name: '吉本', kana: 'よしもと', age: 37},
	{name: '鈴木', kana: 'すずき', age: 12},
	{name: '高橋', kana: 'たかはし', age: 32}
];

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

data.sort(compare);
console.log( data );

コンソール画面

(6) [{...}, {...}, {...}, {...}, {...}, {...}]
	0:{name: "鈴木", kana: "すずき", age: 12}
	1:{name: "佐藤", kana: "さとう", age: 20}
	2:{name: "高橋", kana: "たかはし", age: 32}
	3:{name: "吉本", kana: "よしもと", age: 37}
	4:{name: "藤原", kana: "ふじわら", age: 42}
	5:{name: "田中", kana: "たなか", age: 50}

上のコードは、オブジェクトのプロパティageを基準にソートしたコードになっています。比較関数compareの引数a,bには、それぞれ要素であるオブジェクトが代入されていますので、a.age, b.ageといった指定で参照することができます。ソートに関しては、先ほど解説した昇順の順にソートされています。
続いて、プロパティkanaを基準にソートしてみます。

sample.js
let data = [
	{name: '田中', kana: 'たなか', age: 50},
	{name: '佐藤', kana: 'さとう', age: 20},
	{name: '藤原', kana: 'ふじわら', age: 42},
	{name: '吉本', kana: 'よしもと', age: 37},
	{name: '鈴木', kana: 'すずき', age: 12},
	{name: '高橋', kana: 'たかはし', age: 32}
];

function compare(a,b){
	if( a.kana < b.kana ){
		return -1;
	};
	if( a.kana > b.kana ){
		return 1;
	};
	return 0;
};

data.sort(compare);
console.log( data );

コンソール画面

(6) [{...}, {...}, {...}, {...}, {...}, {...}]
	0:{name: "佐藤", kana: "さとう", age: 20}
	1:{name: "鈴木", kana: "すずき", age: 12}
	2:{name: "高橋", kana: "たかはし", age: 32}
	3:{name: "田中", kana: "たなか", age: 50}
	4:{name: "藤原", kana: "ふじわら", age: 42}
	5:{name: "吉本", kana: "よしもと", age: 37}

プロパティkanaの値が数値ではないため、return文で直接計算させることができません。そのためif文で分岐をし、returnに直接-1,1,0のいずれかを返すようにしています。