JavaScriptの配列操作は、Array のメソッド(push・find・map・toSorted など)と、スプレッド構文 ...、Object.groupBy などの組み合わせで書けます。 選ぶときに一番大事なのは、そのメソッドが「元の配列を変える(破壊的)」か「新しい配列を返す(非破壊)」かです。

この記事では、配列のメソッドを目的別(作成・追加・削除・検索・判定・並べ替え・変換・結合・集計・コピー)の一覧表に「破壊的か」「戻り値」「ES版」をつけて整理し、すべての例をNode.js 22(v22.22.2)で実行した出力つきで紹介します。ES2023〜ES2026の新メソッドや、sort の数値の並び、NaN の検索、浅いコピーのつまずきも実行結果で確認します。

JavaScript 配列操作の早見表(目的別・全メソッド)

「破壊的」が「する」のメソッドは元の配列を書き換えます。ES版はECMAScriptの版(ES1〜ES5は2009年までの版)です。

目的 メソッド 破壊的 戻り値 ES版
作成 Array.from(反復可能, 変換) - 新しい配列 ES2015
Array.of(...要素) - 新しい配列 ES2015
Array.fromAsync(非同期反復可能) - Promise(配列) ES2026
fill(値, 開始, 終了) する 元の配列 ES2015
追加 push(...要素)(末尾) する 新しい長さ ES3
unshift(...要素)(先頭) する 新しい長さ ES3
削除 pop()(末尾) する 取り出した要素 ES3
shift()(先頭) する 取り出した要素 ES3
追加・削除・置換 splice(位置, 個数, ...追加) する 削除した要素の配列 ES3
toSpliced(位置, 個数, ...追加) しない 新しい配列 ES2023
with(位置, 値)(1つ置き換え) しない 新しい配列 ES2023
検索 find(条件) / findLast(条件) しない 要素 or undefined ES2015 / ES2023
findIndex(条件) / findLastIndex(条件) しない 位置 or -1 ES2015 / ES2023
indexOf(値) / lastIndexOf(値) しない 位置 or -1 ES5
at(位置)(負の数で末尾から) しない 要素 ES2022
判定 includes(値) しない 真偽値 ES2016
some(条件) / every(条件) しない 真偽値 ES5
Array.isArray(値) - 真偽値 ES5
並べ替え sort(比較関数) / reverse() する 元の配列 ES1
toSorted(比較関数) / toReversed() しない 新しい配列 ES2023
copyWithin(先, 開始, 終了) する 元の配列 ES2015
変換 map(関数) しない 新しい配列 ES5
filter(条件) しない 新しい配列 ES5
flat(深さ) / flatMap(関数) しない 新しい配列 ES2019
結合・分割 concat(...配列) しない 新しい配列 ES3
slice(開始, 終了) しない 新しい配列 ES3
join(区切り) しない 文字列 ES1
集計 reduce(関数, 初期値) / reduceRight しない 集計結果 ES5
Object.groupBy(配列, 関数) - オブジェクト ES2024
Map.groupBy(配列, 関数) - Map ES2024
反復 forEach(関数) しない undefined ES5
keys() / values() / entries() しない イテレーター ES2015
コピー [...arr] / arr.slice() / Array.from(arr) しない 浅いコピー ES2015 / ES3
structuredClone(arr) - 深いコピー HTML標準

破壊的なのは push pop shift unshift splice sort reverse fill copyWithin の9つだけです。このうち sort・reverse・splice には、ES2023で非破壊版の toSorted・toReversed・toSpliced が追加されました。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript 配列 操作 一覧|全メソッド早見表とNode 22実行結果」

配列を作る:Array.from・Array.of・fill

連番や決まった長さの配列は Array.from({ length: n }, 関数) で作ります。 以下、コメントの // → はNode.js v22.22.2での実際の出力です。

console.log(Array.from({ length: 3 }, (_, i) => i * 10)); // → [ 0, 10, 20 ]
console.log(Array.from("abc"));                  // → [ 'a', 'b', 'c' ]
console.log(Array.from(new Set([1, 1, 2])));     // → [ 1, 2 ]
console.log(Array.of(7), Array(7).length);       // → [ 7 ] 7
console.log(new Array(3).fill(0));               // → [ 0, 0, 0 ]
console.log([...Array(3).keys()]);               // → [ 0, 1, 2 ]

Array(7) は「長さ7の空の配列」、Array.of(7) は「7が1つ入った配列」です。

fill にオブジェクトや配列を渡すと、全要素が同じものを指します。

