JavaScript | ES6+ 文法:新データ構造 – 反復処理

JavaScript JavaScript
スポンサーリンク

ES6 の反復処理とは何か(まずイメージから)

ES6 以降の「新データ構造」(Map, Set, WeakMap, WeakSet)には、
「反復処理しやすいように統一された仕組み」 が用意されています。

この「反復処理しやすいもの」を
専門用語で「イテラブル(iterable)」と言います。

配列も、Map も、Set も、
「for…of で回せる」「...(スプレッド構文)で展開できる」
という共通のルールを持っています。

ここが重要です。
ES6 の反復処理を理解するときは、
「配列だけ特別」ではなく
「配列も Map も Set も、みんな“同じルール”で回せる」と捉えるのがポイントです。

for…of と従来の for 文の違い

昔ながらの for 文(インデックスで回す)

配列を回すとき、昔はよくこう書きました。

const nums = [10, 20, 30];

for (let i = 0; i < nums.length; i++) {
  console.log(i, nums[i]);
}
JavaScript

インデックス i を使って、nums[i] を取り出すスタイルです。
これは今でも使えますが、インデックスを手で管理する必要があります。

ES6 の for…of(値をそのまま取り出す)

ES6 以降は、より「中身に集中できる」書き方ができます。

const nums = [10, 20, 30];

for (const value of nums) {
  console.log(value);
}
// 10
// 20
// 30
JavaScript

for (const value of nums)
「nums の中身を 1 個ずつ、順番に取り出して value に入れる」
という意味です。

インデックスを意識せず、「中身そのもの」に集中できるので、
配列だけでなく Map / Set でも同じ書き方が使えます。

ここが重要です。
for...of は「イテラブルを回すための共通文法」です。
配列、文字列、Map、Set など、「反復可能なもの」なら何でも回せます。

Set の反復処理

Set を for…of で回す

Set は「重複を許さない値の集合」でした。
for…of で簡単に回せます。

const colors = new Set(["red", "green", "blue"]);

for (const color of colors) {
  console.log(color);
}
// red
// green
// blue
JavaScript

Set は「挿入した順番」で値を返します。
配列と同じ感覚で、「順番に全部処理したい」場面で素直に使えます。

Set の forEach

配列と同じように forEach も使えます。

colors.forEach((value) => {
  console.log("value:", value);
});
JavaScript

Set の forEach には、(value, value, set) という少し変わった引数が渡されますが、
普通は最初の value だけ見ていれば十分です。

反復処理+Set の得意技を組み合わせる

例えば、Set で重複排除したあとに、
反復処理で 2 倍にしてみます。

const nums = [1, 2, 2, 3, 3, 3];

const uniqueSet = new Set(nums);  // 重複なし集合
const doubled = [];

for (const n of uniqueSet) {
  doubled.push(n * 2);
}

console.log(doubled); // [2, 4, 6]
JavaScript

「Set で集合を作る → for…of で順番に処理する」
この流れはかなりよく使います。

Map の反復処理

Map をそのまま for…of すると [key, value] のペアになる

Map は「キーと値のペア」を持つコレクションでした。
for…of すると、1 回ごとに [key, value] の配列が取れます。

const scores = new Map();
scores.set("Alice", 80);
scores.set("Bob", 90);

for (const [name, score] of scores) {
  console.log(name, score);
}
// Alice 80
// Bob 90
JavaScript

[name, score] という分割代入がかなり気持ちよく使えます。
Map の反復処理では、ほぼこの書き方を使うと思ってよいです。

keys / values / entries で役割ごとに回す

Map には、反復処理用のメソッドがいくつかあります。

const scores = new Map([
  ["Alice", 80],
  ["Bob", 90],
]);

for (const name of scores.keys()) {
  console.log("name:", name);
}

for (const score of scores.values()) {
  console.log("score:", score);
}

for (const [name, score] of scores.entries()) {
  console.log("entry:", name, score);
}
JavaScript

for...of scoresfor...of scores.entries() と同じ動きです。
「キーだけ欲しい」「値だけ欲しい」というときは keys() / values() を使います。

Map の forEach

Map も forEach を持っています。

