Map 的特性
JavaScript 的建構對象 Map 是一種資料結構,相較於 Array 與 Object 來說是比較不常見,是 JavaScript ES6 中新增的資料結構。
MDN 文件中提到,Map 是保存了鍵值對(key-value pairs)的物件。任何值(包括物件及基本型別(primitive)值 (en-US))都可以作為鍵或值。
在 database 的資料庫中 Collections 或是 Schema 中,基本所對應的資料單位,也就是 鍵(key): 值(value),在 JavaScript 裡的 Map() 是用來改善早期只能使用 Array 資料結構的問題,增加在資料結構上的針對性。
Map 與 object 的比較
Map 類似於 object (一般的物件資料格式),但在二者之中還是有著不同的地方:
- Map 的 key 是可以是原始型別、物件、函式,而 object 的 key 限定為 string、symbol
- Map 是 array-like (類陣列),可使用陣列方法,例如 forEach、for in 等等…
- Map 的 key 在被加入時有時間順序性,Object 則沒有順序。
- 高度操作新增與刪除屬性時,Map 的效能比 Object 好。
ES6 Map() 與 Set() 資料結構
ES6 增加了 Set() 跟 Map() 語法,讓 javaScript 可以用更簡潔的語法操作資料結構。
Set() 關心的是元素 { value1, value2 }, Map() 關心的是 { 鍵(key): 值(value)} 之間的關係。
透過 Map() 所建構出來的實體,可以使用 Set() 的實加入資料結構中,加入後可以使用 Map() 原型下的方法,進行資料的操作與使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // ES6 Map let myMap= new Map(); var keyString = 'I am string', keyObj = {}, keyFunc = function() {}, keyNumber = 1 // 增加 myMap.set(keyString , 'string value'); myMap.set(keyObj, {obj: 1}); myMap.set(keyFunc , function(){console.log('I am function')}); myMap.set(keyNumber , 100); // 有幾個 myMap.size; // 4 // 取值 myMap.get(keyObj); // {obj: 1} // 看是否存在 myMap.has(keyString ); // true // 刪掉 myMap.delete(keyNumber); myMap.size; // 3 // 轉陣列 [...myMap.values()] // ["string value", {obj: 1}, ƒ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | const data = [ [1, { name: 'Jimmy', email: 'jimmy@test.com' }], [2, { name: 'Jed', email: 'jed@test.com' }], [3, { name: 'Frank', email: 'frank@test.com' }] ] const map = new Map(data) // 取得 key-value pairs 的個數 console.log(map.size) // 3 // 某個 key-value pair 是否存在 const isFirstUserExist = map.has(1) const isForthUserExist = map.has(4) console.log(isFirstUserExist) // true console.log(isForthUserExist) // false // 用 key 取得 value const firstUser = map.get(1) console.log(firstUser) // { name: 'Jimmy', email: 'jimmy@test.com' } // 新增、更新 key-value pairs map.set(4, { name: 'John', email: 'james@test.com' }) console.log(map.get(4)) // { name: 'John', email: 'john@test.com' } // 刪除 key-value pairs map.delete(4) console.log(map) /* Map(3) { 1 => { name: 'Jimmy', email: 'jimmy@test.com' }, 2 => { name: 'Jed', email: 'jed@test.com' }, 3 => { name: 'Frank', email: 'frank@test.com' } } */ // 清空 Map map.clear() console.log(map) // Map(0) {} |
Map 比較 Array 的資料結構操作上的方便的地方
Map 特別之處標籤 (key),不像是 object 在取用 value 時,所用屬性命名一定只能只用 String,在 Map 來說可以接受 Number、Array、Object 或其他任何型別。
而在 Array 來說,每個對應的結構裡就是以 index 的索引位置來取用 Value,如果分成二筆 Array 資料分別記項目名稱與項目數是,就會下面面的程式碼。
1 2 3 4 | // array 需要兩個陣列來記錄 fruitsArr = ['watermelon', 'grape', 'avocado'] // store friut ateCount = [0 , 1, 4] // store how many times eat // 沒有人吃西瓜、葡萄被吃了一個、酪梨被吃 4 個 |
找項目名稱 (水果名) 時會先需要去 fruitsArr 變數,透過迴圈找到對應的名稱出來取得索引位置的數值,在取用時 +1 取用名稱再向下個變數 ateCount 另外一個 Array 進行二層的 loop 取得數是,這樣的資料結構大大的增加複雜度。
但如果是使用 Map 資料結構的話,就點單多了。
1 2 3 4 5 6 7 8 9 10 11 12 13 | let fruitsMap = new Map( [ ['watermelon',0], ['grape', 1], ['avocado', 4] ] ); fruitsMap.get("watermelon") // 0 fruitsMap.get("grape") // 1 fruitsMap.get("avocado") // 4 |
將 fruitsMap 透過 new Map() 建構出來的實體,透過 .get() 方法直接在每個陣列資料向 Key 以字串名稱取出對應的值出來,就可直接取出毎個項目陣列資料裡的值。
Map() 的疊代
在 JavaScript 的疊代 (JavaScript 疊代一文) 中有說明到,Map 的資料結構是可以透過 new Map() 進行建構來宣告成變數使用。
Map Iteration:
- Map.keys():回傳 map 所有的 key
- Map.values():回傳 map 所有的 value
- Map.entires():回傳 map 每個元素的 [key, value] 的鍵值對
1 2 3 4 5 6 7 | let map = new Map([ ["amber", 18], ["shane", 33], ]); map.keys() // {'amber', 'shane'} map.values() // {18, 33} map.entires() // {"amber" => 18, "shane" => 33} |
1 2 3 4 5 6 7 | //for of for(let [key, value] of map){ console.log('key', key, 'value', value) }//forEach map.forEach((value, key) => { console.log('key', key, 'value', value) }) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | const data = [ [1, { name: 'Jimmy', email: 'jimmy@test.com' }], [2, { name: 'Jed', email: 'jed@test.com' }], [3, { name: 'Frank', email: 'frank@test.com' }] ] const map = new Map(data) // 方法一:for...of for (const [key, value] of map) { console.log(`${key} = ${JSON.stringify(value)}`); } // 方法二:forEach (注意 key, value 的 argument 順序) map.forEach((value, key) => { console.log(`${key} = ${JSON.stringify(value)}`); }); // 方法三:for..of 搭配 entries for (const [key, value] of map.entries()) { console.log(`${key} = {value}`); } /* 以上結果都是: 1 = {"name":"Jimmy","email":"jimmy@test.com"} 2 = {"name":"Jed","email":"jed@test.com"} 3 = {"name":"Frank","email":"frank@test.com"} */ // 疊代 keys for (const key of map.keys()) { console.log(key); } // 1 // 2 // 3 // 疊代 values for (const value of map.values()) { console.log(JSON.stringify(value)); } // {"name":"Jimmy","email":"jimmy@test.com"} // {"name":"Jed","email":"jed@test.com"} // {"name":"Frank","email":"frank@test.com"} |
參考資料
– medium [資料結構] — Map
– Jimmy 的架站筆記 – JavaScript Map
– ithome 第 11 屆 iThome 鐵人賽 – hannahpun – Map
– MDN – Map