JavaScriptでは、開発者は使用する正しいデータ構造を決定するのに多くの時間を費やすことがよくあります。 これは、正しいデータ構造を選択すると、後でそのデータを操作しやすくなり、時間を節約し、コードを理解しやすくなるためです。 データのコレクションを格納するための2つの主要なデータ構造は、ObjectsArrays(オブジェクトの一種)です。 開発者は、オブジェクトを使用してキーと値のペアを格納し、配列を使用してインデックス付きリストを格納します。 ただし、開発者の柔軟性を高めるために、ECMAScript 2015仕様では、キーと値のペアの順序付きコレクションである Maps と、コレクションであるSetsの2つの新しいタイプの反復可能オブジェクトが導入されました。一意の値の。

この記事では、MapオブジェクトとSetオブジェクト、オブジェクトと配列との類似点または相違点、それらで使用できるプロパティとメソッド、およびいくつかの実際の使用例について説明します。

マップ

マップは、任意のデータ型をキーとして使用でき、そのエントリの順序を維持できるキーと値のペアのコレクションです。 マップには、オブジェクト(一意のキー/値ペアコレクション)と配列(順序付けられたコレクション)の両方の要素がありますが、概念的にはオブジェクトに似ています。 これは、エントリのサイズと順序は配列のように保持されますが、エントリ自体はオブジェクトのようにキーと値のペアであるためです。

マップはで初期化できます new Map() 構文:

const map = new Map()

これにより、空のマップが作成されます。

Output
Map(0) {}

マップへの値の追加

マップに値を追加するには、 set() 方法。 最初の引数がキーになり、2番目の引数が値になります。

以下は、3つのキー/値ペアをに追加します map:

map.set('firstName', 'Luke')
map.set('lastName', 'Skywalker')
map.set('occupation', 'Jedi Knight')

ここで、マップにオブジェクトと配列の両方の要素がどのようにあるかを確認し始めます。 配列と同様に、インデックスがゼロのコレクションがあり、デフォルトでマップに含まれるアイテムの数も確認できます。 マップは => キーと値のペアを次のように表す構文 key => value:

Output
Map(3) 0: {"firstName" => "Luke"} 1: {"lastName" => "Skywalker"} 2: {"occupation" => "Jedi Knight"}

この例は、文字列ベースのキーを持つ通常のオブジェクトに似ていますが、マップのキーとして任意のデータ型を使用できます。

マップに手動で値を設定することに加えて、すでに値を使用してマップを初期化することもできます。 これは、それぞれキーと値のペアである2つの要素を含む配列の配列を使用して行います。これは次のようになります。

[ [ 'key1', 'value1'], ['key2', 'value2'] ]

次の構文を使用して、同じマップを再作成できます。

const map = new Map([
  ['firstName', 'Luke'],
  ['lastName', 'Skywalker'],
  ['occupation', 'Jedi Knight'],
])

注:この例では、末尾のコンマを使用します。これは、ダングリングコンマとも呼ばれます。 これはJavaScriptの書式設定方法であり、データのコレクションを宣言するときの一連の最後の項目の最後にコンマがあります。 このフォーマットの選択は、よりクリーンな差分とより簡単なコード操作に使用できますが、それを使用するかどうかは好みの問題です。 末尾のコンマの詳細については、MDNWebドキュメントのこの末尾のコンマの記事を参照してください。

ちなみに、この構文は、オブジェクトに対して Object.entries()を呼び出した結果と同じです。 これにより、次のコードブロックに示すように、オブジェクトをマップに変換するための既成の方法が提供されます。

const luke = {
  firstName: 'Luke',
  lastName: 'Skywalker',
  occupation: 'Jedi Knight',
}

const map = new Map(Object.entries(luke))

または、1行のコードでマップをオブジェクトまたは配列に戻すこともできます。

以下は、マップをオブジェクトに変換します。

const obj = Object.fromEntries(map)

これにより、次の値になります。 obj:

Output
{firstName: "Luke", lastName: "Skywalker", occupation: "Jedi Knight"}

それでは、マップを配列に変換しましょう。

const arr = Array.from(map)

これにより、次の配列が作成されます。 arr:

Output
[ ['firstName', 'Luke'], ['lastName', 'Skywalker'], ['occupation', 'Jedi Knight'] ]

マップキー

マップは任意のデータ型をキーとして受け入れ、キー値の重複を許可しません。 これは、マップを作成し、文字列以外の値をキーとして使用し、2つの値を同じキーに設定することで実証できます。

まず、文字列以外のキーを使用してマップを初期化します。

const map = new Map()

map.set('1', 'String one')
map.set(1, 'This will be overwritten')
map.set(1, 'Number one')
map.set(true, 'A Boolean')