scores.forEach((score, name) => {
  console.log(name, score);
});
JavaScript

ここは引数の順番に注意です。
(value, key) の順で渡されます。

イテラブルとスプレッド構文(…)の関係

反復可能なものはスプレッドで展開できる

for...of で回せるもの(イテラブル)は、
スプレッド構文 ... で配列に展開することもできます。

配列のコピー:

const nums = [1, 2, 3];
const copied = [...nums];
JavaScript

Set から配列:

const set = new Set([1, 2, 3]);
const arr = [...set];
JavaScript

Map の「エントリ配列」:

const map = new Map([
  ["a", 1],
  ["b", 2],
]);

const entries = [...map];
console.log(entries); // [["a", 1], ["b", 2]]
JavaScript

Set の重複排除と反復の組み合わせ

「Set による重複排除」+「反復処理」を組み合わせる典型例です。

const nums = [1, 2, 2, 3, 3, 3];

// 重複を消してから、その値を使って新しい配列を作る
const doubled = [...new Set(nums)].map((n) => n * 2);

console.log(doubled); // [2, 4, 6]
JavaScript

[...new Set(nums)] で「重複なし配列」になっているので、
あとは普通の配列の mapforEach を使えます。

ここが重要です。
「イテラブル → 配列」への変換は、
[...iterable] という形を体に覚えてしまうと、
Map / Set の反復処理が一気に柔らかくなります。

WeakMap / WeakSet と反復処理

WeakMap / WeakSet は「反復できない」と理解しておく

WeakMapWeakSet は、
ガベージコレクションと連動するために「中身を一覧してはいけない」設計になっています。

そのため、

for…of
forEach
keys, values, entries
size

こういった「中身を全部見る」ための機能は一切ありません。

const wm = new WeakMap();
const ws = new WeakSet();

// for (const v of ws) {}         // エラー
// ws.forEach(...)                // なし
// wm.keys()                      // なし
// wm.size                        // なし
JavaScript

WeakMap / WeakSet は
「ある特定のオブジェクトについて追加情報があるか」「フラグが立っているか」
を、get / has でピンポイントに見るための構造です。

ここが重要です。
「全部なめたい」「何個あるか数えたい」と思ったら、Weak 系ではなく
普通の Map / Set にするべき、という判断軸を持っておいてください。

反復処理の選び方(実務での感覚)

単純に「全部の要素を順番に処理したい」なら

配列 / Set / Map いずれも、
for…of か forEach で回すのが基本です。

配列:

for (const x of array) { … }
array.forEach((x) => { … });
JavaScript

Set:

for (const value of set) { … }
set.forEach((value) => { … });
JavaScript

Map:

for (const [key, value] of map) { … }
map.forEach((value, key) => { … });
JavaScript

「キーだけ」「値だけ」が欲しいなら

Map なら keys() / values() を使います。

for (const key of map.keys()) { … }
for (const value of map.values()) { … }
JavaScript

「配列にしてから配列メソッドを使いたい」なら

イテラブルを配列に変換してから、map, filter, reduce を使うのが楽です。

const doubled = [...set].map((x) => x * 2);
const keyList = [...map.keys()];
JavaScript

まとめ

ES6 の「新データ構造」と反復処理の関係をまとめると、こうなります。

配列、文字列、Map、Set は「イテラブル」であり、for...of で同じように回せる
Set は「値の集合」を、Map は「[キー, 値] のペア」を、順番を保って返してくれる
スプレッド構文 ... を使えば、「イテラブル → 配列」の変換が簡単にできる
WeakMap / WeakSet は GC と連動するため、反復処理(for…of, size など)はできない
辞書的な処理をしたいときは、「Object で for…in」より「Map で for…of」の方が読みやすいことが多い

まずは、配列で書いていた for 文の一部を

for (const x of array)

に置き換えてみてください。

慣れてきたら、

  • Set を for...of で回す
  • Map を for (const [k, v] of map) で回す

という 2 つのパターンを、手を止めずに書けるようにしていくと、
「ES6 の反復処理」が自然な道具として手に馴染んでいきます。

タイトルとURLをコピーしました