JavaScriptでデータを効率的に管理するためには、2次元配列の操作をマスターすることが不可欠です。

2次元配列とは、配列の要素としてさらに配列が格納されている「配列の配列」という構造を指します。

表形式のデータや行列計算、グリッドベースのゲーム開発など、実務における活用シーンは多岐にわたります。

しかし、1次元配列と比較すると、要素の追加には少し工夫が必要になる場面が多いのも事実です。

本記事では、JavaScriptで2次元配列に行や列を追加するさまざまな手法について、基本から応用まで詳しく解説します。

最新のJavaScript仕様に基づき、破壊的なメソッドから非破壊的なスプレッド構文まで幅広く網羅しました。

JavaScriptにおける2次元配列の基本構造

まずは、JavaScriptにおける2次元配列がどのような形で定義されるのかを確認しておきましょう。

JavaScriptには「2次元配列」という専用のデータ型は存在せず、通常の配列を入れ子にすることで実現します。

以下のコードは、3行2列の基本的な2次元配列を定義した例です。

JavaScript
// 3行2列の2次元配列を定義
const matrix = [
  [10, 20], // 0行目
  [30, 40], // 1行目
  [50, 60]  // 2行目
];

console.log(matrix[0][1]); // 0行目の1番目の要素にアクセス
実行結果
20

このように、matrix[行番号][列番号]の形式で個別の要素にアクセスすることが可能です。

この構造を理解した上で、既存の配列に対して新しい要素を追加する方法を見ていきましょう。

2次元配列に「行」を追加する方法

2次元配列における「行の追加」とは、外側の配列に新しい配列要素を挿入することを意味します。

これには、配列操作でお馴染みのメソッドや、モダンな構文を利用することができます。

末尾に行を追加する:pushメソッド

配列の最後に新しい行を追加するには、pushメソッドを使用するのが最も一般的です。

pushメソッドは元の配列を直接書き換える破壊的な操作となります。

JavaScript
const table = [
  ["Tanaka", 25],
  ["Sato", 30]
];

// 末尾に新しい配列(行)を追加
table.push(["Suzuki", 22]);

console.log(table);
実行結果
[
  ["Tanaka", 25],
  ["Sato", 30],
  ["Suzuki", 22]
]

このように、1次元配列に要素を追加する感覚で、配列そのものを引数に渡すだけで行が追加されます。

先頭に行を追加する:unshiftメソッド

配列の最初の要素として新しい行を挿入したい場合は、unshiftメソッドを利用します。

既存のインデックスがすべて1つずつ後ろにずれる点に注意してください。

JavaScript
const list = [
  ["Apple", 100],
  ["Banana", 150]
];

// 先頭に行を追加
list.unshift(["Orange", 120]);

console.log(list);
実行結果
[
  ["Orange", 120],
  ["Apple", 100],
  ["Banana", 150]
]

任意の場所に行を追加する:spliceメソッド

特定のインデックスの位置に新しい行を割り込ませたい場合は、spliceメソッドが便利です。

spliceは第1引数に開始位置、第2引数に削除する個数を指定します。

JavaScript
const data = [
  ["A", 1],
  ["C", 3]
];

// インデックス1の位置に、何も削除せずに新しい行を追加
data.splice(1, 0, ["B", 2]);

console.log(data);
実行結果
[
  ["A", 1],
  ["B", 2],
  ["C", 3]
]

スプレッド構文による非破壊的な行の追加

元の配列を変更せずに、新しい行が追加された新しい配列を作成したい場合は、スプレッド構文(…)を活用します。

Reactなどのモダンなフレームワークでは、状態のイミュータビリティ(不変性)を保つためにこの手法が推奨されます。

JavaScript
const original = [[1, 1], [2, 2]];

// 末尾に追加した新しい配列を作成
const newArray = [...original, [3, 3]];

console.log("Original:", original);
console.log("New:", newArray);
実行結果
Original: [[1, 1], [2, 2]]
New: [[1, 1], [2, 2], [3, 3]]

2次元配列に「列」を追加する方法

2次元配列における「列の追加」とは、内部にあるすべての配列要素(各行)に対して、新しい値を追加する操作です。

行の追加とは異なり、ループ処理やマッピング処理が必要になります。

forEachメソッドによる破壊的な列の追加

各行に対して繰り返し処理を行い、個別に値をpushしていく方法です。

既存の配列を直接更新したい場合に適しています。

JavaScript
const scores = [
  [80, 90],
  [70, 60],
  [85, 95]
];

// 各行に「合計点」などの新しい列データを追加
scores.forEach(row => {
  const sum = row[0] + row[1];
  row.push(sum);
});

console.log(scores);
実行結果
[
  [80, 90, 170],
  [70, 60, 130],
  [85, 95, 180]
]

mapメソッドによる非破壊的な列の追加

mapメソッドを使用すると、列が追加された新しい2次元配列をスマートに生成できます。

この方法は、コードが宣言的になり、バグを防ぎやすいというメリットがあります。

JavaScript
const users = [
  ["Alice"],
  ["Bob"],
  ["Charlie"]
];

// 各行にID列を追加
const usersWithId = users.map((row, index) => {
  return [...row, index + 1];
});