この例では、の最初のキーをオーバーライドします 1 次のものと、そしてそれは扱います '1' 文字列と 1 一意のキーとしての番号:

Output
0: {"1" => "String one"} 1: {1 => "Number one"} 2: {true => "A Boolean"}

通常のJavaScriptオブジェクトはすでに数値、ブール値、およびその他のプリミティブデータ型をキーとして処理できるというのが一般的な信念ですが、オブジェクトはすべてのキーを文字列に変更するため、実際にはそうではありません。

例として、オブジェクトをテンキーで初期化し、数値の値を比較します 1 キーと文字列化 "1" 鍵:

// Initialize an object with a numerical key
const obj = { 1: 'One' }

// The key is actually a string
obj[1] === obj['1']  // true

これが、オブジェクトをキーとして使用しようとすると、文字列が出力される理由です。 object Object 代わりは。

例として、オブジェクトを作成し、それを別のオブジェクトのキーとして使用します。

// Create an object
const objAsKey = { foo: 'bar' }

// Use this object as the key of another object
const obj = {
  [objAsKey]: 'What will happen?'
} 

これにより、次のようになります。

Output
{[object Object]: "What will happen?"}

これはマップには当てはまりません。 オブジェクトを作成し、それをマップのキーとして設定してみてください。

// Create an object
const objAsKey = { foo: 'bar' }

const map = new Map()

// Set this object as the key of a Map
map.set(objAsKey, 'What will happen?')

The key これで、Map要素のが作成したオブジェクトになります。

Output
key: {foo: "bar"} value: "What will happen?"

オブジェクトまたは配列をキーとして使用する場合に注意すべき重要な点が1つあります。マップは、オブジェクトのリテラル値ではなく、オブジェクトへの参照を使用して同等性を比較しています。 JavaScriptの場合 {} === {} 戻り値 false、同じ(空の)値があるにもかかわらず、2つのオブジェクトは同じ2つのオブジェクトではないためです。

つまり、同じ値を持つ2つの一意のオブジェクトを追加すると、2つのエントリを持つマップが作成されます。

// Add two unique but similar objects as keys to a Map
map.set({}, 'One')
map.set({}, 'Two')

これにより、次のようになります。

Output
Map(2) {{…} => "One", {…} => "Two"}

ただし、同じオブジェクト参照を2回使用すると、1つのエントリを持つマップが作成されます。

// Add the same exact object twice as keys to a Map
const obj = {}

map.set(obj, 'One')
map.set(obj, 'Two')

その結果、次のようになります。

Output
Map(1) {{…} => "Two"}

二番目 set() は最初のキーとまったく同じキーを更新しているため、値が1つしかないマップになります。

マップからのアイテムの取得と削除

オブジェクトを操作することの欠点の1つは、オブジェクトを列挙したり、すべてのキーまたは値を操作したりすることが難しい場合があることです。 対照的に、Map構造には、要素をより直接的に操作できるようにする多くの組み込みプロパティがあります。

新しいマップを初期化して、次のメソッドとプロパティを示すことができます。 delete(), has(), get()、 と size.

// Initialize a new Map
const map = new Map([
  ['animal', 'otter'],
  ['shape', 'triangle'],
  ['city', 'New York'],
  ['country', 'Bulgaria'],
])

使用 has() マップ内のアイテムの存在を確認するメソッド。 has() ブール値を返します。

// Check if a key exists in a Map
map.has('shark') // false
map.has('country') // true

使用 get() キーで値を取得するメソッド。

// Get an item from a Map
map.get('animal') // "otter"

マップがオブジェクトよりも優れている点の1つは、配列の場合と同様に、いつでもマップのサイズを見つけることができることです。 マップ内のアイテムの数は、 size 財産。 これには、オブジェクトを配列に変換して長さを見つけるよりも少ない手順で済みます。

// Get the count of items in a Map
map.size // 4

使用 delete() キーによってマップからアイテムを削除するメソッド。 このメソッドはブール値を返します—true アイテムが存在して削除された場合、および false どのアイテムとも一致しなかった場合。

// Delete an item from a Map by key
map.delete('city') // true

これにより、次のマップが作成されます。

Output
Map(3) {"animal" => "otter", "shape" => "triangle", "country" => "Bulgaria"}

最後に、マップからすべての値をクリアすることができます map.clear().

// Empty a Map
map.clear()

これにより、次のようになります。

Output
Map(0) {}

マップのキー、値、およびエントリ

オブジェクトは、のプロパティを使用して、キー、値、およびエントリを取得できます。 Object コンストラクタ。 一方、マップには、Mapインスタンスのキー、値、およびエントリを直接取得できるプロトタイプメソッドがあります。

The keys(), values()、 と entries() すべてのメソッドは MapIterator、これは、使用できるという点で配列に似ています for...of 値をループします。

