本文へスキップ
ウェブエンジニア問題集
配列メソッド 5

配列の初期化・平坦化メソッド — Array.from・fill・flat・flatMap

約12分
この章の目次開く

この章では、配列の作成・初期化と、ネストした配列の処理で使う Array.from・fill・flat・flatMap を扱います。

書き方・メソッド役割注意点
[](配列リテラル)新しい配列を作る最も基本的な作り方
Array.from配列ではないものから配列を作る静的メソッド。第2引数で変換もできる
fill配列を同じ値で埋める破壊的メソッド。同じ参照が入ることがある
flatネストした配列を平坦化する深さを指定できる
flatMap変換して1段階だけ平坦化するmap().flat(1) に近い

配列リテラル — 新しい配列を作る

配列を作るもっとも基本的な方法は、角かっこを使った配列リテラル [] です。

const empty = [];
const numbers = [1, 2, 3];
const users = [{ id: 1, name: '太郎' }, { id: 2, name: '花子' }];
js

要素の型を揃える決まりはないため、異なる型を混ぜることもできます(実務では型を揃えるのが一般的です)。

const mixed = ['太郎', 20, true, null];
js

空配列から作って、あとから要素を足していく書き方もよく使います。

const logs = [];
 
logs.push('開始'); // 破壊的に追加
const added = [...logs, '完了']; // 非破壊で追加した新しい配列
js

追加・削除の使い分けは 配列の破壊的メソッド を参照してください。

new Array・Array.of との違い

Array コンストラクタでも配列を作れますが、数値の引数を1つだけ渡した場合は「長さの指定」として扱われるため、意図と違う結果になりやすいです。

new Array(3); // [空 × 3] ← 長さ3で要素が空の配列
new Array(1, 2, 3); // [1, 2, 3]
 
Array.of(3); // [3] ← 常に「その値を要素に持つ配列」
js

Array.of は、この引数1つのときの挙動の違いをなくすために用意された静的メソッドです。


Array.from — 配列ではないものから配列を作る

Array.fromで変換するイメージ

Array.from は、配列ではないもの(配列風オブジェクトや反復可能オブジェクト)から新しい Array インスタンスを生成する静的メソッドです。 map や filter と違い、配列のインスタンスに対して呼ぶのではなく、Array.from(...) のように Array に直接呼び出します。

構文: Array.from(arrayLike, mapFn?)

引数説明
arrayLike配列に変換したい対象。length プロパティを持つオブジェクトや、for...of で回せるもの
mapFn(省略可)各要素に適用する変換関数。(element, index) を受け取る

戻り値: 新しい配列。元の arrayLike は変更されない

配列風オブジェクトを配列に変換する

document.querySelectorAll() が返す NodeList や、関数の arguments オブジェクトは、length を持っていますが Array のインスタンスではないため、.map() などが使えません。 Array.from で本物の配列に変換すれば、配列メソッドが使えるようになります。

const nodes = document.querySelectorAll('div');
// nodes.map(...) → TypeError: nodes.map is not a function
 
const arr = Array.from(nodes);
arr.map((node) => node.textContent); // OK
js

文字列も反復可能なので、1文字ずつの配列に分解できます。

Array.from('hello'); // ["h", "e", "l", "l", "o"]
js

第2引数の mapFn — 変換しながら配列を作る

第2引数に関数を渡すと、Array.from(x).map(fn) と同じ結果を1ステップで得られます。

Array.from([1, 2, 3], (x) => x * 2); // [2, 4, 6]
js

この mapFn は、{ length: n } と組み合わせて「n個の要素を持つ初期化済み配列」を作るパターンでよく使います。

// 0〜4 の連番
Array.from({ length: 5 }, (_, i) => i);
// [0, 1, 2, 3, 4]
 
// 要素ごとに独立した空配列を作る
Array.from({ length: 3 }, () => []);
// [[], [], []]  ← 各要素が別々の配列
js
学習者学習者

new Array(5).fill(0).map(...) で連番を作ったことがあるけど、Array.from なら中間配列なしで1回で作れるんですね。


fill — 配列を同じ値で埋める

fill は、配列の要素を指定した値で埋めるメソッドです。 配列の初期化や、既存配列の一部をまとめて同じ値に置き換えたいときに使います。

ただし、fill は元の配列を直接変更する破壊的メソッドです。 map や filter のように新しい配列を返す感覚で使うと、意図せず元の配列を書き換えてしまいます。

構文: array.fill(value, start?, end?)

引数説明
value埋める値
start(省略可)埋め始める位置。省略すると 0
end(省略可)埋め終える位置。その手前までが対象

戻り値: 埋めたあとの配列。新しい配列ではなく、元の配列と同じ参照が返る

const scores = new Array(5).fill(0);
console.log(scores); // [0, 0, 0, 0, 0]
 
const statuses = ['todo', 'todo', 'todo', 'todo'];
statuses.fill('done', 1, 3);
 
console.log(statuses); // ["todo", "done", "done", "todo"]
js

実務パターン: 初期値つきの配列を作る

new Array(length) だけだと「長さはあるが、要素が空の配列」になります。 そのままでは map などが期待どおりに処理されないため、連番や初期値つき配列を作るときは fill と組み合わせることがあります。

const indexes = new Array(5).fill(0).map((_, index) => index);
console.log(indexes); // [0, 1, 2, 3, 4]
 
const counts = new Array(3).fill(0);
console.log(counts); // [0, 0, 0]
js
const original = [1, 2, 3];
 
// ❌ original も変わる
original.fill(0);
console.log(original); // [0, 0, 0]
 
