JavaScript | 配列・オブジェクト:配列の追加・削除 – shift

JavaScript JavaScript
スポンサーリンク

shift とは何か

shift は「配列の先頭の要素を1つ取り出して、配列から削除する」メソッドです。取り出した要素を返し、配列の長さを1つ減らします。ここが重要です:shift は“破壊的操作”で、元の配列を直接書き換えます。空配列に対して shift を呼ぶと、戻り値は undefined で、長さは 0 のままです。さらに、shift は“先頭”に触るため全要素を詰め直すコストが発生します。大量データでは末尾操作(push/pop)より遅くなりがちです。


基本の使い方と戻り値の扱い

先頭の要素を取り出す

const a = [10, 20, 30];
const first = a.shift();
console.log(first); // 10(取り出した先頭)
console.log(a);     // [20, 30](先頭が削除された)
JavaScript

ここが重要です:戻り値は「削除された先頭要素」です。取り出した後の新しい先頭は a[0] や a.at(0) で参照できます。

空配列の shift(安全に判定する)

const a = [];
const x = a.shift();
console.log(x);         // undefined
console.log(a.length);  // 0
JavaScript

ここが重要です:x が 0 や “” のような“falsy”でも正当な要素になり得ます。存在判定は if (x === undefined) や if (a.length === 0) のように、undefined または length を使うと安全です。


破壊的操作の注意と非破壊の代替

共有参照への影響(同じ配列が同時に変わる)

const a = [1, 2, 3];
const alias = a; // 同じ配列参照
a.shift();
console.log(alias); // [2, 3](alias も同時に変化)
JavaScript

ここが重要です:状態管理(React/Vue)や別の変数と共有している配列で shift を使うと、副作用で不具合が起きやすくなります。非破壊で「先頭を除いた新配列」を作る代替が安全です。

非破壊で“先頭を除いた新配列”を得る

const a = [1, 2, 3];
const withoutFirst = a.slice(1); // [2, 3]
const also = [...a].slice(1);    // スプレッド+slice でも同様
JavaScript

ここが重要です:slice(1) は元配列を壊さずに“最初の1件を除いた新配列”を返します。UIの差分検知や副作用回避に向いています。


shift と他メソッドの違い(使い分けの基準)

pop(末尾から取り出す)との比較

const a = [10, 20, 30];
console.log(a.shift()); // 10(先頭)
console.log(a.pop());   // 30(末尾)
JavaScript

ここが重要です:先頭操作(shift/unshift)は全体を詰めたりずらしたりするため、配列が大きいほど遅くなりがちです。性能重視なら末尾操作(push/pop)に寄せる設計が定石です。

先頭参照だけなら at(0) を使う

const a = [10, 20, 30];
const head = a.at(0); // 10(参照のみ、削除しない)
JavaScript

ここが重要です:値を見るだけなら at(0)。削除したいときだけ shift を選ぶと、意図が明確になり、誤破壊を防げます。


実践例(キュー・ストリーム処理・入力取り込み)

キュー(FIFO)の基本操作

const queue = [];
queue.push("A"); // 入れる(末尾に追加)
queue.push("B");
console.log(queue.shift()); // "A"(先に入れたものから出る)
console.log(queue.shift()); // "B"
JavaScript

ここが重要です:push で入れて shift で出すと「先入れ先出し(FIFO)」が実現できます。メッセージ処理やタスク実行の基本パターンです。

ストリームのように先頭から順次処理

const jobs = ["parse", "validate", "save"];
while (jobs.length > 0) {
  const job = jobs.shift();
  run(job);
}
JavaScript

ここが重要です:length を条件にすれば、空配列でも安全に終了します。先頭から順に処理する意図がコードにそのまま表れます。

“先頭 n 件だけ取り出す”短縮テク

function shiftN(arr, n) {
  const taken = [];
  for (let i = 0; i < n && arr.length > 0; i++) {
    taken.push(arr.shift());
  }
  return taken; // 取り出した順(先頭から)
}
const a = [1, 2, 3, 4];
console.log(shiftN(a, 2)); // [1, 2]
console.log(a);            // [3, 4]
JavaScript

ここが重要です:必要数と残数を両方考慮できます。返り値の順序は「先頭からの順」です。


性能と設計の指針(大きな配列・頻繁な先頭操作への対策)

先頭操作は高コストという前提を持つ

配列は“連続したインデックス”を前提とするため、先頭を削ると残りを詰める再割り当てが起きます。大量データや高頻度の shift/unshift があるワークロードでは、末尾中心の設計(push/pop)に寄せる、または両端キュー(Deque)のデータ構造を用意する選択肢を検討してください。

擬似キューを末尾中心で実装する

// 末尾を“頭”とみなす擬似キュー(性能重視)
const q = [];
const enqueue = (x) => q.push(x);  // 入れる
const dequeue = () => q.pop();     // 出す(末尾を頭として扱う)
JavaScript

ここが重要です:実際の順序要件次第ですが、可能なら末尾操作(高速)のみで設計することで性能を稼げます。順序の定義を明確にしてから選択しましょう。


よくある落とし穴と回避策

空配列で shift して undefined を“意味のある値”と誤判定することがあります。戻り値の判定は x === undefined、または length を条件にしてください。参照共有のまま shift すると、同じ配列を使っている他所のロジックや画面が同時に変化して不具合が起こります。非破壊の代替(slice(1))を選ぶと安全です。大量データで shift/unshift を多用するとパフォーマンスが落ちます。キューが本質ならデータ構造の見直し(Deque)、別解なら末尾中心の設計へ切り替えましょう。“先頭を見たいだけ”なのに shift を使って削ってしまう誤操作が起こりがちです。参照は at(0)、削除は shift、と役割をはっきり分けるのがコツです。


まとめ

shift は「先頭を取り出して削除する」破壊的メソッドで、戻り値に取り出した要素を返します。空配列なら undefined を返すため、判定は length か undefined 比較で安全に。性能面では先頭操作は高コストになりやすいため、可能なら末尾中心(push/pop)へ寄せるか、適切なデータ構造を検討してください。共有状態では非破壊の代替(slice(1))を選び、単なる参照には at(0) を使う。これらを押さえれば、初心者でも読みやすく壊れにくい「先頭削除」のロジックを正しく書けます。

タイトルとURLをコピーしました