これらのメソッドを示すために使用できるマップの別の例を次に示します。

const map = new Map([
  [1970, 'bell bottoms'],
  [1980, 'leg warmers'],
  [1990, 'flannel'],
])

The keys() メソッドはキーを返します:

map.keys()
Output
MapIterator {1970, 1980, 1990}

The values() メソッドは値を返します:

map.values()
Output
MapIterator {"bell bottoms", "leg warmers", "flannel"}

The entries() メソッドは、キーと値のペアの配列を返します。

map.entries()
Output
MapIterator {1970 => "bell bottoms", 1980 => "leg warmers", 1990 => "flannel"}

マップによる反復

マップには組み込みがあります forEach 組み込みの反復のための、配列に似たメソッド。 ただし、反復する内容には少し違いがあります。 マップのコールバック forEach を繰り返します value, key、 と map それ自体、アレイバージョンは item, index、 と array 自体。

// Map 
Map.prototype.forEach((value, key, map) = () => {})

// Array
Array.prototype.forEach((item, index, array) = () => {})

オブジェクトはで変換する必要があるため、これはオブジェクトよりもマップの大きな利点です。 keys(), values()、 また entries()、およびオブジェクトを変換せずにオブジェクトのプロパティを取得する簡単な方法はありません。

これを示すために、マップを繰り返し処理して、キーと値のペアをコンソールに記録しましょう。

// Log the keys and values of the Map with forEach
map.forEach((value, key) => {
  console.log(`${key}: ${value}`)
})

これにより、次のようになります。

Output
1970: bell bottoms 1980: leg warmers 1990: flannel

以来 for...of ループはMapやArrayなどの反復可能オブジェクトを反復処理します。マップ項目の配列を破棄することで、まったく同じ結果を得ることができます。

// Destructure the key and value out of the Map item
for (const [key, value] of map) {
  // Log the keys and values of the Map with for...of
  console.log(`${key}: ${value}`)
}

マップのプロパティとメソッド

次の表に、クイックリファレンス用のマッププロパティとメソッドのリストを示します。

プロパティ/メソッド 説明 戻り値
set(key, value) キーと値のペアをマップに追加します Map 物体
delete(key) キーごとのマップからキーと値のペアを削除します ブール値
get(key) キーで値を返します 価値
has(key) キーによるマップ内の要素の存在をチェックします ブール値
clear() マップからすべてのアイテムを削除します 該当なし
keys() マップ内のすべてのキーを返します MapIterator 物体
values() マップ内のすべての値を返します MapIterator 物体
entries() マップ内のすべてのキーと値を次のように返します [key, value] MapIterator 物体
forEach() マップを挿入順に繰り返します 該当なし
size マップ内のアイテムの数を返します 番号

マップを使用する場合

要約すると、マップはキーと値のペアを保持するという点でオブジェクトに似ていますが、マップにはオブジェクトに比べていくつかの利点があります。

  • サイズ-マップには size 一方、オブジェクトにはサイズを取得するための組み込みの方法がありません。
  • 反復-マップは直接反復可能ですが、オブジェクトはそうではありません。
  • 柔軟性-マップは値のキーとして任意のデータ型(プリミティブまたはオブジェクト)を持つことができますが、オブジェクトは文字列のみを持つことができます。
  • Ordered -マップは挿入順序を保持しますが、オブジェクトには保証された順序がありません。

これらの要因により、マップは考慮すべき強力なデータ構造です。 ただし、オブジェクトにはいくつかの重要な利点もあります。

  • JSON-オブジェクトは問題なく動作します JSON.parse()JSON.stringify() JSON を操作するための2つの重要な関数、多くのRESTAPIが処理する一般的なデータ形式。
  • 単一の要素の操作-オブジェクト内の既知の値を操作すると、マップなどのメソッドを使用せずに、キーを使用してオブジェクトに直接アクセスできます。 get().

このリストは、マップまたはオブジェクトがユースケースに適したデータ構造であるかどうかを判断するのに役立ちます。

セットする

セットは、一意の値のコレクションです。 マップとは異なり、セットはオブジェクトよりも配列に概念的に似ています。これは、値のリストであり、キーと値のペアではないためです。 ただし、Setは配列の代わりではなく、複製されたデータを操作するための追加サポートを提供するための補足です。

セットを初期化するには、 new Set() 構文。

const set = new Set()

これにより、空のセットが得られます。

Output
Set(0) {}

アイテムをセットに追加することができます add() 方法。 (これは、 set() Mapで使用できるメソッドですが、類似しています。)

// Add items to a Set
set.add('Beethoven')
set.add('Mozart')
set.add('Chopin')

セットには一意の値しか含めることができないため、既存の値を追加しようとしても無視されます。

