flatMap は「変換+1階層平坦化」を一度にやる魔法
arr.flatMap(x => x.items)
flatMap は、「map で変換してから flat で1階層平坦化する」処理を、1回でまとめてやってくれるメソッドです。 天才プログラマー視点で言うと、「配列の配列を扱うときに、map と flat を毎回つなげるのが面倒になったら flatMap に乗り換えろ」という感じの便利ツールです。
「各要素から配列を取り出して、それらを全部まとめて1本の配列にしたい」 まさにそのためのメソッドが flatMap です。
flatMap の基本構造と「変換+平坦化」が同時に起きる仕組み
まず、よくあるパターンから見てみます。
const arr = [
{ name: "Aki", items: ["apple", "banana"] },
{ name: "Mika", items: ["orange"] },
{ name: "Ken", items: ["grape", "melon"] }
];
const r = arr.flatMap(x => x.items);
console.log(r);
// ["apple", "banana", "orange", "grape", "melon"]
JavaScriptここで起きていることを丁寧に分解します。
flatMapは、まずmapと同じように、各要素xに対してコールバックx => x.itemsを実行します。 その結果は、[["apple", "banana"], ["orange"], ["grape", "melon"]]のような「配列の配列」になります。- 次に、その「配列の配列」を
flat()と同じように 1階層だけ平坦化します。 結果として、["apple", "banana", "orange", "grape", "melon"]という1本の配列になります。
つまり、「map で配列を取り出す → flat で1階層崩す」という2ステップを、 flatMap は 1ステップでやってくれるわけです。
map+flat と flatMap の違いを具体的に比較する
同じことを、map と flat を使って書くとこうなります。
const arr = [
{ name: "Aki", items: ["apple", "banana"] },
{ name: "Mika", items: ["orange"] },
{ name: "Ken", items: ["grape", "melon"] }
];
const nested = arr.map(x => x.items);
// [["apple", "banana"], ["orange"], ["grape", "melon"]]
const r = nested.flat();
// ["apple", "banana", "orange", "grape", "melon"]
JavaScriptこれを flatMap で書くと、こうなります。
const r = arr.flatMap(x => x.items);
JavaScript天才講師として一言でまとめるなら、 「map してから flat(1) するなら、flatMap にまとめた方がコードの意図がはっきりする」 ということです。
例題:カテゴリごとの商品リストを「全商品リスト」に変換する
const categories = [
{ category: "fruit", items: ["apple", "banana"] },
{ category: "drink", items: ["coffee", "tea"] },
{ category: "snack", items: ["cookie"] }
];
const allItems = categories.flatMap(c => c.items);
console.log(allItems);
// ["apple", "banana", "coffee", "tea", "cookie"]
JavaScriptここでのイメージはこうです。
カテゴリごとに「商品配列」が入っている構造は、人間には分かりやすいけれど、 「全部の商品を1本の配列で扱いたい」ときには少し扱いづらい。
そこで flatMap を使うと、 「カテゴリごとの items を取り出して、それらを全部まとめて1本の配列にする」 という処理を、非常に自然な形で書けます。
flatMap のコールバックで「変換」も同時に行う
flatMap のコールバックは、単に配列を返すだけでなく、 その中で変換処理も一緒に書けます。
const categories = [
{ category: "fruit", items: ["apple", "banana"] },
{ category: "drink", items: ["coffee", "tea"] }
];
const labeledItems = categories.flatMap(c => {
return c.items.map(item => `${c.category}:${item}`);
});
console.log(labeledItems);
// ["fruit:apple", "fruit:banana", "drink:coffee", "drink:tea"]
JavaScriptここで起きていることはこうです。
- 各カテゴリ
cに対して、c.items.map(...)で「カテゴリ名付きの文字列配列」を作る。 - その結果は「配列の配列」になる。
flatMapがそれを1階層平坦化して、1本の配列にしてくれる。
「中でさらに map を使って変換し、その結果を flatMap に渡す」というパターンは、 プロの現場でもよく使われるテクニックです。
flatMap の「深さ」について少しだけ触れる
重要なポイントとして、flatMap が平坦化するのは 常に1階層だけです。 flat() のように「深さを指定する引数」はありません。
つまり、flatMap は本質的に 「map+flat(1)」のショートカットです。 ネストがもっと深い場合は、flatMap の結果に対してさらに flat(2) などを使う必要があります。
初心者のうちは、 「flatMap は“1階層だけ”平坦化する」と覚えておけば十分です。
よくあるミスと注意点を深掘りする
まず、flatMap のコールバックは 「配列を返すことが前提」です。 配列ではないものを返すと、意図した結果になりません。
const arr = [1, 2, 3];
const r = arr.flatMap(x => x * 2);
console.log(r); // [2, 4, 6](これは動くが、flat の意味はない)
JavaScriptこの例では、flatMap は単なる map と同じ動きになっています。 本来の力を発揮するのは、「配列を返すとき」です。
const arr = [1, 2, 3];
const r = arr.flatMap(x => [x, x * 2]);
console.log(r); // [1, 2, 2, 4, 3, 6]
JavaScriptここでは、
1 を [1, 2] に、 2 を [2, 4] に、 3 を [3, 6] に変換し、 それらを平坦化して 1 本の配列にしています。
「1つの要素から複数の要素を生み出し、それを全部並べたい」という場面で、flatMap は非常に強力です。
まとめ:flatMap は「map+flat(1)」を美しく書くためのプロ向けツール
flatMap を使いこなせると、配列のネストを扱うコードが一気に洗練されます。
配列の中に配列が入っている。 各要素から配列を取り出したい。 その配列たちを、1本の配列にまとめたい。
そんなときに、 arr.map(...).flat() ではなく、arr.flatMap(...) と書けるようになることが、 “配列処理を理解しているプログラマー”への階段の一段目です。