const grid = Array(2).fill([]);
grid[0].push("x");
console.log(grid);  // → [ [ 'x' ], [ 'x' ] ]

const grid2 = Array.from({ length: 2 }, () => []);
grid2[0].push("x");
console.log(grid2); // → [ [ 'x' ], [] ]

要素ごとに別の配列・オブジェクトが必要なときは Array.from の関数で毎回作ります。

追加・削除:push・pop・shift・unshift・splice

const a = [1, 2, 3];
console.log(a.push(4), a);    // → 4 [ 1, 2, 3, 4 ]
console.log(a.unshift(0), a); // → 5 [ 0, 1, 2, 3, 4 ]
console.log(a.pop(), a);      // → 4 [ 0, 1, 2, 3 ]
console.log(a.shift(), a);    // → 0 [ 1, 2, 3 ]

const b = ["a", "b", "c", "d"];
console.log(b.splice(1, 2, "X"), b); // → [ 'b', 'c' ] [ 'a', 'X', 'd' ]

push / unshift の戻り値は「追加後の長さ」、pop / shift は「取り出した要素」、splice は「削除した要素の配列」です。新しい配列が欲しいときは [...arr, x] や [x, ...arr] を使います。

元の配列を変えない新メソッド(ES2023):toSorted・toReversed・toSpliced・with

ES2023で追加された4つは、破壊的メソッドの「コピーを返す版」です。 Reactのstateのように元を書き換えたくない場面で便利です。

const nums = [3, 1, 10, 2];
console.log(nums.toSorted((x, y) => x - y)); // → [ 1, 2, 3, 10 ]
console.log(nums.toReversed());              // → [ 2, 10, 1, 3 ]
console.log(nums.toSpliced(1, 1));           // → [ 3, 10, 2 ]
console.log(nums.with(0, 99));               // → [ 99, 1, 10, 2 ]
console.log(nums);                           // → [ 3, 1, 10, 2 ]
破壊的 非破壊(ES2023) 以前の書き方
arr.sort(f) arr.toSorted(f) [...arr].sort(f)
arr.reverse() arr.toReversed() [...arr].reverse()
arr[i] = x arr.with(i, x) arr.map((v, j) => (j === i ? x : v))

with は範囲外の位置を指定すると RangeError: Invalid index : 5 のようなエラーになります(arr[i] = x は配列を伸ばします)。

並べ替え:sortの数値比較と日本語の順番

sort() を引数なしで呼ぶと、要素を文字列にして辞書順で並べます。

console.log([3, 1, 10, 2].sort());                  // → [ 1, 10, 2, 3 ]
console.log([3, 1, 10, 2].sort((x, y) => x - y));   // → [ 1, 2, 3, 10 ]

const fruits = ["いちご", "りんご", "バナナ", "あんず"];
console.log(fruits.toSorted());                               // → [ 'あんず', 'いちご', 'りんご', 'バナナ' ]
console.log(fruits.toSorted((x, y) => x.localeCompare(y, "ja"))); // → [ 'あんず', 'いちご', 'バナナ', 'りんご' ]

数値は (a, b) => a - b(降順は b - a)、日本語は localeCompare(b, "ja") を渡します。 引数なしの sort は文字コード順なので、カタカナの「バナナ」がひらがなの後ろに来ます。複数キーは a.done - b.done || a.id - b.id のように || でつなぎます。

検索:find・findLast・at・indexOf

以降の例では、次のタスク一覧を使います。

const tasks = [
  { id: 1, title: "備品を発注", done: true, owner: "佐藤" },
  { id: 2, title: "会議室を予約", done: false, owner: "鈴木" },
  { id: 3, title: "在庫を確認", done: true, owner: "鈴木" },
  { id: 4, title: "記事を公開", done: false, owner: "佐藤" },
];
console.log(tasks.find((t) => t.done).id, tasks.findIndex((t) => t.done));         // → 1 0
console.log(tasks.findLast((t) => t.done).id, tasks.findLastIndex((t) => t.done)); // → 3 2
console.log(tasks.find((t) => t.id === 9), tasks.findIndex((t) => t.id === 9));    // → undefined -1
console.log([1, 2, 3].at(-1), [1, 2, 3][-1]); // → 3 undefined

findLast は後ろから探して最初に見つかった要素を返します(ES2023)。 「最新の完了タスク」のように末尾側が新しいデータで便利です。arr[-1] は末尾ではなく undefined なので、末尾は at(-1) で取り出します。見つからないときは find が undefined、findIndex / indexOf が -1 なので、if (idx !== -1) のように判定します。

判定:includesとindexOfのNaNの違い・some・every

