1. ホーム
  2. JavaScript

【JavaScript】flat・flatMap の使い方|多次元配列を平坦化する方法

Share

配列の中にさらに配列が入った「入れ子の配列」を、1つのフラットな配列にまとめたい場面はよくあります。JavaScript にはこれを簡単に行う flat()flatMap() というメソッドが用意されています。この記事では、それぞれの基本的な使い方と、map() との違い、どんなときに使うと便利かを、具体的なコードで解説します。

flat() で入れ子の配列を平坦化する

flat() は、配列の中の配列を取り出して1つの配列にまとめる(平坦化する)メソッドです。元の配列は変更せず、平坦化した新しい配列を返します

flat.js
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段階だけ平坦化され、配列でない要素(上の 12)はそのまま残ります。

深い入れ子は引数で深さを指定する

flat() は引数で「どこまで深く平坦化するか」を指定できます。省略すると 1 です。配列が何重にも入れ子になっている場合は、深さを数値で渡します。

flat-depth.js
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() には、配列内の空のスロット(要素が抜けている穴)を取り除く性質もあります。配列から欠けた要素を取り除きたいだけのときにも使えます。

flat-holes.js
const sparse = [1, , 3, , 5]; // 2番目と4番目が空

console.log(sparse.flat()); // [1, 3, 5]

flatMap() は map() と flat() を合わせたもの

flatMap() は、各要素を map() で変換したあと、結果を1段階だけ平坦化するメソッドです。「map() してから flat() する」という流れを1つのメソッドにまとめたものと考えると分かりやすいです。

flatmap.js
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() の便利な点は、コールバックが返す配列の要素数を変えることで、結果の要素を増やしたり減らしたりできることです。空の配列 [] を返した要素は結果から消えるため、「変換と絞り込みを同時に行う」用途に向いています。

flatmap-filter.js
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次元の単語リストにまとめられます。

words.js
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() を思い出すとコードがすっきりします。ぜひ実際のデータ処理で使ってみてください。

参考ページ