Map 為 ECMAScript 2015 新支援的資料結構,類似 Object 但功能更強,尤其 Key 可為任何型態包含 Object;若 Key 為 Object 時,由於 ECMAScript 語言特性,將無法簡單使用 get()
取得資料。
Version
macOS Catalina 10.15.1
VS Code 1.40.1
Quokka 1.0.261
ECMAScript 2015
Map
Primitive
let data = new Map([
[1, 'FP in JavaScript'],
[2, 'RxJS in Action'],
[3, 'Speaking JavaScript']
])
let fn = val => data.get(val)
fn(1) // ?
直接以 array 傳入 Map
的 constructor 建立 map,若 key 為 primitive,可直接以 get()
取得。
Object
let data = new Map([
[{ identity: 'admin', status: 1 }, 0],
[{ identity: 'admin', status: 2 }, 1],
[{ identity: 'member', status: 1 }, 2],
[{ identity: 'member', status: 2 }, 3],
[{ identity: 'all', status: 'default' }, 4]
])
let fn = (identity, status) => data.get({ identity, status })
fn('admin', 2); // ?
fn('admin', 3); // ?
若 map 的 key 為 object,直覺會想用 object literal 湊出 object,然後使用 get()
取值。
會發現因為找不到而回傳 undefined
。
因為 ECMAScript 對於 object 的比較是 reference,所以新湊的 object 的 reference 一定與 data
內 object 不相同,因此一定找不到而回傳 undefined
。
let data = new Map([
[{ identity: 'admin', status: 1 }, 0],
[{ identity: 'admin', status: 2 }, 1],
[{ identity: 'member', status: 1 }, 2],
[{ identity: 'member', status: 2 }, 3],
[{ identity: 'all', status: 'default' }, 4]
]);
let fn = (identity, status) => [...data]
.filter(([k, _]) => k.identity === identity && k.status === status)
.map(([_, v]) => v)
.reduce((_, x) => x, 0)
fn('admin', 2); // ?
fn('admin', 3); // ?
使用 ES6 的 spread operator 將 map 展開再形成 array,透過其 filter()
語法針對 object 的 value 做比較。
唯 map 轉成 array 後為兩層 array,須再使用 array destructuring [k, v]
分解 key / value,map()
只取 value 後仍然是 array,最後使用 reduce()
取得單一 value。
Conclusion
- 當 map 的 key 為 object 時,無法簡單以
get()
取得 value,必須將 map 轉成 array,透過 array 的filter()
、map()
與reduce()
合作才能取得 value