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
JavaScriptfor (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
JavaScriptSet は「挿入した順番」で値を返します。
配列と同じ感覚で、「順番に全部処理したい」場面で素直に使えます。
Set の forEach
配列と同じように forEach も使えます。
colors.forEach((value) => {
console.log("value:", value);
});
JavaScriptSet の 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);
}
JavaScriptfor...of scores は for...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];
JavaScriptSet から配列:
const set = new Set([1, 2, 3]);
const arr = [...set];
JavaScriptMap の「エントリ配列」:
const map = new Map([
["a", 1],
["b", 2],
]);
const entries = [...map];
console.log(entries); // [["a", 1], ["b", 2]]
JavaScriptSet の重複排除と反復の組み合わせ
「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)] で「重複なし配列」になっているので、
あとは普通の配列の map や forEach を使えます。
ここが重要です。
「イテラブル → 配列」への変換は、[...iterable] という形を体に覚えてしまうと、
Map / Set の反復処理が一気に柔らかくなります。
WeakMap / WeakSet と反復処理
WeakMap / WeakSet は「反復できない」と理解しておく
WeakMap と WeakSet は、
ガベージコレクションと連動するために「中身を一覧してはいけない」設計になっています。
そのため、
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 // なし
JavaScriptWeakMap / WeakSet は
「ある特定のオブジェクトについて追加情報があるか」「フラグが立っているか」
を、get / has でピンポイントに見るための構造です。
ここが重要です。
「全部なめたい」「何個あるか数えたい」と思ったら、Weak 系ではなく
普通の Map / Set にするべき、という判断軸を持っておいてください。
反復処理の選び方(実務での感覚)
単純に「全部の要素を順番に処理したい」なら
配列 / Set / Map いずれも、
for…of か forEach で回すのが基本です。
配列:
for (const x of array) { … }
array.forEach((x) => { … });
JavaScriptSet:
for (const value of set) { … }
set.forEach((value) => { … });
JavaScriptMap:
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 の反復処理」が自然な道具として手に馴染んでいきます。
