JavaScriptのMapの使い方と具体例

2021年2月1日JavaScript

JavaScriptのMapはとても便利なので詳しくみていきましょう。

Mapの使い方

Mapオブジェクトはキーと値のペアを保持します。

そして、キーが最初に挿入された順序を覚えてくれているので、それを利用することができます。

キーや値には任意の値 (オブジェクトとプリミティブ値)を使用することができるため、Mapはどんなデータ型でも使うことができます。
まずはmapに情報をセットする方法をみていきましょう。

Mapとset

{
const rest = new Map();
rest.set('name', '居酒屋エンジニア');
rest.set('1', '東京、日本');
console.log(rest); //Map(2) {"name" => "居酒屋エンジニア", "1" => "東京、日本"}
}

このようにMapは次々とsetで設定された値を格納していくことができます。

setとチェーンして使用

setはチェーンして設定していくことができます。

{
  const rest = new Map();
rest.set('categories', ['中華', '和食','イタリアン', 'フレンチ', 'アフリカン']).set('open', 10).set('close', 22).set(true, '営業中です:)').set(false, '閉店中ですm(_ _)m');
}

Mapとget

{
//getで取得
const rest = new Map();
rest.set('categories', ['中華', '和食','イタリアン', 'フレンチ', 'アフリカン']).set('open', 10).set('close', 22).set(true, '営業中です:)').set(false, '閉店中ですm(_ _)m');
console.log(rest.get('name')); //居酒屋エンジニア
console.log(rest.get(true)); //営業中です:)
console.log(rest.get('categories')); //(5) ["中華", "和食", "イタリアン", "フレンチ", "アフリカン"]
}

Mapとtrue/falseを上手に使う

{
//true/falseを上手に使う
const rest = new Map();
rest.set('categories', ['中華', '和食','イタリアン', 'フレンチ', 'アフリカン']).set('open', 10).set('close', 22).set(true, '営業中です:)').set(false, '閉店中ですm(_ _)m');
const time = 20;
const openMsg = rest.get(time > rest.get('open') && time < rest.get('close'));
console.log(openMsg); //trueが返されて「営業中です:)」が表示される
}

Mapとhas

{
//has
const rest = new Map();
rest.set('categories', ['中華', '和食','イタリアン', 'フレンチ', 'アフリカン']).set('open', 10).set('close', 22).set(true, '営業中です:)').set(false, '閉店中ですm(_ _)m');
console.log(rest.has('categories')); //true
}

Mapとdelete

{
//delete
rest.delete(1);
}

Mapとsize

{
//size
const rest = new Map();
rest.set('categories', ['中華', '和食','イタリアン', 'フレンチ', 'アフリカン']).set('open', 10).set('close', 22).set(true, '営業中です:)').set(false, '閉店中ですm(_ _)m');
console.log(rest.size);//5
}

sizeはlengthの代わりに使うと便利です。

Mapとclear

{
//clear
rest.clear();
}

配列をキーとして使う

 

//配列をキーとして使うとき
const arr = [2,2];
rest.set(arr, 'テスト');
console.log(rest);
console.log(rest.size);

console.log(rest.get(arr));

配列をキーとして呼び出すときは、console.log(rest.get([2,2]))として呼び出すこととはできません。
それはメモリーと参照のところで説明しています。
そのため、配列をキーとするのであれば、一度変数に入れてから使う必要があります。
整理すると、Mapへはsetを使うことで、[2,2]といった配列でもキーとして設定することができますが、呼び出すときには、[2,2]では完全に新しい配列になってしまっているため呼び出せません。
そのため、キーとなる部分を変数で置き換えます。

DOMを操作する

//DOMを操作する
const h1 = new Map();
h1.set(document.querySelector('h1'), 'Heading');
console.log(h1);

実際にコンソールで確認すると、h1にカーソルをあわせることでh1を要素選択できることがわかります。

たくさん値があるときに便利にセットする方法

{
//たくさん値があるときに便利にセットする方法
const question = new Map([
  ['質問', '世界で最も使われているプログラミング言語はなんでしょうか。'],
  [1, 'C'],
  [2, 'Java'],
  [3, 'JavaScript'],
  ['正解', 3],
  [true, '正解'],
  [false, '残念、もう一度'],
]);
console.log(question);
}

setを使ってもいいのですが、このようにすると、見やすくいっきに設定することができます。

オブジェクトをMapに変換する

次のオブジェクトをMapに変換したいとします。
上のMapをコンソールでみると「entries」ということばが見つかります。とてもオブジェクトに似た形をしているのがわかると思います。
オブジェクトにObject.entries()を使うことで簡単にMapに変換することができます。