const x = [1, NaN, 3];
console.log(x.includes(NaN), x.indexOf(NaN), x.findIndex(Number.isNaN)); // → true -1 1
console.log([0].includes(-0), [, 1].includes(undefined), [, 1].indexOf(undefined)); // → true true -1

console.log(tasks.some((t) => t.done), tasks.every((t) => t.done)); // → true false
console.log([].every(() => false));                          // → true
console.log(Array.isArray([]), Array.isArray({ length: 0 })); // → true false

includes は NaN を見つけられますが、indexOf は -1 を返します。 indexOf は === で比べ、NaN === NaN が false になるためです。空きのある配列([, 1])でも同様の差が出ます。「含むか」だけなら includes、位置が必要なら indexOf(NaNを探すなら findIndex(Number.isNaN))と使い分けます。また、空の配列の every は常に true なので、「全部完了」を判定するときは tasks.length > 0 && tasks.every(...) とします。

変換:map・filter・flat・flatMap

map・filter・reduce の詳しい使い方と落とし穴は「JavaScript map filter reduce 使い方」の記事にまとめているので、ここでは要点と flat 系を扱います。

console.log(tasks.filter((t) => !t.done).map((t) => t.id)); // → [ 2, 4 ]
console.log([[1, 2], [3, [4, [5]]]].flat());          // → [ 1, 2, 3, [ 4, [ 5 ] ] ]
console.log([[1, 2], [3, [4, [5]]]].flat(Infinity));  // → [ 1, 2, 3, 4, 5 ]
console.log(["a b", "c"].flatMap((s) => s.split(" "))); // → [ 'a', 'b', 'c' ]
console.log([1, 2, 3].flatMap((n) => (n % 2 ? [n, n] : []))); // → [ 1, 1, 3, 3 ]

flat() の既定の深さは1段で、すべて平らにするなら flat(Infinity) です。flatMap は「map してから1段 flat」なので、空配列を返せば要素を消し、2要素の配列を返せば増やせます。filterとmapを1回で書けるのが利点です。

結合・分割:concat・スプレッド・slice・join

console.log([1, 2].concat([3], 4)); // → [ 1, 2, 3, 4 ]
console.log([...[1, 2], ...[3]]);   // → [ 1, 2, 3 ]
console.log(["a", "b"].join("-"), "a,b".split(",")); // → a-b [ 'a', 'b' ]
console.log([1, 2, 3, 4, 5].slice(1, 3), [1, 2, 3, 4, 5].slice(-2)); // → [ 2, 3 ] [ 4, 5 ]

slice(開始, 終了) は終了位置を含みません。 負の数は末尾からの位置です。名前が似ている splice は元の配列を変えるので、切り出すだけなら slice を使います。

集計:reduce・Object.groupBy・Map.groupBy

console.log([1, 2, 3].reduce((s, n) => s + n, 0));       // → 6
console.log(Math.max(...[3, 9, 2]), Math.min(...[3, 9, 2])); // → 9 2

const byOwner = Object.groupBy(tasks, (t) => t.owner);
console.log(Object.fromEntries(
  Object.entries(byOwner).map(([k, v]) => [k, v.map((t) => t.id)])
)); // → { '佐藤': [ 1, 4 ], '鈴木': [ 2, 3 ] }
console.log(Object.getPrototypeOf(byOwner)); // → null

const byDone = Map.groupBy(tasks, (t) => t.done);
console.log(byDone.get(true).map((t) => t.id), byDone.get(false).map((t) => t.id)); // → [ 1, 3 ] [ 2, 4 ]

Object.groupBy(ES2024)は、関数の戻り値をキーにして要素をまとめます。 結果はプロトタイプを持たないオブジェクト(null プロトタイプ)なので、byOwner.hasOwnProperty(...) は使えず、Object.hasOwn(byOwner, "佐藤") や in を使います。キーが真偽値やオブジェクトのときは Map.groupBy を使うと、キーの型がそのまま保たれます(Object.groupBy だとキーは文字列の "true" になります)。

Math.max(...arr) は要素数が多いとエラーになります。 Node.js 22で100万件を渡すと RangeError: Maximum call stack size exceeded でした。大きな配列は reduce((m, n) => Math.max(m, n), -Infinity) を使います。なお、小数の誤差を抑えて合計する Math.sumPrecise がES2026で標準化されましたが、Node.js 22では typeof Math.sumPrecise が "undefined" でした。

重複を除く:SetとMap

console.log([...new Set([3, 1, 3, 2, 1])]); // → [ 3, 1, 2 ]
console.log([...new Map(tasks.map((t) => [t.owner, t])).values()].map((t) => t.id)); // → [ 4, 3 ]

