著者は、 Open Internet / Free Speech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

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

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

マップ

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

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

const map = new Map()

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

Output
Map(0) {}

マップへの値の追加

set()メソッドを使用してマップに値を追加できます。 最初の引数がキーになり、2番目の引数が値になります。

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

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?')

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

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"}

2番目の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インスタンスのキー、値、およびエントリを直接取得できるプロトタイプメソッドがあります。

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

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

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

keys()メソッドは次のキーを返します。

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

values()メソッドは次の値を返します。

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

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

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

マップによる反復

マップには、組み込みの反復のために、配列と同様の組み込みのforEachメソッドがあります。 ただし、反復する内容には少し違いがあります。 マップのforEachのコールバックは、valuekey、および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などの反復可能オブジェクトを反復処理するため、Mapアイテムの配列を破棄しても、まったく同じ結果を得ることができます。

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

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

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

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

セットする

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

セットはnew Set()構文で初期化できます。

const set = new Set()

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

Output
Set(0) {}

add()方式でセットにアイテムを追加できます。 (これは、Mapで使用可能なset()メソッドと混同しないでください。ただし、これらは類似しています。)

// 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には、delete()has()clear()sizeなど、Mapと同じメソッドとプロパティが多数あります。

// 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]のように、キーまたはインデックスによって値にアクセスする方法がないことに注意してください。

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

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

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

セットによる反復

Mapと同様に、SetにはforEach()メソッドが組み込まれています。 セットにはキーがないため、forEach()コールバックの最初と2番目のパラメーターは同じ値を返します。したがって、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シリーズのコーディング方法で記事をご覧ください。 -開発を終了します。