🐰

JavaScriptの便利な機能を復習【Map・イテレーター / ジェネレーター】

2023/02/11に公開

JavaScriptの便利な機能を復習【Map・イテレーター / ジェネレーター】

Map オブジェクト

Map オブジェクトは Object と同じで キー: 値 の形式で値を格納することができます。
詳細については mdn web docs を参照してください。

Map と Object 比較

ざっくり言うと大きな違いとしてこちらです。

  • Map は iterable なので for…of を使って反復処理が書ける。Object では for…in。
  • Map には要素を更新するためのメソッドが用意されている。
  • キー: 値 の更新を頻繁に行う場合は Map の方が性能が良い。

Map で for…of を使う

const sample = new Map()
sample.set('key1', 'aaa')
sample.set('key2', 'bbb')
sample.set('key3', 'ccc')

for(const item of sample) {
  console.log(item);
}
👆このように書くと item には各要素が配列として渡ってきます
["key1", "aaa"]
["key2", "bbb"]
["key3", "ccc"]

for(const [key, value] of sample) {
  console.log(key, value);
}
👆このように書くと各要素の key, value を分割して取得できます
"key1" "aaa"
"key2" "bbb"
"key3" "ccc"

Object の場合は for…in を使う

const sample = {
  'key1': 'aaa',
  'key2': 'bbb',
  'key3': 'ccc'
}

for(const key in sample) {
  console.log(key)
}
👆各要素の key が渡ってきます
"key1"
"key2"
"key3"

イテレーター(Iterator)について

next メソッドを使って反復処理を定義するためのオブジェクトです。
詳細については mdn web docs を参照してください。

例えば数値を10までインクリメントして配列に追加していくイテレーターはこのように書けます。

function sampleIterator(start = 0, end = 10) {
    let nextIndex = start;
    let array = [start];

    const iterator = {
       next: function() {
           if (nextIndex <= end) {
               nextIndex ++;
               return { value: array.push(nextIndex), done: false };
           }
           return { value: array, done: true };
       }
    };
    return iterator;
}

ポイント

  • 初期値など呼び出し側で定義したい値はパラメータにする
  • next メソッドでは value, done をプロパティに持たせる
    → value には処理の結果の値、done には true / false が入る

呼び出す際 while を使って書くと以下のようになります。

const it = sampleIterator(1);
let result = it.next();
while(!result.done) {
    result = it.next();
}

console.log(result.value)
// [ 1, 2, 3, 4,  5, 6, 7, 8, 9, 10]

初期値を1としているので1~10が格納された配列が取得できます。

ジェネレーター(Generator)について

ジェネレーター関数を使うことでイテレーターの処理を簡潔に書くことができます。

function* sampleGeneator(start = 0, end = 10) {
    let array = [start];
    for (let nextIndex = start; nextIndex <= end; nextIndex++) {
        array.push(nextIndex);
        yield nextIndex;
    }
    return array;
}

ポイント

  • functionの後ろに * を付けて関数を定義
  • yield で反復処理の継続を行う
    →イテレーターだと next メソッドで return { done: false } を行うのと同じ

同じようにして呼び出すことができます。

const it = sampleGeneator(1);
let result = it.next();
while(!result.done) {
    result = it.next();
}

console.log(result.value)
// [ 1, 2, 3, 4,  5, 6, 7, 8, 9, 10]

まとめ

反復処理のコードを見ると Map で for…of を使う方が要素を扱いやすいように感じます。
要素の更新をする際もメソッドの返り値で真偽値が返ってくるので安心感もあります。
パフォーマンスも Map の方が良いらしいので積極的に使っていきたいですね。

ジェネレーターを使うと反復処理をかなり簡潔に書くことができるので積極的に使っていきたいですね。
yield の挙動がイテレーターを理解していないと分かりづらいのでイテレーターと合わせて知っておくと良さそうです。

参考

Discussion