ネスト配列とは何か
ネスト配列は「配列の中にさらに配列が入っている構造」です。2次元(行・列の表)、階層(ツリーの子配列)、グループ化(カテゴリごとの要素)など、現実のデータを自然に表現できます。ここが重要です:扱いの基本は“アクセス(インデックスで辿る)”“更新(非破壊で差し替える)”“反復(map・for…of)”“整形(flat/flatMap/reduce)”を押さえることです。
ネスト配列へのアクセス(インデックスで辿る)
2次元配列(行・列)を辿る
const grid = [
[1, 2, 3], // row 0
[4, 5, 6], // row 1
[7, 8, 9] // row 2
];
const value = grid[1][2]; // 6(row=1 の col=2)
JavaScript固定の位置を読むだけなら、このインデックスの連鎖で十分です。動的に辿る場合は安全にガードを入れます。
“無いかも”に備える(欠損ガード)
function getAt(grid, r, c) {
if (!Array.isArray(grid[r])) return undefined;
return grid[r][c];
}
JavaScriptここが重要です:インデックスが範囲外だと undefined。必ずガードを入れてからアクセスし、落ちないコードにします。
ネスト配列の更新(非破壊で差し替える)
2次元配列の要素を安全に更新
const grid = [
[1, 2, 3],
[4, 5, 6]
];
// row=1, col=0 を 400 に変更(非破壊)
const next = grid.map((row, ri) =>
ri === 1 ? row.map((v, ci) => (ci === 0 ? 400 : v)) : row
);
JavaScriptここが重要です:直接代入は共有状態で危険です。map で“該当行を作り直し、該当列だけ置き換える”のが定番の非破壊更新です。
子配列の置き換え(丸ごと差し替え)
const lists = [
["a", "b"],
["c"]
];
const nextLists = lists.map((group, i) =>
i === 0 ? [...group, "z"] : group
);
JavaScript部分的な追加・削除はスプレッドや filter を組み合わせ、元を壊さず新インスタンスを返します。
反復と整形(map・flat・flatMap・reduce)
2次元を1次元へ(flat)
const nested = [[1, 2], [3], [4, 5]];
const flat = nested.flat(); // [1, 2, 3, 4, 5]
JavaScript深さがさらに多い場合は flat(depth) を指定します。ここが重要です:flat は“穴(空スロット)を無視し、存在する要素を平坦化”します。
変換+平坦化(flatMap)
const rows = ["ab", "cd"];
const chars = rows.flatMap(s => s.split("")); // ["a","b","c","d"]
JavaScriptmap 後の配列を自動で 1 段平坦化します。生成・展開が同時に欲しいときに最短です。
集計・検索(reduce / find / some / every)
const groups = [[1,2], [3,4,5]];
const sum = groups.flat().reduce((a, n) => a + n, 0); // 15
const found = groups.flat().find(n => n > 3); // 4
const anyLarge = groups.some(g => g.some(n => n >= 5)); // true
const allPositive = groups.every(g => g.every(n => n > 0)); // true
JavaScriptここが重要です:some/every は“ネストの中で条件が満たされるか”の短絡評価に向きます。flat は“形を単純化してから処理”するため、可読性が高まります。
ツリー構造(子配列)を扱う基本
子を持つレコードの反復(再帰)
const tree = {
name: "root",
children: [
{ name: "A", children: [] },
{ name: "B", children: [{ name: "B1", children: [] }] }
]
};
function walk(node, visit) {
visit(node);
for (const child of node.children ?? []) {
walk(child, visit);
}
}
walk(tree, n => console.log(n.name)); // root, A, B, B1
JavaScriptここが重要です:再帰は“子が無い場合(undefined)”も考慮して安全に辿ります。更新は“該当ノードだけ新しく作り直す”非破壊パターンが基本です。
ノード更新(探して置き換える)
function updateByName(node, target, patch) {
if (node.name === target) return { ...node, ...patch };
const children = (node.children ?? []).map(ch => updateByName(ch, target, patch));
return { ...node, children };
}
const nextTree = updateByName(tree, "B", { flag: true });
JavaScript該当ノードを見つけたらスプレッドで置き換え、その他はそのまま返します。変更箇所だけが新インスタンスになり、差分検知が安定します。
2次元データの定番操作(行・列・検索・挿入)
行の挿入・削除
const grid = [[1,2], [3,4]];
const insertRow = (g, idx, row) => [...g.slice(0, idx), row, ...g.slice(idx)];
const removeRow = (g, idx) => [...g.slice(0, idx), ...g.slice(idx + 1)];
const g1 = insertRow(grid, 1, [9,9]); // [[1,2],[9,9],[3,4]]
const g2 = removeRow(grid, 0); // [[3,4]]
JavaScript列の挿入・削除
const insertCol = (g, idx, value = null) =>
g.map(row => [...row.slice(0, idx), value, ...row.slice(idx)]);
const removeCol = (g, idx) =>
g.map(row => [...row.slice(0, idx), ...row.slice(idx + 1)]);
JavaScriptここが重要です:列操作は“各行に対して同じ位置に挿入・削除”する、という思考で map します。
行・列の検索
const findRowIndex = (g, pred) => g.findIndex(pred);
const findColIndex = (g, colPred) => (g[0] ?? []).findIndex(colPred);
const r = findRowIndex(grid, row => row.includes(4)); // 行番号
const c = findColIndex(grid, v => v === 2); // 列番号
JavaScript浅いコピーと深いコピー(ネスト配列の独立度)
浅いコピーでは“内側は共有”
const nested = [[1], [2]];
const shallow = [...nested]; // 行配列の参照をコピー
shallow[0][0] = 9;
nested[0][0]; // 9(同じ参照)
JavaScript行ごとに独立させるなら、もう一段 map を入れて“各行もコピー”します。
const safe = nested.map(row => [...row]); // 行配列までコピー
JavaScript深いコピーが必要なら structuredClone
const deep = structuredClone(nested); // 全階層を独立
JavaScriptここが重要です:更新範囲が狭いときは“必要な階層だけコピー”、全体の独立が必要なら深いコピーを選び、性能と安全性のバランスを取ります。
欠損・境界・不変性の安全設計(重要ポイントの深掘り)
インデックス境界を常に確認する
function at2D(g, r, c) {
return Array.isArray(g[r]) ? g[r][c] : undefined;
}
JavaScript境界外アクセスは undefined。常にガードを通すことで、例外なく安全に扱えます。
非破壊(イミュータブル)更新を徹底する
共有状態(UI・ストア)では直接代入を避け、map・slice・スプレッドで新インスタンスを返します。差分検知の一貫性が高まり、バグを避けられます。
反復は for…of/map を使う(for…in は使わない)
配列に for…in を使うと、順序保証が弱く、追加プロパティまで回る可能性があります。意図が明確な for…of/map/filter/reduce を選びます。
実践レシピ(よくあるネスト配列の処理)
グループ化されたデータの平坦化と集計
const groups = [
[{ id: 1, score: 10 }, { id: 2, score: 20 }],
[{ id: 3, score: 5 }]
];
const total = groups.flat().reduce((a, r) => a + r.score, 0); // 35
JavaScript多段ネストの取り出し(安全な経路)
function pickPath(arr, path) {
let cur = arr;
for (const idx of path) {
if (!Array.isArray(cur) || cur[idx] === undefined) return undefined;
cur = cur[idx];
}
return cur;
}
pickPath([[1],[2,[3]]], [1, 1, 0]); // 3
JavaScript条件に合う要素をネストごと置換
function updateNested(groups, pred, patch) {
return groups.map(lst =>
lst.map(item => (pred(item) ? { ...item, ...patch } : item))
);
}
const next = updateNested(groups, it => it.id === 2, { score: 99 });
JavaScriptまとめ
ネスト配列は“2次元・階層・グループ化”を自然に表現できる強力な構造です。アクセスはインデックスを安全に辿り、更新は非破壊(map・slice・スプレッド)で“該当部分だけ作り直す”。整形は flat/flatMap/reduce を使い、検索・検証は some/every/find を組み合わせる。浅いコピーは内側が共有なので“必要な階層までコピー”し、全体独立には structuredClone。境界チェックと反復方法の選択を徹底すれば、初心者でも複雑なネスト配列を短く明快に、しかも安全に扱えます。
