2:00am, Writing code

Roughly coding. Posted from Berlin.

Javascriptで非破壊ソートを実装する

Javascriptで数値配列をソートする - 2:00am, Writing codeでも少し触れたが、JavascriptArray.sortは破壊的なソートなので、配列そのものの値を変更してしまう。 それでも問題ない場合も多いかもしれないが、うっかり配列を破壊してしまうことにより問題が発生することもあるだろうし、その手のバグは往々にして発見しづらいので厄介だ。何よりFunctional Programming的な観点で言うと無駄な副作用の発生は極力避けたいところである。

ということで今回はJavascriptで非破壊ソートを実装してみる。

Array.slice()で配列をコピー

元の配列を変更しないようにするためには、元の配列をコピーしてコピー後の配列をソートすればよい。ざっと検索してみると数年前までは以下のようなArray.slice()でコピーした配列をソートすることで非破壊ソートするのが主流だった模様。

const arr = [3, 2, 8, 4, 6, 1, 3, 20, 14];
console.log(arr.slice().sort((a, b) => a - b)); // [ 1, 2, 2, 3, 3, 6, 7, 9, 12, 16, 22 ]
console.log(arr); // [ 3, 2, 8, 4, 6, 1, 3, 20, 14 ]

Object.assignで配列をコピー

ES6ではObject.assignメソッドによるオブジェクトのコピーができるようになったので、今後はこっちの書き方も目にする機会が増えてくると思われる。今回は配列のコピーだが実際にはオブジェクトのコピーをする場合に目にすることの方が多いはず。

const arr = [3, 2, 8, 4, 6, 1, 3, 20, 14];
console.log(Object.assign([], arr).sort((a, b) => a - b)); // [ 1, 2, 2, 3, 3, 6, 7, 9, 12, 16, 22 ]
console.log(arr); // [ 3, 2, 8, 4, 6, 1, 3, 20, 14 ]

Object.assignは第1引数のオブジェクトに第2引数の直接所有かつ列挙可能な要素をコピーする。上記の例では空の配列に第2引数の配列の全要素をコピーした新しい配列を呼び出し元に返すという意味になる。

参考

[Sy] JavaScriptのArrayをソートした結果が欲しいけど、元の配列もそのままキープしたい(非破壊的にソート) | Syntax Error. Array.sliceの仕様にも触れられており調べた中では一番わかりやすかった

Object.assign() - JavaScript | MDN