{
//オブジェクトをMapに変換する

const openingHours = {
  wed: {
    open: 12,
    close: 22,
  },
  thu: {
    open: 11,
    close: 23,
  },
  fri: {
    open: 0, // Open 24 hours
    close: 24,
  },
};
console.log(openingHours);
//const hoursMap = new Map(openingHours); これはできない。objectはiterableじゃないため。
const hoursMap = new Map(Object.entries(openingHours));
console.log(hoursMap);
}

オブジェクトはiterableではないため、forループがつかえません。
しかし、mapはiterableのため、forループが使えます。
そして、オブジェクトからmapへの変換はとても簡単です。

Mapでforループを使う

{
const question = new Map([
  ['質問', '世界で最も使われているプログラミング言語はなんでしょうか。'],
  [1, 'C'],
  [2, 'Java'],
  [3, 'JavaScript'],
  ['正解', 3],
  [true, '正解'],
  [false, '残念、もう一度'],
]);

console.log(question.get('質問'));
世界で最も使われているプログラミング言語はなんでしょうか。
for (const [key, value] of question) {
  if(typeof key === 'number') console.log(`答えは ${key}: ${value}`);
  答えは 1: C
  答えは 2: Java
  答えは 3: JavaScript
}

const answer = Number(prompt('あなたの答えは?'));
console.log(answer);
console.log(question.get(question.get('正解') === answer));
}

question.get('正解’) === answerという条件がtrueの場合、すでにMapに [true, '正解’]と返す配列が入っていますので、
3という正解が返ってくれば、question.get(true)という形に変換されます。
それ以外がanswerとして回答されると、question.get(false)となり、falseの文が返されます。

Mapを配列に変換する

[]をつけるだけなのでとても簡単です。

{
const question = new Map([
  ['質問', '世界で最も使われているプログラミング言語はなんでしょうか。'],
  [1, 'C'],
  [2, 'Java'],
  [3, 'JavaScript'],
  ['正解', 3],
  [true, '正解'],
  [false, '残念、もう一度'],
]);

console.log([...question]);
// entries()を使うとキーとバリューを取り出せる
console.log([...question.keys()]);
console.log([...question.values()]);
}

map()で返された関数の値を別の関数を作って返す

配列に何らかの処理をして、その結果を別の配列にしたい場合があります。
そんなときにmap()を使います。
map()は返された関数の値を別の関数を作って返すことができます。

たとえば10円ずつ値下げしたいとします。
すでに200円,300円,400円という値段にそれぞれ10円引いた結果がほしい場合、次のようになります。

 'use strict';
  {
    const prices = [200, 300, 400];

    const discountedPrices = prices.map((price) => {
      return price - 10;
    });
    console.log(discountedPrices);

    //より短く書く場合、次のようにもかけます。
    //const prices = [200, 300, 400];
    // const discountedPrices = price.map(price => price - 10);
    //console.log(discountedPrices);
  }

まずmap()で返される値を格納するため、定数を作ってあげます。
今回は割引するのでdiscountedPricesという定数名にします。
そして、引数としてpriceを用意しておきます。
返す処理を書いてreturnすることで、pricesのそれぞれの値が引数priceに渡されます。
そしてそれぞれ10円値引きした値を返し、discountedPricesに格納すことができます。

mapの挙動まとめ

const gameEvents = new Map([
  [15, '⚽️ ゴール'],
  [33, '🔁 選手交代'],
  [48, '⚽️ ゴール'],
  [61, '🔁 選手交代'],
  [67, '🔶 イエローカード'],
  [69, '🔴 レッドカード'],
  [70, '🔁 選手交代'],
  [76, '🔁 選手交代'],
  [78, '⚽️ ゴール'],
  [85, '⚽️ ゴール'],
  [90, '🔶 イエローカード'],
]);

/* ========================= */
/*   lengthを取得したい  */
/* ========================= */
console.log(gameEvents.size);//11


/* ========================= */
/*   mapの状態で値とキーを取得したい  */
/* ========================= */

//mapの状態でvaluesを取得する
console.log(gameEvents.values());
/*
MapIterator {"⚽️ ゴール", "🔁 選手交代", "⚽️ ゴール", "🔁 選手交代", "🔶 イエローカード", …}
[[Entries]]
0: "⚽️ ゴール"
1: "🔁 選手交代"
2: "⚽️ ゴール"
3: "🔁 選手交代"
4: "🔶 イエローカード"
5: "🔴 レッドカード"
6: "🔁 選手交代"
7: "🔁 選手交代"
8: "⚽️ ゴール"
9: "⚽️ ゴール"
10: "🔶 イエローカード"
*/
//mapの状態でkeysを取得する
console.log(gameEvents.keys());
/*返される値
MapIterator {15, 33, 48, 61, 67, …}
[[Entries]]
0: 15
1: 33
2: 48
3: 61
4: 67
5: 69
6: 70
7: 76
8: 78
9: 85
10: 90
*/


