メインコンテンツまでスキップ

配列の破壊的操作

JavaScriptの配列メソッドには、破壊的なメソッドと非破壊的なメソッドの2種類があります。特に、破壊的なメソッドは注意深く使う必要があります。

非破壊的なメソッド

破壊的なメソッドは、操作に配列の変更をともなわないメソッドです。たとえば、concatは非破壊的なメソッドです。これは複数の配列を結合するメソッドです。もとの配列は書き換えず、新しい配列を返します。

ts
const nums1 = [1, 2];
const nums2 = [3, 4];
const all = nums1.concat(nums2);
console.log(nums1);
[ 1, 2 ]
console.log(nums2);
[ 3, 4 ]
console.log(all);
[ 1, 2, 3, 4 ]
ts
const nums1 = [1, 2];
const nums2 = [3, 4];
const all = nums1.concat(nums2);
console.log(nums1);
[ 1, 2 ]
console.log(nums2);
[ 3, 4 ]
console.log(all);
[ 1, 2, 3, 4 ]

非破壊的なメソッドの一覧

非破壊的なメソッドには次のものがあります。

メソッド操作
concat2つ以上の配列を結合した配列を返す
find提供されたテスト関数を満たす配列内の最初の要素を返す
findIndex配列内の指定されたテスト関数を満たす最初の要素の位置を返す
lastIndexOf配列中で与えられた要素が見つかった最後のインデックスを返す
slice配列の一部を切り出して返す
includes配列に任意の要素が含まれているかをtruefalseで返す
indexOf引数に与えられた内容と同じ内容を持つ最初の配列要素のインデックスを返す
join全要素を連結した文字列を返す
keys配列のインデックスをArray Iteratorオブジェクトで返す
entries配列のインデックスと値のペアをArray Iteratorオブジェクトで返す
values配列の値をArray Iteratorオブジェクトで返す
forEach与えられた関数を、配列の各要素に対して一度ずつ実行する
filter与えられた関数によって実装されたテストに合格したすべての配列からなる新しい配列を返す
flatすべてのサブ配列の要素を指定した深さで再帰的に結合した新しい配列を返す
flatMap最初にマッピング関数を使用してそれぞれの要素をマップした後、結果を新しい配列内にフラット化する
map与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を返す
every列内のすべての要素が指定された関数で実装されたテストに合格するかどうかをテストする
some配列の少なくともひとつの要素が、指定された関数で実装されたテストに合格するかどうかをテストする
reduce配列のそれぞれの要素に対してユーザーが提供した「縮小」コールバック関数を呼び出す
reduceRightアキュームレーターと配列のそれぞれの値に対して (右から左へ) 関数を適用して、単一の値にする

破壊的なメソッド

破壊的なメソッドは、配列の内容や配列の要素の順番を変更する操作をともなうメソッドです。たとえば、pushは破壊的メソッドの1つです。これは、配列末尾に要素を追加します。

ts
const nums = [1, 2];
nums.push(3);
console.log(nums);
[ 1, 2, 3 ]
ts
const nums = [1, 2];
nums.push(3);
console.log(nums);
[ 1, 2, 3 ]

破壊的なメソッドの一覧

破壊的なメソッドには次のものがあります。

メソッド操作
push配列の末尾に要素を追加する
unshift配列の最初に要素を追加する
pop配列から最後の要素を取り除き、その要素を返す
shift配列から最初の要素を取り除き、その要素を返す
splice要素を取り除いたり、置き換えたり、新しい要素を追加する
sort配列の要素をソートする
reverse配列の要素を逆順に並び替える
fill開始インデックスから終了インデックスまでのすべての要素を、静的な値に変更した配列を返す
copyWithinサイズを変更せずに、配列の一部を同じ配列内の別の場所にシャローコピーして返す

特に要注意な破壊的なメソッド

reverseメソッドは配列を逆順にした配列を返します。戻り値があるので、一見すると破壊なメソッドに見えなくもありません。しかし、このメソッドは配列の順番も逆にしてしまうので注意が必要です。

ts
const nums = [1, 2, 3];
const newNums = nums.reverse();
console.log(nums);
[ 3, 2, 1 ]
console.log(newNums);
[ 3, 2, 1 ]
ts
const nums = [1, 2, 3];
const newNums = nums.reverse();
console.log(nums);
[ 3, 2, 1 ]
console.log(newNums);
[ 3, 2, 1 ]

PHPのarray_reverse関数はJavaScriptのreverseメソッドと名前が同じですが、PHPのほうは破壊的な操作です。もとの配列を変更せずに逆順にソートされた配列を新しく生成して返します。

PHPのarray_reverse
php
$nums = [1, 2, 3];
$reversedNums = array_reverse($nums);
var_dump($nums);
//=> [1, 2, 3]
var_dump($reversedNums);
//=> [3, 2, 1]
PHPのarray_reverse
php
$nums = [1, 2, 3];
$reversedNums = array_reverse($nums);
var_dump($nums);
//=> [1, 2, 3]
var_dump($reversedNums);
//=> [3, 2, 1]

このように、他の言語では非破壊的な配列操作がJavaScriptでは破壊的操作の場合もあります。メソッド名だけで破壊的か非破壊的かを判断せず、各メソッドの使い方をしっかり確認する必要があります。

破壊的なメソッドを安全に使う方法

破壊的なメソッドを破壊的に使うには、破壊的操作を行う前に、配列を別の配列にコピーします。配列のコピーはスプレッド構文...を用います。

📄️ 配列のスプレッド構文「...」

JavaScript の配列ではスプレッド構文「...」を使うことで、要素を展開することができます。

コピーした配列に対して破壊的操作を行えば、もとの配列が変更される心配が無くなります。

ts
const original = [1, 2, 3];
const copy = [...original]; // コピーを作る
copy.reverse();
console.log(original); // 破壊的操作の影響がない
[ 1, 2, 3 ]
console.log(copy);
[ 3, 2, 1 ]
ts
const original = [1, 2, 3];
const copy = [...original]; // コピーを作る
copy.reverse();
console.log(original); // 破壊的操作の影響がない
[ 1, 2, 3 ]
console.log(copy);
[ 3, 2, 1 ]

このreverseの例は、コピーと破壊的なメソッドの呼び出しを1行に短縮して書くこともできます。

ts
const original = [1, 2, 3];
const reversed = [...original].reverse();
console.log(original);
[ 1, 2, 3 ]
console.log(reversed);
[ 3, 2, 1 ]
ts
const original = [1, 2, 3];
const reversed = [...original].reverse();
console.log(original);
[ 1, 2, 3 ]
console.log(reversed);
[ 3, 2, 1 ]
学びをシェアする

・JavaScriptの配列メソッドには、破壊的なものと非破壊的なものがある
・破壊的なものは、配列に変更を加える
・非破壊的なものは、配列に変更を加えない
・非破壊に思えるメソッドが実は破壊的なこともあるから要注意
・配列をコピーしてから破壊的操作をすると安全

『サバイバルTypeScript』より

この内容をツイートする
  • 質問する ─ 読んでも分からなかったこと、TypeScriptで分からないこと、お気軽にGitHubまで🙂
  • 問題を報告する ─ 文章やサンプルコードなどの誤植はお知らせください。