1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【JavaScript】Mapって何?

Last updated at Posted at 2024-12-10

Mapとは

Mapオブジェクトはキーと値のペアを持つオブジェクトです。

データ構造はオブジェクトリテラルと似ていますが、Mapは特にキーが変動する連想配列に使えます。

使い方

コンストラクタから作成できます。

const map = new Map()

キーと値のペアを登録するにはsetを使います。

map.set('text1', 'Hello World!')
map.set('text2', 'JavaScript')

キーから値を取得するにはgetを使います。

map.get('text1') // 'Hello World!'
map.get('text2') // 'JavaScript'

よく使うメソッド

Mapオブジェクトからデータを取得したり操作するには、メソッドを使うのが一般的です。
ここではその中でもよく使うものを紹介します。

get, set

この2つはMapの最も基本的なメソッドで、値を登録したり取得したりできます。

  • get: キー対応する値を取得する
  • set: キーと値のペアを登録する
const map = new Map()
map.set('key', 'value')
map.get('key') // 'value'

なお、getで指定されたキーが見つからなかった場合、undefinedが返されます。

const map = new Map()
map.get('not-found') // undefined

has

Mapの中に与えられたキーが登録されているかを調べます。
戻り値は登録されているかを示すbooleanです。

const map = new Map()
map.set('key', 'value')

map.has('key') // true
map.has('not-found') // false

hasを使うと、キーが存在するかどうかで条件分岐ができます。
例えば、

  • キーが存在する: その値を使って何か処理をする
  • キーが存在しない: そのキーに何か値を登録する

といったことができます。

if (map.has(key)) { // 条件分岐
  const value = map.get(key)
  // ...
} else {
  // ...
  const value = // 登録したい値
  map.set(value)
}

forEach

値とキーのペアに対して、与えられた関数(callbackFn)を順番に実行します。

callbackFnは以下の引数で実行されます。

  1. value: ペアの値
  2. key: ペアのキー
  3. map: Mapオブジェクトそのもの

valuekeyより先に来るので注意しでください。

const map = new Map()
map.set('key1', 10)
map.set('key2', 20)

map.forEach((value, key) => {
  console.log(`${key}: ${value}`)
})

// key1: 10
// key2: 20

削除系

ペアの削除には2つのメソッドが使えます。

  • delete: 与えられたキーのペアを削除
  • clear: 全てのペアを削除

コンストラクタ

コンストラクタに反復可能オブジェクトを入れることで、Mapの初期値を設定できます。
要素は[キー, 値]形式のArray-likeオブジェクトである必要があります。

反復可能やArray-likeには、例えばArrayがあります。
配列を使うと、例えば以下のようにオブジェクトを作成できます。

const map = new Map([
  ['key', 'value'],
  ['key2', 10],
])

map.get('key') // 'value'
map.get('key2') // 10

Array-Like

ここでのArray-Likeとは、[]を使って各要素にアクセスできるオブジェクトです。
また、コンストラクタで使われるのは以下のプロパティのようです。

  • 0: キー
  • 1: 値

そのためコンストラクタの引数iterableは、この2つのプロパティがあるオブジェクトを要素に取れます。

例えば以下のようなオブジェクトでも正常にMapを作成できます。

new Map([{ 0: 'key', 1: 'value' }])
// Map(1) { 'key' => 'value' }

仕様書だとMapから辿れるAddEntriesFromIterableが該当しそうです。

The parameter iterable is expected to be an object that implements a %Symbol.iterator% method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a Map key and whose second element is the value to associate with that key.

機械翻訳:

パラメータ iterable は、最初の要素がマップ・キーとして使用される値で、2 番目の要素がそのキーに関連付けられる値である 2 つの要素の配列のようなオブジェクトを生成するイテレータ・オブジェクトを返す %Symbol.iterator% メソッドを実装するオブジェクトであることが期待されます。

また、Array-Likeについてはこちらの記事も参考になると思います。

オブジェクトとの違い

大半の場合では、通常のオブジェクトリテラルでもMapと似たようなことができます。
しかし、オブジェクトリテラルではなくMapを使うべき場面もあります。

サイズを取得したい場合

Mapの場合、サイズはsizeプロパティから取得できます。

const map = new Map([
  [1: 'ABC'],
  [2: 'DEF'],
  [3: 'GHI'],
])

map.size // 3

しかし、特定のオブジェクトにいくつの要素があるのかを、直接調べる方法はありません。
Object.keys()から返される配列のlengthを調べる方法があります。

Object内のアイテムの数を決定することは、より回りくどく、効率的ではありません。一般的な方法は、Object.keys()から返される配列のlengthを通じて行う方法です。 - MDN

const obj = {
  1: 'ABC',
  2: 'DEF',
  3: 'GHI'
}

Object.keys(obj).length // 3

ユーザーの入力を入れる場合

Mapのキーや値にユーザーの入力があるのは安全です。

const input = ['__proto__', 'console.log("Hello World!")']
map.set(input[0], input[1]) // OK

しかし、オブジェクトにユーザーの入力を入れた場合、予期せぬ挙動を引き起こす場合があります。

特にバリデーションなどを設けなかった場合、ユーザーは__proto__constructorなど、JavaScriptの内部の動作に影響を及ぼす可能性のあるプロパティを書き換えられます。
これはオブジェクトインジェクション攻撃を引き起こす可能性があります。

const input = ['__proto__', 'console.log("Hello World!")']
map.set(input[0], input[1]) // OK

ユーザーが提供したキーと値のペアを Object に設定すると、攻撃者がオブジェクトのプロトタイプを上書きできる可能性があり、 オブジェクトインジェクション攻撃 につながる可能性があります。

偶発的なキーの問題と同様に、これもnull-prototypeオブジェクトを使用することによって軽減することができます。

シリアライズしたい場合

文字列にシリアライズしたい場合、オブジェクトのほうが簡単です。
オブジェクトはJSON.stringifyJSON.parseで簡単に文字列と変換できます。

const obj = { key: 'value' }

// JSON形式の文字列にできる
const str = JSON.stringify(obj) // '{"key":"value"}'

// 簡単にオブジェクトにできる
const obj2 = JSON.parse(str) // {key: 'value'}

しかし、Mapだとそう簡単にはいきません。
シリアライズしたい場合、そのような処理を自作するか、ライブラリに頼る必要があります。

シリアライズや解釈のためのネイティブな対応はありません。

キーの順番が重要な場合

詳細はこちらの記事が参考になると思います。

Mapの場合

キーはsetで挿入された順番で保管されます。
そのためforEachメソッドなどでの反復処理の順番は、setで登録された順番になります。

また、コンストラクタで追加された要素の順番も保証されます。

オブジェクトの場合

オブジェクトのプロパティの順序は複雑だそうです。
そのため、順序に依存する処理はあまり書かないほうが良いでしょう。

通常の Object のキーは現在では順序付けされていますが、以前はそうではなかったので、順序は複雑です。結果として、プロパティの順序に頼らない方が良いでしょう。


これ以外にもオブジェクトとMapにはいくつか違いがあります。
詳細はMDNをご覧ください。

1
0
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?