プログラミング

[JavaScript] null が最後に配置されるように配列を並び替える方法

sort() メソッドの挙動を理解する

JavaScript の配列の並び替えは sort() メソッドを使うことが多いと思いますが、

配列の要素に null が含まれる場合に、null を最後に配置したいな〜と思っていろいろ試行錯誤してたらなかなか面白いことが分かったので忘れないうちにまとめておこうと思います。

前回ブログはこちらから↓

単純に使ってみる

sort() メソッドのデフォルトのソート順は昇順だそうですが、sort() と書いただけではなぜか1桁目しか比較されず変なことに。

const array = [2, null, 50, 34, null, 105, 350]
console.log(array.sort()) // [105, 2, 34, 350, 50, null, null]

昇順で並び替える(うまくいかない)

明確に昇順で並び替えるよう記載したら null が最初に来る。

const array = [2, null, 50, 34, null, 105, 350]
console.log(array.sort((a, b) => a - b))  // [null, null, 2, 34, 50, 105, 350]

降順で並び替える(うまくいったけどちょっと微妙)

明確に降順で並び替えるよう記載したら null が最後に来る。

const array = [2, null, 50, 34, null, 105, 350]
console.log(array.sort((a, b) => b - a))  // [350, 105, 50, 34, 2, null, null]

けど、これじゃ昇順で並び替えて null は最後に配置したいってのが叶えられないですね。

昇順で並び替えて null は最後に配置する最適解

こうするときちんと昇順に並び替えられて、null が最後に来る。

const array = [2, null, 50, 34, null, 105, 350]
console.log(array.sort((a, b) => {
  if(a === null){
    return 1
  }
  if(b === null){
    return -1
  }
  if(a === b){
    return 0
  }
  return a < b ? -1 : 1
}))  // [2, 34, 50, 105, 350, null, null]

降順で並び替えて null は最後に配置する最適解

降順にする場合は、最後の return 文の値をちょっと変えれば良い。

const array = [2, null, 50, 34, null, 105, 350]
console.log(array.sort((a, b) => {
  if(a === null){
    return 1
  }
  if(b === null){
    return -1
  }
  if(a === b){
    return 0
  }
  return a < b ? 1 : -1 // ←ここを変更
}))  // [350, 105, 50, 34, 2, null, null]

昇順で並び替えて null は最後に配置する(オブジェクトの場合)

要素がオブジェクトの場合でも同様に null を最後に配置することができます。

下記のように個人の出退勤を模したようなデータで、DATETIME型の値を持つケースを例に見てみます。in の値を基準に昇順に並び替えると…

const array = [
  {name: '高木', in: '2022-08-22 10:49:00', out: '2022-08-22 18:30:00'},
  {name: '鈴木', in: '2022-08-22 09:50:00', out: null},
  {name: '松本', in: '2022-08-22 08:44:00', out: '2022-08-22 22:40:00'},
  {name: '染谷', in: null, out: '2022-08-22 23:23:00'},
  {name: '谷口', in: '2022-08-22 06:10:00', out: '2022-08-22 20:23:00'},
  {name: '林', in: '2022-08-22 07:35:00', out: null},
  {name: '佐藤', in: null, out: '2022-08-22 16:23:00'},
]
// 昇順に並び替え
console.log(array.sort((a, b) => {
  if(a.in === null){
    return 1;
  }
  if(b.in === null){
    return -1;
  }
  if(a.in === b.in){
    return 0;
  }
  return a.in < b.in ? -1 : 1;
}))

このように昇順で null 値は最後に配置されました。

[
 {name: '谷口', in: '2022-08-22 06:10:00', out: '2022-08-22 20:23:00'},
 {name: '林', in: '2022-08-22 07:35:00', out: null},
 {name: '松本', in: '2022-08-22 08:44:00', out: '2022-08-22 22:40:00'},
 {name: '鈴木', in: '2022-08-22 09:50:00', out: null},
 {name: '高木', in: '2022-08-22 10:49:00', out: '2022-08-22 18:30:00'},
 {name: '染谷', in: null, out: '2022-08-22 23:23:00'},
 {name: '佐藤', in: null, out: '2022-08-22 16:23:00'}
]