set.add('Chopin') // Set will still contain 3 unique values

:マップキーに適用されるのと同じ同等性の比較がセットアイテムに適用されます。 同じ値を持つが同じ参照を共有しない2つのオブジェクトは、等しいとは見なされません。

値の配列を使用してセットを初期化することもできます。 配列に重複する値がある場合、それらはセットから削除されます。

// Initialize a Set from an Array
const set = new Set(['Beethoven', 'Mozart', 'Chopin', 'Chopin'])
Output
Set(3) {"Beethoven", "Mozart", "Chopin"}

逆に、セットは1行のコードで配列に変換できます。

const arr = [...set]
Output
(3) ["Beethoven", "Mozart", "Chopin"]

Setには、Mapと同じメソッドとプロパティが多数あります。 delete(), has(), clear()、 と size.

// Delete an item
set.delete('Beethoven') // true

// Check for the existence of an item
set.has('Beethoven') // false

// Clear a Set
set.clear()

// Check the size of a Set
set.size // 0

Setには、キーまたはインデックスによって値にアクセスする方法がないことに注意してください。 Map.get(key) また arr[index].

セットのキー、値、およびエントリ

マップとセットの両方が持っています keys(), values()、 と entries() イテレータを返すメソッド。 ただし、これらのメソッドはそれぞれMapで異なる目的を持っていますが、Setにはキーがないため、キーは値のエイリアスです。 この意味は keys()values() 両方とも同じイテレータを返し、 entries() 値を2回返します。 使用するのが最も理にかなっています values() 他の2つの方法は、Mapとの一貫性と相互互換性のために存在するため、Setを使用します。

const set = new Set([1, 2, 3])
// Get the values of a set
set.values()
Output
SetIterator {1, 2, 3}

セットによる反復

マップと同様に、セットには組み込みがあります forEach() 方法。 セットにはキーがないため、 forEach() コールバックは同じ値を返すため、Mapとの互換性以外にユースケースはありません。 のパラメータ forEach() それは (value, key, set).

両方 forEach()for...of セットで使用できます。 まず、見てみましょう forEach() 反復:

const set = new Set(['hi', 'hello', 'good day'])

// Iterate a Set with forEach
set.forEach((value) => console.log(value))

次に、 for...of バージョン:

// Iterate a Set with for...of
for (const value of set) {  
    console.log(value);
}

これらの戦略は両方とも、次のようになります。

Output
hi hello good day

プロパティとメソッドを設定する

次の表に、クイックリファレンス用のSetプロパティとメソッドのリストを示します。

プロパティ/メソッド 説明 戻り値
add(value) セットに新しいアイテムを追加します Set 物体
delete(value) 指定したアイテムをセットから削除します ブール値
has() セット内のアイテムの存在を確認します ブール値
clear() セットからすべてのアイテムを削除します 該当なし
keys() セット内のすべての値を返します(と同じ values()) SetIterator 物体
values() セット内のすべての値を返します(と同じ keys()) SetIterator 物体
entries() セット内のすべての値を次のように返します [value, value] SetIterator 物体
forEach() セットを挿入順に繰り返します 該当なし
size セット内のアイテムの数を返します 番号

セットを使用する場合

Setは、JavaScriptツールキットへの便利な追加機能であり、特にデータ内の重複する値を操作する場合に役立ちます。

1行で、重複する値を持つ配列から重複する値なしで新しい配列を作成できます。

const uniqueArray = [ ...new Set([1, 1, 2, 2, 2, 3])] // (3) [1, 2, 3]

これにより、次のようになります。

Output
(3) [1, 2, 3]

セットは、2つのデータセット間の和集合、共通部分、および差を見つけるために使用できます。 ただし、配列は、データの追加操作のためにセットよりも大きな利点があります。 sort(), map(), filter()、 と reduce() メソッド、およびとの直接の互換性 JSON メソッド。

結論

この記事では、マップは順序付けられたキーと値のペアのコレクションであり、セットは一意の値のコレクションであることを学びました。 これらのデータ構造は両方ともJavaScriptに追加機能を追加し、キーと値のペアのコレクションの長さの検索やデータセットからの重複アイテムの削除などの一般的なタスクをそれぞれ簡素化します。 一方、オブジェクトと配列は、JavaScriptでのデータの保存と操作に従来から使用されており、JSONと直接互換性があるため、特にRESTAPIを使用する場合に最も重要なデータ構造になり続けています。 マップとセットは、主にオブジェクトと配列のデータ構造をサポートするのに役立ちます。

JavaScriptの詳細については、ホームページで JavaScriptシリーズのコーディング方法を確認するか、Node.jsシリーズのコーディング方法で記事をご覧ください。 -開発を終了します。