MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - Mapのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - Map
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == JavaScriptのMapは、ES2015 (ES6) で導入されたキーバリューペアのコレクションである。<br> 通常のObjectと異なり、キーとして文字列やSymbolだけでなく、オブジェクト、関数、数値等、任意の型を使用できる。<br> <br> キーの比較にはSameValueZeroアルゴリズムを採用しており、<code>NaN</code> 同士を同一のキーとして扱うことができる。<br> 挿入した順序が仕様によって保証されており、反復処理においても挿入順に要素が返される。<br> <br> <code>size</code> プロパティで要素数を直接取得できるほか、<code>forEach</code> や <code>for...of</code> による反復処理、<br> <code>keys()</code> / <code>values()</code> / <code>entries()</code> によるイテレータ取得等、豊富な操作メソッドが用意されている。<br> <br> ES2024では静的メソッド <code>Map.groupBy()</code> が追加され、配列等のイテラブルをグループ化してMapとして返す操作が標準化された。<br> <br> また、弱参照版である <code>WeakMap</code> も用意されており、キーへの外部参照がなくなった際にガベージコレクタが自動的にエントリを削除するため、<br> メモリリークを防ぎつつプライベートデータの格納やDOM要素へのメタデータ付与に活用できる。<br> <br><br> == Mapの作成 == Mapを作成する方法は主にコンストラクタを使用する1種類である。<br> <br> ES2024からは静的メソッド <code>Map.groupBy()</code> によるグループ化も可能になった。<br> <br> ==== コンストラクタ ==== <code>new Map()</code> で空のMapを生成し、<code>new Map(iterable)</code> で初期値を持つMapを生成する。<br> <code>new</code> を省略すると TypeErrorが発生するため注意が必要である。<br> <br> iterableを渡す場合、各要素は <code>[key, value]</code> の形式でなければならない。<br> <br> <syntaxhighlight lang="javascript"> // 空のMapを生成 const map1 = new Map(); // 初期値を持つMapを生成 ([key, value] のペアの配列を渡す) const map2 = new Map([ ["name", "Alice"], ["age", 30], ["city", "Tokyo"] ]); console.log(map2.get("name")); // "Alice" console.log(map2.size); // 3 // Objectからの変換 const obj = { a: 1, b: 2, c: 3 }; const map3 = new Map(Object.entries(obj)); console.log(map3.get("a")); // 1 // newなしではTypeError // const map4 = Map(); // TypeError: Constructor Map requires 'new' </syntaxhighlight> <br> ==== Map.groupBy (ES2024) ==== <code>Map.groupBy(items, callback)</code> は、イテラブルの各要素にコールバック関数を適用し、その戻り値をキー、対応する要素の配列を値とするMapを返す静的メソッドである。<br> 2024年3月以降の全モダンブラウザで利用可能である。<br> <br> コールバック関数の戻り値はキーとして使用されるため、オブジェクトを含む任意の型を返すことができる点が <code>Object.groupBy()</code> との違いである。<br> <code>Object.groupBy()</code> はキーが文字列に変換されるのに対し、<code>Map.groupBy()</code> はキーの型をそのまま保持する。<br> <br> <syntaxhighlight lang="javascript"> const inventory = [ { name: "apple", type: "fruit", count: 5 }, { name: "banana", type: "fruit", count: 0 }, { name: "carrot", type: "vegetable", count: 2 }, { name: "daikon", type: "vegetable", count: 10 }, ]; // 文字列キーでグループ化 const byType = Map.groupBy(inventory, (item) => item.type); console.log(byType.get("fruit")); // [ { name: "apple", ... }, { name: "banana", ... } ] // オブジェクトをキーとしてグループ化 (Map.groupByのみ可能) const restock = { restock: true }; const sufficient = { restock: false }; const byRestock = Map.groupBy(inventory, (item) => item.count > 0 ? sufficient : restock ); console.log(byRestock.get(restock)); // [ { name: "banana", ... } ] // Object.groupByとの比較 (キーは文字列に変換される) const objGrouped = Object.groupBy(inventory, (item) => item.type); console.log(objGrouped.fruit); // Object.groupBy はドットアクセスで取得 </syntaxhighlight> <br><br> == 要素の操作 == Mapには要素の追加、取得、確認、削除のためのメソッドが用意されている。<br> <br> ==== set / get ==== <code>set(key, value)</code> はMapにキーバリューペアを追加または更新するメソッドである。<br> Mapインスタンス自身を返すため、メソッドチェーンで連続して呼び出すことができる。<br> <br> <code>get(key)</code> は指定したキーに対応する値を返し、キーが存在しない場合は <u>undefined</u> を返す。<br> <br> <syntaxhighlight lang="javascript"> const map = new Map(); // set : キーバリューペアを追加 map.set("name", "Alice"); map.set("age", 30); // setはチェーン可能 (Mapインスタンス自身を返す) map.set("city", "Tokyo").set("country", "Japan"); // get : 値を取得 console.log(map.get("name")); // "Alice" console.log(map.get("age")); // 30 // 存在しないキーはundefinedを返す console.log(map.get("email")); // undefined // キーを更新する map.set("age", 31); console.log(map.get("age")); // 31 </syntaxhighlight> <br> ==== has / delete / clear ==== 各メソッドの動作を以下に示す。<br> <br> <center> {| class="wikitable" |+ has / delete / clearメソッドの動作 ! メソッド !! 説明 |- | <code>has(key)</code> || 指定したキーがMapに存在するかを真偽値で返す。 |- | <code>delete(key)</code> || 指定したキーとその値をMapから削除する。<br>削除に成功した場合はtrue、キーが存在しなかった場合はfalseを返す。 |- | <code>clear()</code> || Map内の全ての要素を削除する。<br>戻り値は、<u>undefined</u> である。 |} </center> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["a", 1], ["b", 2], ["c", 3], ]); // has: キーの存在確認 console.log(map.has("a")); // true console.log(map.has("z")); // false // delete: 特定のキーを削除 console.log(map.delete("b")); // true (削除成功) console.log(map.delete("z")); // false (存在しないキー) console.log(map.size); // 2 // clear: 全要素を削除 map.clear(); console.log(map.size); // 0 </syntaxhighlight> <br> ==== sizeプロパティ ==== <code>size</code> は読み取り専用のプロパティで、Mapに格納されているキーバリューペアの数を返す。<br> Objectのサイズ取得には <u>Object.keys(obj).length</u> が必要であるのに対し、Mapは <u>map.size</u> で直接取得することができる。<br> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["x", 10], ["y", 20], ["z", 30], ]); console.log(map.size); // 3 map.set("w", 40); console.log(map.size); // 4 map.delete("x"); console.log(map.size); // 3 // Objectとの比較 const obj = { x: 10, y: 20, z: 30 }; console.log(Object.keys(obj).length); // 3 (Objectはこの方法が必要) </syntaxhighlight> <br><br> == キーの特性 == Mapのキーはあらゆる型の値を使用できる点が、通常のObjectとの大きな違いである。<br> <br> ==== キーに使用できる型 ==== Mapのキーとして使用できる型を以下に示す。<br> <br> <center> {| class="wikitable" |+ Mapのキーに使用できる型 ! 種別 ! 内容 |- | プリミティブ型 || 文字列、数値、真偽値、<u>undefined</u>、<u>null</u>、Symbol、BigInt |- | オブジェクト || 配列、関数、クラスインスタンス等、参照型全般 |- | <code>NaN</code> || <u>NaN</u> 自体もキーとして使用できる。 |} </center> <br> <syntaxhighlight lang="javascript"> const map = new Map(); // プリミティブ型のキー map.set("string", "文字列キー"); map.set(42, "数値キー"); map.set(true, "真偽値キー"); map.set(null, "nullキー"); map.set(undefined, "undefinedキー"); map.set(Symbol("s"), "Symbolキー"); // オブジェクトをキーとして使用 const obj = { id: 1 }; const arr = [1, 2, 3]; const func = () => {}; map.set(obj, "オブジェクトキー"); map.set(arr, "配列キー"); map.set(func, "関数キー"); // NaN をキーとして使用 map.set(NaN, "NaNキー"); console.log(map.get(NaN)); // "NaNキー" console.log(map.size); // 10 </syntaxhighlight> <br> ==== キーの同値比較 ==== MapのキーはSameValueZeroアルゴリズムで比較される。<br> このアルゴリズムは厳密等価 (<code>===</code>) と概ね同じだが、以下の点が異なる。<br> <br> * <code>NaN</code> 同士を同一視する *: 厳密等価では <code>NaN === NaN</code> が <code>false</code> になるが、SameValueZeroでは <code>NaN</code> を同一キーとして扱う。 *: <br> * <code>+0</code> と <code>-0</code> を同一視する *: SameValueアルゴリズムでは <code>+0</code> と <code>-0</code> を区別するが、SameValueZeroでは同一視する。 * その他の値は厳密等価 (<code>===</code>) と同じ比較を行う <br> SameValueZeroが採用された理由は、厳密等価を使用した場合に <code>NaN</code> のキー判定が不可能になり、<br> SameValueを使用した場合に <code>+0</code> と <code>-0</code> が別キーとして扱われ混乱を招くためである。<br> <br> <syntaxhighlight lang="javascript"> const map = new Map(); // NaN 同士は同一視される (厳密等価とは異なる) map.set(NaN, "first"); map.set(NaN, "second"); // 既存のNaNキーを上書き console.log(map.size); // 1 console.log(map.get(NaN)); // "second" // 参考: 厳密等価では NaN は NaN と等しくない console.log(NaN === NaN); // false // +0 と -0 は同一視される map.set(+0, "zero"); map.set(-0, "minus zero"); // +0 のエントリを上書き console.log(map.size); // 2 (NaN のエントリ + 0 のエントリ) console.log(map.get(0)); // "minus zero" console.log(map.get(-0)); // "minus zero" // オブジェクトキーは参照で比較される const obj1 = { id: 1 }; const obj2 = { id: 1 }; // 内容は同じだが別オブジェクト map.set(obj1, "obj1"); map.set(obj2, "obj2"); console.log(map.size); // 4 (obj1 と obj2 は別キー) console.log(map.get(obj1)); // "obj1" console.log(map.get(obj2)); // "obj2" </syntaxhighlight> <br><br> == 反復処理 == Mapは挿入順に要素を反復処理するための複数の方法を提供している。<br> <br> ==== forEach ==== <code>forEach(callback)</code> は、Mapの各エントリに対してコールバック関数を呼び出す。<br> コールバック関数の引数は <code>(value, key, map)</code> の順である。<br> <br> <u>Arrayの <code>forEach</code> と異なり、第1引数がvalueであることに注意が必要である。</u><br> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["name", "Alice"], ["age", 30], ["city", "Tokyo"], ]); map.forEach((value, key) => { console.log(`${key}: ${value}`); }); // name: Alice // age: 30 // city: Tokyo // 第3引数でMapインスタンス自体を受け取れる map.forEach((value, key, m) => { console.log(m.size); // 3 (毎回出力) }); </syntaxhighlight> <br> ==== for...of ==== <code>for...of</code> を使用してMapを反復処理できる。<br> MapのデフォルトイテレータはEntries (キーバリューペアの配列) を返すため、<code>for...of</code> でそのまま分割代入が使用できる。<br> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["name", "Alice"], ["age", 30], ["city", "Tokyo"], ]); // デフォルトイテレータは entries() と同等 for (const [key, value] of map) { console.log(`${key}: ${value}`); } // name: Alice // age: 30 // city: Tokyo </syntaxhighlight> <br> ==== keys / values / entries ==== Mapは3種類のイテレータメソッドを提供している。<br> <br> <center> {| class="wikitable" |+ Mapの3種類のイテレータメソッド ! メソッド !! 説明 |- | <code>keys()</code> || 挿入順にキーを返すイテレータを生成する。 |- | <code>values()</code> || 挿入順に値を返すイテレータを生成する。 |- | <code>entries()</code> || 挿入順に <code>[key, value]</code> のペアを返すイテレータを生成する。<br>Mapのデフォルトイテレータと同等 |} </center> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["a", 1], ["b", 2], ["c", 3], ]); // keys() : キーのイテレータ for (const key of map.keys()) { console.log(key); // "a", "b", "c" } // values() : 値のイテレータ for (const value of map.values()) { console.log(value); // 1, 2, 3 } // entries() : [key, value]ペアのイテレータ for (const [key, value] of map.entries()) { console.log(`${key} => ${value}`); // "a => 1", "b => 2", "c => 3" } </syntaxhighlight> <br> ==== 配列への変換 ==== Mapは <code>Array.from()</code> または スプレッド構文 (<code>...</code>) を使用して配列に変換できる。<br> <br> <syntaxhighlight lang="javascript"> const map = new Map([ ["x", 10], ["y", 20], ["z", 30], ]); // Array.from()で変換 const entries1 = Array.from(map); console.log(entries1); // [["x", 10], ["y", 20], ["z", 30]] const keys1 = Array.from(map.keys()); const values1 = Array.from(map.values()); console.log(keys1); // ["x", "y", "z"] console.log(values1); // [10, 20, 30] // スプレッド構文で変換 const entries2 = [...map]; const keys2 = [...map.keys()]; const values2 = [...map.values()]; console.log(entries2); // [["x", 10], ["y", 20], ["z", 30]] // Mapにfilterやmap等の配列メソッドを適用する場合 const filtered = [...map].filter(([key, value]) => value > 10); console.log(filtered); // [["y", 20], ["z", 30]] </syntaxhighlight> <br><br> == Object との比較 == MapとObjectはどちらもキーバリューペアを格納できるが、特性や用途に違いがある。<br> <br> <center> {| class="wikitable" |+ Map と Object の比較 ! 観点 !! Map !! Object |- | キーの型 || 任意の型 (オブジェクト、関数、NaN等を含む) || 文字列、Symbol のみ |- | 挿入順の保持 || 仕様で保証 || 実装依存 (整数キーは昇順、その他は挿入順が多い) |- | サイズ取得 || <code>map.size</code> で直接取得 || <code>Object.keys(obj).length</code> が必要 |- | 反復処理 || <code>for...of</code>、<code>forEach</code>、各種イテレータが利用可能 || <code>for...in</code>、<code>Object.keys()</code> 等が必要 |- | パフォーマンス || 追加・削除・反復処理が高速 (特に大規模データ) || 小規模データの読み取り中心では有利な場合がある。 |- | JSON変換 || 非対応 (カスタム変換が必要) || <code>JSON.stringify()</code> / <code>JSON.parse()</code> で直接対応 |- | プロトタイプ || なし (意図しないキーの衝突リスクが低い) || <code>Object.prototype</code> を継承 (意図しないキー衝突の可能性あり) |} </center> <br> ==== 使い分けの指針 ==== <center> {| class="wikitable" |+ MapとObjectの使い分け ! 型 !! 場面 !! 理由 |- | rowspan="4" | Map || キーの追加・削除が頻繁に発生する場合 || Mapはdeleteが高速であり、大量のエントリを扱う場合も効率的である。 |- | キーとして文字列・Symbol以外を使用したい場合 || オブジェクトや関数、NaN等をキーとして使用する場合はMapが必要である。 |- | 挿入順を確実に保持する必要がある場合 || Objectの挿入順保持はブラウザ依存であるため、仕様で保証されているMapを使用する。 |- | 大規模データの反復処理が必要な場合 || Mapの反復処理はObjectより高速な場合があり、特に大規模データで顕著である。 |- | rowspan="3" | Object || JSONとの相互変換が必要な場合 || <code>JSON.stringify()</code> / <code>JSON.parse()</code> はObjectを直接サポートする。 |- | 構造が固定されたレコードを扱う場合 || 固定フィールドを持つデータ構造にはObjectの方が直感的で読みやすい。 |- | 小規模でキーが文字列の場合 || シンプルなキーバリューペアであればObjectの方が記述が簡潔になる場合がある。 |} </center> <br><br> == WeakMap == WeakMapは、キーを弱参照で保持するMap類似のコレクションである。<br> <br> ==== WeakMapとは ==== WeakMapはMapと異なり、キーをガベージコレクタが回収できる弱参照で保持する。<br> キーへの外部参照がなくなると、ガベージコレクタが自動的に対応するエントリを削除するため、メモリリークを防ぐことができる。<br> <br> WeakMapには以下の制限がある。<br> <br> * キーはオブジェクトまたは未登録のSymbolのみ使用できる *: プリミティブ型 (文字列、数値等) はキーとして使用できない。 *: <br> * 反復処理ができない *: <code>keys()</code>、<code>values()</code>、<code>entries()</code>、<code>forEach()</code> は存在しない。 *: <br> * <code>size</code> プロパティがない *: 格納されている要素数を取得する方法がない。 *: <br> * <code>clear()</code> メソッドがない *: 全要素を一括削除する方法がない。 <br> ==== 基本操作 ==== WeakMapで使用できるメソッドは <code>set</code>、<code>get</code>、<code>has</code>、<code>delete</code> の4つのみである。<br> <br> <syntaxhighlight lang="javascript"> const weakMap = new WeakMap(); const key1 = { id: 1 }; const key2 = { id: 2 }; // set : エントリを追加 weakMap.set(key1, "value1"); weakMap.set(key2, "value2"); // get : 値を取得 console.log(weakMap.get(key1)); // "value1" console.log(weakMap.get(key2)); // "value2" // has : キーの存在確認 console.log(weakMap.has(key1)); // true // delete : エントリを削除 weakMap.delete(key1); console.log(weakMap.has(key1)); // false // プリミティブ型はキーとして使用不可 // weakMap.set("string", "value"); // TypeError // weakMap.set(42, "value"); // TypeError </syntaxhighlight> <br> ==== 使用例 ==== WeakMapは主に以下の2つのパターンで活用される。<br> <br> ===== プライベートデータの格納 ===== WeakMapを使うことで、クラスインスタンスに紐づいたプライベートデータを外部から隠蔽できる。<br> ES2022以降は <code>#</code> プライベートフィールドが推奨されるが、WeakMapパターンは依然として有用な場面がある。<br> <br> <syntaxhighlight lang="javascript"> // WeakMap を使ったプライベートデータパターン const privateData = new WeakMap(); class Person { constructor(name, age) { privateData.set(this, { name, age }); } getName() { return privateData.get(this).name; } getAge() { return privateData.get(this).age; } greet() { const { name, age } = privateData.get(this); return `Hello, I'm ${name}, ${age} years old.`; } } const alice = new Person("Alice", 30); console.log(alice.getName()); // "Alice" console.log(alice.greet()); // "Hello, I'm Alice, 30 years old." // privateData は外部からアクセスできない // alice インスタンスが参照されなくなると、WeakMap のエントリも自動削除される </syntaxhighlight> <br> ===== DOM要素へのメタデータ付与 ===== WeakMapを使用して、DOM要素にメタデータを安全に付与できる。<br> DOM要素がドキュメントから削除されると、WeakMapのエントリも自動的にガベージコレクトされるため、メモリリークが発生しない。<br> <br> <syntaxhighlight lang="javascript"> const elementMetadata = new WeakMap(); function registerElement(element, metadata) { elementMetadata.set(element, metadata); } function getMetadata(element) { return elementMetadata.get(element); } // DOM要素にメタデータを付与 const button = document.querySelector("#myButton"); registerElement(button, { clickCount: 0, createdAt: Date.now(), label: "送信ボタン", }); button.addEventListener("click", () => { const meta = getMetadata(button); meta.clickCount++; console.log(`クリック回数: ${meta.clickCount}`); }); // button 要素が DOM から削除されると、WeakMap のエントリも自動削除される // button.remove(); </syntaxhighlight> <br> 以下に、MapとWeakMapの主な違いをまとめる。<br> <br> <center> {| class="wikitable" |+ Map と WeakMapの比較 ! 観点 !! Map !! WeakMap |- | キーの型 || 任意の型 || オブジェクト、未登録Symbol のみ |- | 反復処理 || 可能 (forEach, for...of 等) || 不可能 |- | size プロパティ || あり || なし |- | clear メソッド || あり || なし |- | GC連動 || なし (強参照) || あり (弱参照) |- | 主な用途 || 一般的なキーバリューストア || プライベートデータ、DOM メタデータ、キャッシュ |} </center> <br><br> == 関連情報 == * [[JavaScriptの基礎 - Set]] *: Set、WeakSet、ES2025集合演算 * [[JavaScriptの基礎 - イテレータとジェネレータ]] *: イテレータプロトコル、ジェネレータ、ES2025 Iterator Helpers * [[JavaScriptの基礎 - オブジェクトリテラル]] *: オブジェクトの基本操作、プロパティアクセス * [[JavaScriptの基礎 - 配列の反復メソッド]] *: forEach, map, filter, reduce等の反復処理メソッド <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - Map
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse