配列の中にさらに配列が入った「入れ子の配列」を、1つのフラットな配列にまとめたい場面はよくあります。JavaScript にはこれを簡単に行う flat() と flatMap() というメソッドが用意されています。この記事では、それぞれの基本的な使い方と、map() との違い、どんなときに使うと便利かを、具体的なコードで解説します。
目次
flat() で入れ子の配列を平坦化する
flat() は、配列の中の配列を取り出して1つの配列にまとめる(平坦化する)メソッドです。元の配列は変更せず、平坦化した新しい配列を返します。
const nested = [1, 2, [3, 4], [5, 6]];
const flattened = nested.flat();
console.log(flattened); // [1, 2, 3, 4, 5, 6]
console.log(nested); // [1, 2, [3, 4], [5, 6]](元の配列はそのまま)
1段階だけ平坦化され、配列でない要素(上の 1 や 2)はそのまま残ります。
深い入れ子は引数で深さを指定する
flat() は引数で「どこまで深く平坦化するか」を指定できます。省略すると 1 です。配列が何重にも入れ子になっている場合は、深さを数値で渡します。
const deep = [1, [2, [3, [4]]]];
console.log(deep.flat()); // [1, 2, [3, [4]]](深さ1)
console.log(deep.flat(2)); // [1, 2, 3, [4]](深さ2)
// 深さが分からないときは Infinity で全部平坦化
console.log(deep.flat(Infinity)); // [1, 2, 3, 4]
入れ子の深さが分からないときは flat(Infinity) を使うと、何段でもすべて平坦化できます。
空の要素は取り除かれる
flat() には、配列内の空のスロット(要素が抜けている穴)を取り除く性質もあります。配列から欠けた要素を取り除きたいだけのときにも使えます。
const sparse = [1, , 3, , 5]; // 2番目と4番目が空
console.log(sparse.flat()); // [1, 3, 5]
flatMap() は map() と flat() を合わせたもの
flatMap() は、各要素を map() で変換したあと、結果を1段階だけ平坦化するメソッドです。「map() してから flat() する」という流れを1つのメソッドにまとめたものと考えると分かりやすいです。
const numbers = [1, 2, 3];
// 各要素を「その数とその2倍」のペアにする
const result = numbers.flatMap((n) => [n, n * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6]
// map() だと配列の配列になってしまう
const mapped = numbers.map((n) => [n, n * 2]);
console.log(mapped); // [[1, 2], [2, 4], [3, 6]]
map() でコールバックが配列を返すと「配列の配列」になりますが、flatMap() なら自動で1段ほどいてくれるため、扱いやすい1次元の配列になります。なお平坦化は1段階だけで、深さは指定できません。
要素を増やす・減らすのに使える
flatMap() の便利な点は、コールバックが返す配列の要素数を変えることで、結果の要素を増やしたり減らしたりできることです。空の配列 [] を返した要素は結果から消えるため、「変換と絞り込みを同時に行う」用途に向いています。
const items = [
{ name: "りんご", stock: 3 },
{ name: "みかん", stock: 0 },
{ name: "ぶどう", stock: 5 },
];
// 在庫があるものだけ名前を取り出す(在庫0は [] で除外)
const inStock = items.flatMap((item) =>
item.stock > 0 ? [item.name] : []
);
console.log(inStock); // ["りんご", "ぶどう"]
このように、条件に合うものだけ要素を返し、合わないものは空配列を返すことで、filter() と map() を1回のループでまとめて行えます。
よく使う場面:文章を単語に分解する
実践的な例として、複数の文を単語の配列に分解するケースを見てみます。各文を split() で配列に分けると配列の配列になりますが、flatMap() を使えば1次元の単語リストにまとめられます。
const sentences = ["hello world", "foo bar baz"];
const words = sentences.flatMap((s) => s.split(" "));
console.log(words); // ["hello", "world", "foo", "bar", "baz"]
使うときに気をつけたいこと
flatMap は1段階しか平坦化しない
flatMap() の平坦化は必ず1段階だけで、flat() のように深さを指定できません。コールバックが入れ子の配列を返す場合、内側の配列はそのまま残ります。深く平坦化したいときは、map() したあとに flat(Infinity) を使うなど、分けて書く必要があります。
対応していない古い環境に注意
flat() と flatMap() は ES2019 で追加されたメソッドです。最近のブラウザや Node.js ではそのまま使えますが、Internet Explorer では使えません。古い環境をサポートする必要がある場合は、対応状況を確認してから使ってください。
まとめ
flat() と flatMap() は、入れ子の配列を扱うときに便利なメソッドです。
flat()は入れ子の配列を平坦化する。引数で深さを指定でき、flat(Infinity)で全段まとめられるflat()は配列内の空の要素も取り除くflatMap()はmap()+ 1段階のflat()。配列を返す変換の結果を1次元にできる- 空配列
[]を返せば、変換と絞り込みを同時に行える - どちらも元の配列は変更せず、新しい配列を返す
「配列の配列ができてしまった」「変換しながら要素数も変えたい」というときは、flat() と flatMap() を思い出すとコードがすっきりします。ぜひ実際のデータ処理で使ってみてください。