ウェブエンジニア問題集
配列メソッド 4

配列の破壊的メソッド — sort・splice・push と非破壊の代替

6
この章の目次開く

配列メソッドには、元の配列をそのまま残して新しい値を返すものと、元の配列を直接変更するものがあります。 この章では、後者の破壊的メソッドをまとめます。

破壊的メソッド早見表

メソッド何をする非破壊の代替
push末尾に追加[...arr, value] / concat
pop末尾を削除slice(0, -1)
unshift先頭に追加[value, ...arr]
shift先頭を削除slice(1)
splice途中の追加・削除・置換toSpliced()
sort並び替えtoSorted()
reverse逆順にするtoReversed()
fill指定値で埋めるコピーしてから fill
copyWithin配列内で要素をコピー目的に応じて slice / map
arr[i] = value要素を置き換えるwith(i, value)

push / pop — 末尾に追加・削除する

push は末尾に要素を追加し、pop は末尾の要素を取り除きます。 どちらも元の配列を変更します。

const items = ['A', 'B'];
 
items.push('C');
console.log(items); // ["A", "B", "C"]
 
const last = items.pop();
console.log(last);  // "C"
console.log(items); // ["A", "B"]
js

元の配列を変えたくない場合は、スプレッド構文や slice を使います。

const items = ['A', 'B'];
 
const added = [...items, 'C'];
const removedLast = items.slice(0, -1);
 
console.log(items); // ["A", "B"]
js

shift / unshift — 先頭に追加・削除する

unshift は先頭に要素を追加し、shift は先頭の要素を取り除きます。

const queue = ['B', 'C'];
 
queue.unshift('A');
console.log(queue); // ["A", "B", "C"]
 
const first = queue.shift();
console.log(first); // "A"
console.log(queue); // ["B", "C"]
js

非破壊で書くなら、追加は [value, ...arr]、削除は slice(1) です。

const queue = ['B', 'C'];
 
const added = ['A', ...queue];
const removedFirst = queue.slice(1);
js

splice — 途中の追加・削除・置換

splice は配列の途中に対して、削除・追加・置換を行うメソッドです。 柔軟ですが、元の配列を直接変更するため注意が必要です。

構文: array.splice(start, deleteCount, ...items)

const tasks = ['設計', '実装', 'レビュー'];
 
tasks.splice(1, 1, 'テスト');
 
console.log(tasks); // ["設計", "テスト", "レビュー"]
js

splice の戻り値は「削除された要素の配列」です。変更後の配列ではありません。

const items = ['A', 'B', 'C'];
const removed = items.splice(1, 1);
 
console.log(removed); // ["B"]
console.log(items);   // ["A", "C"]
js

ES2023以降は、非破壊の toSpliced が使えます。

const tasks = ['設計', '実装', 'レビュー'];
const updated = tasks.toSpliced(1, 1, 'テスト');
 
console.log(updated); // ["設計", "テスト", "レビュー"]
console.log(tasks);   // ["設計", "実装", "レビュー"]
js

sort — 並び替える

sort は配列を並び替えますが、元の配列を直接変更します。

const scores = [80, 100, 60];
 
scores.sort((a, b) => a - b);
console.log(scores); // [60, 80, 100]
js

数値の並び替えでは、比較関数を必ず渡します。 比較関数なしの sort() は文字列として比較するため、意図しない順番になります。

[1, 20, 3].sort(); // [1, 20, 3] ではなく、文字列順で比較される
 
[1, 20, 3].sort((a, b) => a - b); // [1, 3, 20]
js

元の配列を変えたくない場合は、ES2023の toSorted を使います。

const scores = [80, 100, 60];
const sorted = scores.toSorted((a, b) => a - b);
 
console.log(sorted); // [60, 80, 100]
console.log(scores); // [80, 100, 60]
js

toSorted が使えない環境では、コピーしてから sort します。

const sorted = [...scores].sort((a, b) => a - b);
js

reverse — 逆順にする

reverse は配列の順番を逆にします。 これも元の配列を直接変更します。

const steps = ['入力', '確認', '完了'];
 
steps.reverse();
console.log(steps); // ["完了", "確認", "入力"]
js

非破壊で書くなら toReversed を使います。

const steps = ['入力', '確認', '完了'];
const reversed = steps.toReversed();
 
console.log(reversed); // ["完了", "確認", "入力"]
console.log(steps);    // ["入力", "確認", "完了"]
js

with — 要素の置き換えを非破壊で行う

配列の要素を arr[i] = value で書き換えるのも、元の配列を直接変更する操作です。

const users = ['太郎', '花子', '次郎'];
 
users[1] = '佐藤';
console.log(users); // ["太郎", "佐藤", "次郎"]
js

ES2023の with を使うと、指定した位置だけ置き換えた新しい配列を作れます。

const users = ['太郎', '花子', '次郎'];
const updated = users.with(1, '佐藤');
 
console.log(updated); // ["太郎", "佐藤", "次郎"]
console.log(users);   // ["太郎", "花子", "次郎"]
js

実務パターン: 状態更新では非破壊にする

Reactのstateのように、変更検知が参照の違いに依存する場面では、元配列を直接変更しない書き方が基本です。

// ❌ 元の配列を直接変更している
items.push(newItem);
setItems(items);
 
// ⭕ 新しい配列を作る
setItems([...items, newItem]);
js

削除・並び替え・置き換えも同じです。

// 削除
setItems(items.filter((item) => item.id !== targetId));
 
// 並び替え
setItems(items.toSorted((a, b) => a.order - b.order));
 
// 置き換え
setItems(items.map((item) => (item.id === targetId ? { ...item, done: true } : item)));
js

ちゃんと使うためのポイント

  • push / pop / shift / unshift / splice / sort / reverse / fill は元配列を変更する
  • sort は比較関数を渡す
  • ES2023では toSorted / toReversed / toSpliced / with が非破壊の代替になる
  • UIのstate更新では、元配列を直接変更せず新しい配列を作る

次は、初期化や平坦化で使う 配列の初期化・平坦化メソッド を見ていきます。

参考リンク