値の重複は [...new Set(arr)] で、最初に出た順を保って除けます。 オブジェクトを特定のキーで重複除去するときは Map に入れます。この書き方は同じキーの後の要素で上書きされるため、担当者ごとの最後のタスク(id 4と3)が残ります。

コピー:スプレッドとstructuredCloneの浅い・深いの違い

const src = [{ n: 1 }, [2]];
const s1 = [...src];
const s2 = src.slice();
const s3 = structuredClone(src);
src[0].n = 99;
console.log(s1[0].n, s2[0].n, s3[0].n);   // → 99 99 1
console.log(s1 === src, s1[0] === src[0]); // → false true

try {
  structuredClone([() => 1]);
} catch (e) {
  console.log(`${e.name}: ${e.message}`); // → DataCloneError: () => 1 could not be cloned.
}

スプレッド [...arr]・slice()・Array.from() は「浅いコピー」で、配列そのものは別でも中のオブジェクトは共有されます。 そのため src[0].n を変えると s1・s2 にも反映されました。中身まで別にしたいなら structuredClone(深いコピー)を使います。ただし関数やDOM要素はコピーできず DataCloneError になります。

JSON.parse(JSON.stringify(arr)) も深いコピーになりますが、Dateが文字列になり undefined や関数は消えるため、structuredClone(Date・Map・Setを保てる)が扱いやすいです。

非同期の値を配列にする:Array.fromAsync(ES2026)

const sleep = (ms, v) => new Promise((r) => setTimeout(() => r(v), ms));
console.log(await Array.fromAsync([sleep(20, "a"), sleep(10, "b")])); // → [ 'a', 'b' ]

async function* gen() { yield 1; yield 2; }
console.log(await Array.fromAsync(gen(), (v) => v * 10)); // → [ 10, 20 ]

Array.fromAsync は、非同期ジェネレーターやPromiseの並びを待ちながら配列にします。 結果は元の順番どおりです。Promise.all と違い要素を1つずつ順番に待つので、同時に実行したい処理には Promise.all を使います。上の例は await をトップレベルで使うため、.mjs ファイルで実行しています。

よくあるつまずきと直し方

症状 原因 直し方
並べ替えたら元の配列も変わった sort / reverse は破壊的 toSorted / toReversed
const n = arr.push(x) が数値 push の戻り値は長さ [...arr, x]
コピーを変えたら元も変わった 浅いコピー structuredClone
空配列で every が true 仕様(条件に反する要素がない) length > 0 && を足す
forEach の中の await が待たない forEach はPromiseを待たない for...of か Promise.all(arr.map(...))
toSorted is not a function 古い実行環境(Node.js 18以前など) Node.js 20以降にする、または [...arr].sort()

よくある質問

Q. 破壊的メソッドと非破壊メソッドはどう見分けますか? A. 破壊的なのは push pop shift unshift splice sort reverse fill copyWithin の9つです。 それ以外(map・filter・slice・concat・to〜 系・with など)は元の配列を変えません。

Q. toSorted や Object.groupBy はどの環境で使えますか? A. Node.js 22と現行の主要ブラウザで使えます。 本記事の例はNode.js v22.22.2ですべて動作を確認しました。古いブラウザにも対応する必要がある場合は、TypeScriptの lib 設定やトランスパイル・ポリフィルの対象を確認します。

Q. 配列の中に値があるかを調べるには includes と some のどちらを使いますか? A. 値そのものを探すなら includes、条件で探すなら some です。 tasks.includes(obj) は同じオブジェクト(参照)しか見つけないので、オブジェクトの配列では tasks.some((t) => t.id === 3) と書きます。

まとめ

  • 破壊的メソッドは9つ。それ以外は新しい配列や値を返す
  • ES2023の toSorted・toReversed・toSpliced・with で、元を変えずに並べ替え・置き換えができる
  • sort() は文字列比較。数値は (a, b) => a - b、日本語は localeCompare(b, "ja")
  • 末尾は at(-1)、後ろから探すなら findLast、NaN を探すなら includes
  • グループ分けは Object.groupBy(キーが文字列)と Map.groupBy(キーの型を保つ)
  • スプレッドは浅いコピー、structuredClone は深いコピー
  • 非同期の値の配列化は Array.fromAsync、同時実行は Promise.all

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript 配列 操作 一覧|全メソッド早見表とNode 22実行結果」

株式会社RJCでは、言語の新しい機能を実際に動かして確かめ、その結果をプロジェクトの中で仲間と共有しています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?