JSのソート
こんにちは。kyamashitaです。
JSで配列のソート実装した際に、ちょっとハマったので、備忘録として残しておきます。
単純な数値配列
let items = [5,2,3,1,4];
// 昇順
items.sort((a,b) => a < b ? -1 : 1);
> [1, 2, 3, 4, 5]
// 降順
items.sort((a,b) => a < b ? 1 : -1);
> [5, 4, 3, 2, 1]
連想配列でキーを指定する
let items = [
{"name":"BB", "val":5},
{"name":"EE", "val":2},
{"name":"DD", "val":4},
{"name":"AA", "val":3},
{"name":"CC", "val":1},
];
// val の 昇順
items.sort((a,b) => a.val < b.val ? -1 : 1);
> 0: {name: "CC", val: 1}
> 1: {name: "EE", val: 2}
> 2: {name: "AA", val: 3}
> 3: {name: "DD", val: 4}
> 4: {name: "BB", val: 5}
// val の 降順
items.sort((a,b) => a.val < b.val ? 1 : -1);
> 0: {name: "BB", val: 5}
> 1: {name: "DD", val: 4}
> 2: {name: "AA", val: 3}
> 3: {name: "EE", val: 2}
> 4: {name: "CC", val: 1}
連想配列でキーを指定する2
valの値が数値、文字列、空文字があり、0が重複している。
このような場合うまくいきません。
let items = [
{"name":"D", "val":0},
{"name":"H", "val":"0"},
{"name":"C", "val":""},
{"name":"G", "val":""},
{"name":"B", "val":0},
{"name":"I", "val":""},
{"name":"A", "val":"0"},
{"name":"F", "val":""},
{"name":"E", "val":0},
];
items.sort((a,b) => a.val < b.val ? -1 : 1);
> 0: {name: "D", val: 0}
> 1: {name: "C", val: ""}
> 2: {name: "G", val: ""}
> 3: {name: "F", val: ""}
> 4: {name: "H", val: "0"}
> 5: {name: "B", val: 0}
> 6: {name: "I", val: ""}
> 7: {name: "A", val: "0"}
> 8: {name: "E", val: 0}
下記のような条件をつけるとうまくソートできます。
昇順の場合
function comp(av, bv) {
if (av === '' && bv === '') return 0;
if (av === '') return -1;
if (bv === '') return 1;
return av < bv ? -1 : 1;
}
items.sort((a,b) => comp(a.val, b.val));
> 0: {name: "C", val: ""}
> 1: {name: "G", val: ""}
> 2: {name: "I", val: ""}
> 3: {name: "F", val: ""}
> 4: {name: "D", val: 0}
> 5: {name: "H", val: "0"}
> 6: {name: "B", val: 0}
> 7: {name: "A", val: "0"}
> 8: {name: "E", val: 0}
昇順、降順を指定させる場合はこのような関数にします。
function comp(asc, av, bv) {
if (av === '' && bv === '') return 0;
if (asc) {
if (av === '') return -1;
if (bv === '') return 1;
return av < bv ? -1 : 1;
} else {
if (av === '') return 1;
if (bv === '') return -1;
return av < bv ? 1 : -1;
}
}
// 昇順
items.sort((a,b) => comp(true, a.val, b.val));
> 0: {name: "C", val: ""}
> 1: {name: "G", val: ""}
> 2: {name: "I", val: ""}
> 3: {name: "F", val: ""}
> 4: {name: "D", val: 0}
> 5: {name: "H", val: "0"}
> 6: {name: "B", val: 0}
> 7: {name: "A", val: "0"}
> 8: {name: "E", val: 0}
// 降順
items.sort((a,b) => comp(false, a.val, b.val));
> 0: {name: "E", val: 0}
> 1: {name: "A", val: "0"}
> 2: {name: "B", val: 0}
> 3: {name: "H", val: "0"}
> 4: {name: "D", val: 0}
> 5: {name: "C", val: ""}
> 6: {name: "G", val: ""}
> 7: {name: "I", val: ""}
> 8: {name: "F", val: ""}