入れ子になった配列を1段階だけ、または全部平らにする方法をまとめます。flat() の深さ指定、 flatMap() で変換と平坦化を同時にやる書き方、穴が消える挙動まで実行して確かめます。
答え
flat() を使います。引数を省くと1段だけ平らになります。
example.js
const nested = [1, [2, 3], [4, [5]]];
console.log(nested.flat());
出力書き換えて実行できます
[ 1, 2, 3, 4, [ 5 ] ]
example.js
const deep = [1, [2, [3, [4]]]];
console.log(deep.flat(Infinity));
出力書き換えて実行できます
[ 1, 2, 3, 4 ]
深さが分からないなら Infinity を渡します。
example.js
const deep = [1, [2, [3, [4]]]];
console.log(deep.flat());
console.log(deep.flat(2));
console.log(deep.flat(Infinity));
出力書き換えて実行できます
[ 1, 2, [ 3, [ 4 ] ] ]
[ 1, 2, 3, [ 4 ] ]
[ 1, 2, 3, 4 ]
example.js
const lines = ['a b', 'c d'];
console.log(lines.map((line) => line.split(' ')));
console.log(lines.flatMap((line) => line.split(' ')));
出力書き換えて実行できます
[ [ 'a', 'b' ], [ 'c', 'd' ] ]
[ 'a', 'b', 'c', 'd' ]
何段まで平らにするか
引数は何段まで潰すかです。
example.js
const nums = [1, 2, 3];
console.log(nums.flatMap((n) => [n, n * 2]));
console.log(nums.flatMap((n) => (n % 2 ? [n] : [])));
出力書き換えて実行できます
[ 1, 2, 2, 4, 3, 6 ]
[ 1, 3 ]
example.js
const holes = [1, , 2, [3, , 4]];
console.log(holes);
console.log(holes.flat());
出力書き換えて実行できます
[ 1, <1 empty item>, 2, [ 3, <1 empty item>, 4 ] ]
[ 1, 2, 3, 4 ]
深さが分かっているなら数を書いてください。
Infinity は分かりやすい反面、「本当は2段のはず」という前提が消えます。
意図しない深さのデータが混ざっても、黙って平らになってしまいます。
変換しながら平らにする
map() の結果が配列になるときは、flatMap() が使えます。
example.js
console.log([1, [2]].flat(0));
出力書き換えて実行できます
[ 1, [ 2 ] ]
example.js
console.log([[1], [2]].length);
console.log([[1], [2]].flat().length);
出力書き換えて実行できます
2
2
flatMap() は map() してから flat() するのと同じです。
ただし平らにするのは1段だけで、深さは指定できません。
増やすことも減らすこともできる
返す配列の長さを変えれば、要素を増減できます。
javascript
const nested = [1, [2, 3]];
nested.flat();
console.log(nested);
[ 1, [ 2, 3 ] ]
javascript
const rows = [{ tags: ['a', 'b'] }, { tags: ['c'] }];
console.log(rows.flat());
[ { tags: [ 'a', 'b' ] }, { tags: [ 'c' ] } ]
空の配列を返すと、その要素は消えます。
filter() と map() を一度に書けるので、条件付きの変換で便利です。
読みやすさは場合によります。「絞る」と「変える」を分けたほうが分かりやすいなら、filter() と map() を繋げてください。
→ 配列を変換する・絞り込む
知っておくこと:穴が消える
flat() は、配列の穴を黙って取り除きます。
example.js
const rows = [{ tags: ['a', 'b'] }, { tags: ['c'] }];
console.log(rows.flatMap((row) => row.tags));
出力書き換えて実行できます
[ 'a', 'b', 'c' ]
平らにするだけのつもりが、長さが変わります。
穴のある配列を扱っているなら、要素数を当てにしないでください。
穴は undefined とは違います。[1, , 2] は「2番目が無い」という状態で、読み出すと undefined になるだけです。
→ 配列の基本
深さに 0 を渡すと何もしない
変数から深さを渡すときに効いてきます。
flat(depth) の depth が0になる経路があると、
平らにならないまま先へ進みます。エラーは出ません。
深さが決まらないなら Infinity、決まるなら数を書く。
変数を渡すなら、0になり得ないか確かめてください。
長さがどう変わるか
外側の要素数と、平らにしたあとの要素数は一致するとは限りません。
中身が1つずつなら偶然そろいますが、数を当てにしないでください。
よくある間違い
元の配列が変わると思っている
flat() は新しい配列を返すだけです。元は変わりません。
戻り値を受け取ってください。
オブジェクトの中の配列は平らにならない
flat() が見るのは配列の要素が配列かどうかだけです。
オブジェクトの中までは入りません。取り出してから平らにします。
これが flatMap() のいちばん多い使い道です。
まとめ
flat() は引数を省くと1段。深さは数で指定する
- 深さが不明なら
Infinity。ただし前提が消えることを承知で
flatMap() は変換と平坦化を同時に。平らにするのは1段だけ
- 空の配列を返せば要素を消せる
- 穴は黙って取り除かれる。 長さが変わる
- どちらも元の配列を変えない