/* ========================= */
/*   一文で取得したい  */
/*   スプレッド構文  */
/* ========================= */

//スプレッド構文でvaluesだけ取得する
console.log(...gameEvents.values());
//⚽️ ゴール 🔁 選手交代 ⚽️ ゴール 🔁 選手交代 🔶 イエローカード 🔴 レッドカード 🔁 選手交代 🔁 選手交代 ⚽️ ゴール ⚽️ ゴール 🔶 イエローカード

//スプレッド構文でkeysだけ取得する
console.log(...gameEvents.keys());
//15 33 48 61 67 69 70 76 78 85 90


/* ========================= */
/*   mapを配列で取得したい  */
/*   配列+スプレッド構文  */
/* ========================= */

//配列+スプレッド構文でvaluesだけの配列をつくる
console.log([...gameEvents.values()]);
//(11) ["⚽️ ゴール", "🔁 選手交代", "⚽️ ゴール", "🔁 選手交代", "🔶 イエローカード", "🔴 レッドカード", "🔁 選手交代", "🔁 選手交代", "⚽️ ゴール", "⚽️ ゴール", "🔶 イエローカード"]

//配列+スプレッド構文でkeysだけの配列をつくる
console.log([...gameEvents.keys()]);
//(11) [15, 33, 48, 61, 67, 69, 70, 76, 78, 85, 90]

/* ========================= */
/*   mapの重複を削除したい  *
/*   set+配列+スプレッド構文  */
/* ========================= */
//set+配列+スプレッド構文でvaluesだけのsetをつくる
console.log(new Set([...gameEvents.values()]));
/*
Set(4) {"⚽️ ゴール", "🔁 選手交代", "🔶 イエローカード", "🔴 レッドカード"}
[[Entries]]
0: "⚽️ ゴール"
1: "🔁 選手交代"
2: "🔶 イエローカード"
3: "🔴 レッドカード"
*/

//set+配列+スプレッド構文でkeysだけのsetをつくる
console.log(new Set([...gameEvents.keys()]));
/*
Set(11) {15, 33, 48, 61, 67, …}
[[Entries]]
0: 15
1: 33
2: 48
3: 61
4: 67
5: 69
6: 70
7: 76
8: 78
9: 85
10: 90
*/


/* ========================= */
/*   ループとの組み合わせ  */
/* ========================= */
for (const [key, value] of gameEvents) {
  const half = key <= 45 ? '1st': '2nd'; 
   console.log(`[${half} HALF] ${key}: ${value}`);
 }

 /*
 Set(11) {15, 33, 48, 61, 67, …}
[1st HALF] 15: ⚽️ ゴール
[1st HALF] 33: 🔁 選手交代
[2nd HALF] 48: ⚽️ ゴール
[2nd HALF] 61: 🔁 選手交代
[2nd HALF] 67: 🔶 イエローカード
[2nd HALF] 69: 🔴 レッドカード
[2nd HALF] 70: 🔁 選手交代
[2nd HALF] 76: 🔁 選手交代
[2nd HALF] 78: ⚽️ ゴール
[2nd HALF] 85: ⚽️ ゴール

  */

mapから、重複のない値のみの配列をつくりたい

  1. mapのvalues()をスプレッド構文を使って配列にする(重複のある値のみの配列がつくれる)
  2. それをsetにセットして新しい変数に格納する(重複した値が取り除かれる)
  3. setから配列に変換する

という順番です。


const gameEvents = new Map([
  [15, '⚽️ ゴール'],
  [33, '🔁 選手交代'],
  [48, '⚽️ ゴール'],
  [61, '🔁 選手交代'],
  [67, '🔶 イエローカード'],
  [69, '🔴 レッドカード'],
  [70, '🔁 選手交代'],
  [76, '🔁 選手交代'],
  [78, '⚽️ ゴール'],
  [85, '⚽️ ゴール'],
  [90, '🔶 イエローカード'],
]);
let eventsSet = new Set([...gameEvents.values()]);
console.log(eventsSet);
//Set(4) {"⚽️ ゴール", "🔁 選手交代", "🔶 イエローカード", "🔴 レッドカード"}

const events = [...new Set(eventsSet)];//setから配列に変換
console.log(events);
/*
["⚽️ ゴール", "🔁 選手交代", "🔶 イエローカード", "🔴 レッドカード"]
0: "⚽️ ゴール"
1: "🔁 選手交代"
2: "🔶 イエローカード"
3: "🔴 レッドカード"
*/

Setに関しては次の記事を参考にしてみてください。