降順で並び替えて null は最後に配置する(オブジェクトの場合)

const array = [
  {name: '高木', in: '2022-08-22 10:49:00', out: '2022-08-22 18:30:00'},
  {name: '鈴木', in: '2022-08-22 09:50:00', out: null},
  {name: '松本', in: '2022-08-22 08:44:00', out: '2022-08-22 22:40:00'},
  {name: '染谷', in: null, out: '2022-08-22 23:23:00'},
  {name: '谷口', in: '2022-08-22 06:10:00', out: '2022-08-22 20:23:00'},
  {name: '林', in: '2022-08-22 07:35:00', out: null},
  {name: '佐藤', in: null, out: '2022-08-22 16:23:00'},
]
// 降順に並び替え
console.log(array.sort((a, b) => {
  if(a.in === null){
    return 1
  }
  if(b.in === null){
    return -1
  }
  if(a.in === b.in){
    return 0
  }
  return a.in < b.in ? 1 : -1
}))

console.logの結果はこちら↓

[
 {name: '高木', in: '2022-08-22 10:49:00', out: '2022-08-22 18:30:00'},
 {name: '鈴木', in: '2022-08-22 09:50:00', out: null},
{name: '松本', in: '2022-08-22 08:44:00', out: '2022-08-22 22:40:00'},
{name: '林', in: '2022-08-22 07:35:00', out: null},
{name: '谷口', in: '2022-08-22 06:10:00', out: '2022-08-22 20:23:00'},
{name: '染谷', in: null, out: '2022-08-22 23:23:00'},
{name: '佐藤', in: null, out: '2022-08-22 16:23:00'}
]

配列を壊したくない場合

sort() メソッドは、破壊的メソッドなので使用すると元々の配列を壊してしまいます。

const fruit = ['grape', 'apple', 'orange', 'cherry', 'prune']
console.log(fruit.sort()) // ['apple', 'cherry', 'grape', 'orange', 'prune']
console.log(fruit)  // ['apple', 'cherry', 'grape', 'orange', 'prune'] ←並び順が変更されている!

元々の配列は壊したくないんだよな〜というケースは多々あると思うので、そういった場合はひと工夫必要になる。

slice() メソッドと併用

sort() の前に slice() を使うと、元々の配列が保たれる。

const fruit = ['grape', 'apple', 'orange', 'cherry', 'prune']
console.log(fruit.slice().sort()) // ['apple', 'cherry', 'grape', 'orange', 'prune']
console.log(fruit) // ['grape', 'apple', 'orange', 'cherry', 'prune'] ←元の並び順が保たれている

スプレッド構文を使用

スプレッド構文を使って配列を個々の要素を展開してあげると元々の配列が保たれる。

const fruit = ['grape', 'apple', 'orange', 'cherry', 'prune']
console.log([...fruit].sort()) // ['apple', 'cherry', 'grape', 'orange', 'prune']
console.log(fruit)  // ['grape', 'apple', 'orange', 'cherry', 'prune'] ←元の並び順が保たれている

Array.from() メソッドを使用

Array.from() を使って配列の浅いコピーを生成してあげると元々の配列が保たれる。

const fruit = ['grape', 'apple', 'orange', 'cherry', 'prune']
console.log(Array.from(fruit).sort()) // ['apple', 'cherry', 'grape', 'orange', 'prune']
console.log(fruit)  // ['grape', 'apple', 'orange', 'cherry', 'prune'] ←元の並び順が保たれている

まとめ

1つのメソッドを取ってみただけでこんなにいろいろできちゃうなんて奥が深すぎてまだまだお勉強が足りませんね、、

全然関係ないけど、JavaScript 書くときの末尾コロン付けるか付けないか問題が自分の中で統一されていない…

ここまでお読みいただき、ありがとうございました!