JavaScriptのソート処理
JavaScriptのソート処理するなら、必ずArray.prototype.sort()
を使うだろう。
JavaScriptに限らず、たいていのプログラミング言語にはこれに類似するソート処理を実行できるメソッドが用意されているが、仕事でちょっとしたミスプログラムを発見して見逃していたので、今後のために備忘録として残しておくことにする。
備忘録1: ソート前の配列がin placeでソートされる
ソートされた配列が返却されるが、ソート前の配列もソートされた状態になっている(in place)ため、戻り値を再代入しなくてよい。
というか、再代入すると静的解析ツールなどが警告してくることがある。
戻り値はメソッドチェインするときくらいしか意識しなくてよいだろう。
let list1 = [4, 2, 3, 1, 5];
list1.sort();
console.log(list1); // [1, 2, 3, 4, 5]
let list2 = ['a', '', 'bbb', 'A', 'z'];
list2.sort();
console.log(list2); // ["", "A", "a", "bbb", "z"]
備忘録2: 昇順にしたいなら a<b で -1 を!
ソートしたい配列の要素がオブジェクトなど構造を持っているときは、並び順を教えるためのコールバック関数を引数に指定するわけだが、このコールバック関数の戻り値が「昇順の場合は1
なのか -1
なのか」といつも忘れるので備忘録に残す。
let list = [4, 2, 3, 1, 5];
list.sort((a, b) => {
return (a < b) ? -1 : 1; // 昇順ソートの場合はこれ!!
// return a - b; // これでもいい
});
console.log(list); // [1, 2, 3, 4, 5]
備忘録3: 複数条件でソート
たとえば、以下のように「入社年度」と「社員名」の2つのキーを持つオブジェクトの配列を「まず入社年度で昇順に並べ、同じなら社員名で昇順に並べたい」というような、カテゴリの概念を持ったオブジェクトの配列を複数条件でソートしたいときがある。
ソート前
入社年度 | 社員名 |
---|---|
2020 | 社員A |
2019 | 社員B |
2020 | 社員C |
2019 | 社員D |
ソート後
入社年度 | 社員名 |
---|---|
2019 | 社員B |
2019 | 社員D |
2020 | 社員A |
2020 | 社員C |
このような場合は、まずカテゴリを優先して比較し、カテゴリが同一の場合のみ次の条件で比較することで対応できる。
let list = [{
item: 2,
category: 2,
}, {
item: 1,
category: 1,
}, {
item: 4,
category: 2,
}, {
item: 3,
category: 1,
}];
list.sort((a, b) => {
if (a.category < b.category) {
return -1;
} else if (a.category > b.category) {
return 1;
} else if (a.item < b.item) {
return -1;
} else if (a.item > b.item) {
return 1;
}
return 0;
});
console.log(list);
[{
category: 1,
item: 1
}, {
category: 1,
item: 3
}, {
category: 2,
item: 2
}, {
category: 2,
item: 4
}]
これを覚えておけば、サブカテゴリーが増えても対応できるはず。
おまけ
複数条件でソートするVuejsの検証コード抜粋(vue-property-decorator
使用)
<table>
<tbody>
<tr>
<th @click="sortList">カテゴリ名</th>
<th @click="sortList">項目名</th>
</tr>
<tr v-for="(item, i) in itemList" :key="i">
<td :style="item.categoryStyle">{{ item.category }}</td>
<td :style="item.itemStyle">{{ item.item }}</td>
</tr>
</tbody>
</table>
public itemList = [];
public sortList() {
this.itemList.sort((a, b) => {
if (a.category < b.category) {
return -1;
} else if (a.category > b.category) {
return 1;
} else if (a.item < b.item) {
return -1;
} else if (a.item > b.item) {
return 1;
}
return 0;
});
}
/** 乱数取得 */
private getRandomInt(min: number, max: number): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
public created() {
// 乱数でデータ作成
for (let i = 0; i < 100; i++) {
const c = this.getRandomInt(1, 10);
const s = this.getRandomInt(1, 100);
this.itemList.push({
item: s,
itemStyle: `color:white;background:rgb(0,0,${255 - s * 2})`,
category: c,
categoryStyle: `color:white;background:rgb(0,${255 - c * 10},0)`,
});
}
}