console.log(usersWithId);
実行結果
[
  ["Alice", 1],
  ["Bob", 2],
  ["Charlie", 3]
]

特定のインデックスに列を挿入する

すべての行の特定のインデックス位置に新しい列を挿入する場合は、mapsplice(またはスプレッド構文)を組み合わせます。

JavaScript
const matrix = [
  ["Start", "End"],
  ["Start", "End"]
];

// 1番目のインデックスに "Middle" を挿入
const updatedMatrix = matrix.map(row => {
  const newRow = [...row];
  newRow.splice(1, 0, "Middle");
  return newRow;
});

console.table(updatedMatrix);
実行結果
┌─────────┬─────────┬──────────┬───────┐
│ (index) │    0    │    1     │   2   │
├─────────┼─────────┼──────────┼───────┤
│    0    │ 'Start' │ 'Middle' │ 'End' │
│    1    │ 'Start' │ 'Middle' │ 'End' │
└─────────┴─────────┴──────────┴───────┘

実務で使える応用テクニック

基本的な追加操作を理解したところで、より実践的なシナリオに応用してみましょう。

空の2次元配列を初期化して要素を追加する

あらかじめ決まったサイズの2次元配列を生成し、後から値を代入していくパターンです。

Array.from()fill()を適切に使用しないと、すべての行が同じ参照を持ってしまう罠に注意が必要です。

JavaScript
// 誤った初期化(すべての行が同じ配列を参照してしまう)
const badMatrix = new Array(3).fill([]);
badMatrix[0].push(1); // すべての行に1が入ってしまう

// 正しい初期化
const goodMatrix = Array.from({ length: 3 }, () => []);
goodMatrix[0].push(1);

console.log("Good:", goodMatrix);
実行結果
Good: [[1], [], []]

条件に基づいて行や列を追加する

データのフィルタリングと組み合わせることで、特定の条件を満たす場合にのみ要素を追加することが可能です。

JavaScript
const inventory = [
  ["Item A", 5],
  ["Item B", 12],
  ["Item C", 8]
];

// 在庫が10個以上のアイテムに「充足」フラグを追加
const checkedInventory = inventory.map(item => {
  const status = item[1] >= 10 ? "充足" : "不足";
  return [...item, status];
});

console.log(checkedInventory);

モダンJavaScriptにおける最新の配列操作メソッド

ECMAScript 2023(ES14)以降、配列操作に便利な非破壊メソッドが追加されています。

これらは2次元配列の操作においても非常に有用です。

toSplicedメソッドの活用

spliceの非破壊版であるtoSplicedを使用すると、元の配列を変更せずに行を追加できます。

これにより、スプレッド構文を駆使しなくても簡潔にイミュータブルな更新が可能です。

JavaScript
const base = [["Row 1"], ["Row 3"]];

// インデックス1に新しい行を挿入(元のbaseは変更されない)
const extended = base.toSpliced(1, 0, ["Row 2"]);

console.log(extended);
実行結果
[
  ["Row 1"],
  ["Row 2"],
  ["Row 3"]
]

withメソッドによる特定要素の更新と追加

withメソッドを使用すると、特定のインデックスの要素を置き換えた新しい配列を取得できます。

2次元配列において、特定の「行」を丸ごと差し替えたい場合に非常に強力です。

JavaScript
const rows = [[1], [2], [3]];

// 1行目を新しいデータで置き換え
const newRows = rows.with(1, [2, "Updated"]);

console.log(newRows);
実行結果
[
  [1],
  [2, "Updated"],
  [3]
]

パフォーマンスに関する注意点

大規模な2次元配列を扱う場合、操作方法によってパフォーマンスに差が出ることがあります。

特に、ループの中でspread構文を多用すると、ループのたびに新しい配列のコピーが作成されるため、メモリ消費量が増加します。

数万件規模のデータを扱う場合は、破壊的メソッドであるpushを使用してメモリ効率を高めるか、TypedArrayの検討が必要かもしれません。

また、行の追加に比べて「列の追加」は全要素へのアクセスが必要になるため、時間計算量は O(N)(Nは行数)となります。

頻繁に列の追加が発生する設計であれば、データの持ち方を「オブジェクトの配列」にするなどの工夫も検討すべきです。

操作内容破壊的メソッド非破壊的メソッド(推奨)
末尾に行を追加push()[…array, newRow]
先頭に行を追加unshift()[newRow, …array]
任意の場所に行を追加splice()toSpliced()
列を追加forEach + pushmap + spread

まとめ

JavaScriptで2次元配列に要素を追加する方法は、用途に応じて多岐にわたります。

単純にデータを蓄積するだけであれば、pushunshiftといった標準的なメソッドが最も手軽です。

一方で、モダンなフロントエンド開発においてデータの不変性を保ちたい場合は、スプレッド構文やmap、あるいは最新のtoSplicedメソッドを活用するのがベストプラクティスです。

「行の追加」は外側の配列に対する操作、「列の追加」は内側の配列すべてに対する繰り返し操作であるという違いを意識することが大切です。

それぞれのメソッドの特性(破壊的か非破壊的か)を正しく理解し、プロジェクトの要件に最適なコードを選択してください。

この記事で紹介したテクニックを駆使することで、複雑なデータ構造も自信を持って操作できるようになるはずです。