// ⭕ コピーしてから fill する
const numbers = [1, 2, 3];
const filled = [...numbers].fill(0);
 
console.log(filled);  // [0, 0, 0]
console.log(numbers); // [1, 2, 3]
js

オブジェクトを fill すると同じ参照が入る

fill にオブジェクトや配列を渡すと、各要素に別々のオブジェクトが作られるわけではありません。 同じ参照がすべての要素に入ります。

const rows = new Array(3).fill([]);
 
rows[0].push('A');
 
console.log(rows); // [["A"], ["A"], ["A"]]
js

これは「3つの空配列を作った」のではなく、「1つの空配列への参照を3か所に入れた」状態です。 2次元配列やオブジェクト配列を初期化するときは、上で紹介した Array.from を使って要素ごとに新しい値を作ります。

const rows = Array.from({ length: 3 }, () => []);
 
rows[0].push('A');
 
console.log(rows); // [["A"], [], []]
js

flat — ネストされた配列を平坦化する

flat は配列内のネストを取り除き、指定した深さまで平坦化した新しい配列を返します。

構文: array.flat(depth?)

引数渡せるもの説明
depth(省略可)数値平坦化する深さ。省略すると 1。全階層なら Infinity

戻り値: 平坦化した新しい配列。元の配列は変わらない

const nested = [1, [2, 3], [4, [5, 6]]];
 
nested.flat(); // [1, 2, 3, 4, [5, 6]]
nested.flat(2); // [1, 2, 3, 4, 5, 6]
js

深さが不明な場合は Infinity を渡すと、どれだけ深くても完全に平坦化できます。

const deep = [1, [2, [3, [4, [5]]]]];
 
deep.flat(Infinity); // [1, 2, 3, 4, 5]
js

また、flat は配列内の空スロット(holes)を取り除く性質もあります。

const sparse = [1, , 3, , 5];
 
sparse.flat(); // [1, 3, 5]
js

flatMap — 変換と平坦化を同時に行う

flatMap は各要素にコールバックを適用し、その結果を1段階だけ平坦化した新しい配列を返します。 map してから flat(1) するのと同じですが、1回の走査で済むため効率的です。

構文: array.flatMap(callbackFn)

引数渡せるもの説明
callbackFn(element, index, array) => 値各要素を変換する関数。引数は map と同じ3つ。配列を返すと1段だけ展開される

戻り値: 変換結果を1段階だけ平坦化した新しい配列。元の配列は変わらない

flat と違い、flatMap に深さの引数はありません。平坦化は常に1段です。

const sentences = ['Hello world', 'Good morning'];
 
sentences.map((s) => s.split(' '));
// [["Hello", "world"], ["Good", "morning"]]
 
sentences.flatMap((s) => s.split(' '));
// ["Hello", "world", "Good", "morning"]
js

コールバックが返す配列の長さで、要素数を増減できる

学習者学習者

引数は map と同じなのに、filter の代わりにもなるって説明を見て混乱しました。結局なにを返せばいいんですか?

flatMap が分かりにくいのは、コールバックが何を返すかで結果の要素数が変わるからです。ここさえ押さえれば、使い方は一本道になります。

コールバックが返すもの結果の要素数相当する操作
[a, b](2要素の配列)2つに増える1対多の変換(分割・展開)
[a](1要素の配列)1つのままmap と同じ
[](空配列)消えるfilter で除外するのと同じ
a(配列以外の値)1つのままmap と同じ。配列で包まなくてよい
[1, 2, 3].flatMap((n) => [n, n * 10]); // [1, 10, 2, 20, 3, 30] ← 増やす
[1, 2, 3].flatMap((n) => (n % 2 === 1 ? [n] : [])); // [1, 3] ← 減らす
[1, 2, 3].flatMap((n) => n * 2); // [2, 4, 6] ← map と同じ
js
「1つの要素から0個・1個・複数個を作り分けられる map」が flatMap の正体です。

実務パターン: filter + map を1回で

flatMap で空配列 [] を返すと、その要素を除外できます。 filter + map を1回の走査にまとめたいときに便利です。

const items = [
  { name: 'PC', price: 120000, inStock: true },
  { name: 'マウス', price: 3000, inStock: false },
  { name: 'モニター', price: 45000, inStock: true },
];
 
items.filter((i) => i.inStock).map((i) => i.name);
 
items.flatMap((i) => (i.inStock ? [i.name] : []));
// ["PC", "モニター"]
js

ちゃんと使うためのポイント

  • 配列を作る基本は配列リテラル []
  • new Array(n) は数値1つだと「長さの指定」になるため、要素にしたいときは Array.of を使う
  • Array.from は配列風オブジェクトや { length: n } から配列を作る静的メソッド
  • 第2引数の mapFn を使えば、連番や独立したオブジェクト配列を1ステップで生成できる
  • fill は元配列を変更する
  • オブジェクトや配列を fill すると、すべての要素に同じ参照が入る
  • 2次元配列やオブジェクト配列の初期化は Array.from を使う
  • flat はネスト配列を指定した深さまで平坦化する。引数を省略すると深さ1、全階層なら Infinity
  • flatMap は「1つの要素から0個以上の要素を作る」変換で便利
  • flatMap はコールバックの戻り値で要素数が決まる。空配列 [] で除外、複数要素の配列で増加、配列以外の値なら map と同じ
  • flatMap の平坦化は1段固定で、深さは指定できない

次の章では、文字列操作と正規表現 を取り上げます。実務でよく使うパターンをまとめます。

参考リンク