JavaScript | 配列・オブジェクト:配列の変換・加工 – flatMap

JavaScript JavaScript
スポンサーリンク

flatMap とは何か

flatMap は「各要素を“変換”して、その結果を1階層だけ“平坦化(フラットに)”して新しい配列を返す」メソッドです。map と flat(1) を連続で行うのと同じ効果を、1回の処理で安全かつ読みやすく書けます。ここが重要です:flatMap は“返り値が配列のときだけ”展開します。配列以外の値はそのまま入るので、返す型を揃える設計が読みやすさの鍵です。


基本の使い方(map+flat(1) の一体化)

1階層の平坦化を伴う変換

const users = [
  { id: 1, tags: ["js", "web"] },
  { id: 2, tags: ["ai"] }
];

// map → flat(1) と同等
const allTags = users.flatMap(u => u.tags);
console.log(allTags); // ["js", "web", "ai"]
JavaScript

ここが重要です:コールバックが「配列」を返すと、その中身が1階層だけ展開されます。毎回 1 階層と分かっている処理では flatMap が最短で意図が明確です。

条件によって空配列を返して“スキップ”を表現

const products = [
  { name: "A", stock: 0 },
  { name: "B", stock: 3 },
  { name: "C", stock: 2 }
];

const namesInStock = products.flatMap(p => p.stock > 0 ? [p.name] : []);
console.log(namesInStock); // ["B", "C"]
JavaScript

ここが重要です:filter と map を分けず、“返す配列の中身の有無”で選別を表現できます。読みやすく 1 パスで処理できます。


map+flat の違いと使い分け

flatMap と map(…).flat() の等価性と違い

const nested = [1, [2, 3], 4];
const r1 = nested.map(x => Array.isArray(x) ? x : [x]).flat(); // [1, 2, 3, 4]
const r2 = nested.flatMap(x => Array.isArray(x) ? x : [x]);    // [1, 2, 3, 4]
JavaScript

ここが重要です:動作は同じですが、flatMap は“1回の走査”で済みパフォーマンスと可読性が良くなります。深さは常に 1。2階層以上の平坦化は flat(depth) を使います。

返り値が配列以外ならそのまま残る

const xs = [1, 2];
const out = xs.flatMap(x => x % 2 === 0 ? [x, x] : x);
console.log(out); // [1, 2, 2](配列は展開、非配列はそのまま)
JavaScript

ここが重要です:混在させると読みにくくなるため、基本は“常に配列を返す”設計に揃えると意図が伝わります(例:あるときは [値]、無いときは [])。


空スロット(疎配列)・undefined の挙動

疎配列の“穴”は flat 同様にスキップ

const sparse = [ , [1], , [2, , 3] ]; // 空スロットを含む
const out = sparse.flatMap(x => x);   // 1階層展開しつつ穴は消える
console.log(out); // [1, 2, 3]
JavaScript

ここが重要です:空スロットは処理されず消えます。穴を保持したい要件では、事前に Array.from で正規化してから扱う設計にしましょう。

値としての undefined は残る

const arr = [undefined, [undefined, 1]];
console.log(arr.flatMap(x => x)); // [undefined, undefined, 1]
JavaScript

ここが重要です:空スロットと undefined を混同しないこと。欠損をゼロ埋めしたいなら null 合体演算子(??)で明示的に埋めます。


実務での定番レシピ

ネスト配列の一括抽出(中身だけ欲しい)

const groups = [
  { name: "A", items: [1, 2] },
  { name: "B", items: [3] }
];
// items を集めて1本の配列へ
const allItems = groups.flatMap(g => g.items); // [1, 2, 3]
JavaScript

部分的な展開+変換(タグを“表示ラベル”へ)

const users = [
  { name: "Alice", tags: ["js", "web"] },
  { name: "Bob", tags: ["ai"] }
];
const labels = users.flatMap(u => u.tags.map(t => `${u.name}:${t}`));
// ["Alice:js","Alice:web","Bob:ai"]
JavaScript

ここが重要です:返す配列の中でさらに map して、展開と変換を一度に記述できます。

条件抽出を 1 パスで(filter+map を合成)

const products = [
  { name: "A", stock: 0, price: 100 },
  { name: "B", stock: 5, price: 200 },
  { name: "C", stock: 3, price: 150 }
];
const labels = products.flatMap(p => p.stock > 0 ? [`${p.name} (${p.price})`] : []);
// ["B (200)","C (150)"]
JavaScript

ここが重要です:“あるなら1件の配列、無ければ空配列”という定型で、絞り込みと変換を同時に書けます。

Object.values と組み合わせて辞書の値を統合

const catalog = {
  fruits: ["apple","banana"],
  vegs: ["carrot"]
};
const all = Object.values(catalog).flatMap(xs => xs); // ["apple","banana","carrot"]
JavaScript

パフォーマンスと設計の指針

1階層が確定なら flatMap が最短で効率的

map→flat(1) と比べて、flatMap は1パスで意図が明確。読み手に「返り値は配列で、1階層だけ展開する」と伝えられます。

深い階層は flat(depth) か専用ロジックへ

2階層以上の平坦化は flat(depth) を使うか、要件が複雑なら再帰・スタックで明示的に処理します。flatMap はあくまで“1階層”に特化した道具です。

返り値の型を揃える(常に配列を返す)

返り値が配列と非配列の混在は、意図を読みづらくします。原則「返り値は配列。不要なら空配列」を徹底すると、後続処理が安定します。


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

コールバックで非配列を返してしまう

非配列は展開されずそのまま入ります。常に配列を返す設計(例:条件 ? [値] : [])にすると誤動作を防げます。

2階層以上を展開したいのに flatMap を使う

flatMap は 1 階層のみ。多段なら map(...).flat(2) または flat(2) に切り替えます。

疎配列の穴が消えて困る

空スロットはスキップされます。穴を扱いたいなら正規化してから flatMap、または Array.from で埋めてから処理します。

複雑すぎるロジックを flatMap に詰め込みすぎる

分岐・重い処理・多段変換を詰め込むと可読性が落ちます。必要に応じてステップを分ける(前処理→flatMap→後処理)方が保守しやすいです。


まとめ

flatMap は「各要素を変換し、その結果を1階層だけ平坦化して“新しい配列”を返す」非破壊メソッドです。map+flat(1) を1回で表現でき、“配列を返す”という意図が明確。疎配列の穴はスキップされ、undefined は残るため、欠損の扱いは設計で意識する。返り値は配列に統一し、2階層以上は flat(depth) へ。この指針を押さえれば、初心者でも“絞り込み+変換+統合”を短く正確に書けます。

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