現代 JavaScript 程式設計教學:使用映射 (Map) 和集合 (Set)

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    映射 (map) 和集合 (set) 是 ES6 後新增的容器物件。映射是用來儲存以鍵/值對 (key-value pair) 為單位的非線性容器。集合的概念源自於數學上的集合論,用來表示獨特的 (unique) 資料存在的關係。

    為什麼會把映射和集合放在一起講呢?因為映射和集合在概念上相當接近。我們可以把集合想成不限定鍵的型別,但值固定為布林的映射。甚至,我們可以用映射當基底資料結構,自行實作集合容器。

    在 ES5 (含) 之前的年代,會用物件實字 (object literal) 來模擬映射和集合的功能;雖然現在仍然可以繼續使用物件實字來模擬映射和集合,能夠使用映射和集合物件時,應優先使用新的容器。

    使用映射 (Map)

    建立映射物件

    Map 建構函式可建立新的映射物件:

    let map = new Map([
            ["one", "eins"],
            ["two", "zwei"],
            ["three", "drei"]
        ]);
    

    這時候,我們會以二維陣列做為 Map 建構函式的參數,可以一次建立所需的鍵/值對。

    也可以先建立空映射物件後再逐一增添鍵/值對:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    

    存取映射中的元素

    我先在上一節看過了,使用 Map 物件的 set 函式可以儲存新的鍵值對。之後再以 get 函式藉由鍵取得值。參考下例:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    
    assert(map.get("two") === "zwei", "It should be zwei");
    
    // Map returns `undefined` when the key-value pair doesn't exist.
    assert(typeof map.get("four") === "undefined", "It should be undefined");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    映射的演算方式是以鍵取值,但不能以值取鍵。在儲存鍵值對時,鍵當成運算內部索引的資料,實際儲存起來的是內部索引和值。因此,只能進行單向取值。

    走訪映射

    走訪映射的方式是以 for 迴圈搭配 of 修飾字。我們可以根據自身的需求,以鍵、值、鍵值對等方式來走訪。

    藉由 keys() 函式可提取出映射的鍵。搭配 for 迴圈即可走訪所有的元素。參考下例:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    
    for (let k of map.keys()) {
        console.log(`${k} -> ${map.get(k)}`);
    }
    

    如果我們只需要值的部分,可以改用 values() 函式來走訪映射。參考下例:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    
    for (let v of map.values()) {
        console.log(v);
    }
    

    如果我們鍵、值都要,除了先前的方式,也可以用 entries() 函式將整個鍵/值對同時取出。參考下例:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    
    for (let e of map.entries()) {
        console.log(`${e[0]} -> ${e[1]}`);
    }
    

    取出的 entry 是一個包含兩個元素的陣列,分別用索引值即可取出鍵、值。由於陣列取索引的運算會比映射取索引的開銷小,適度使用 entries() 會比只用 keys() 來得好。

    移除映射中的元素

    使用 delete() 函式可移除映射中已存在的鍵值對。參考下例:

    let map = new Map();
    
    map.set("one", "eins");
    map.set("two", "zwei");
    map.set("three", "drei");
    
    assert(map.get("two") === "zwei", "It should be zwei");
    
    // Delete a key-value pair.
    map.delete("two");
    
    // Map returns `undefined` when the key-value pair doesn't exist.
    assert(typeof map.get("two") === "undefined", "It should be undefined");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    移除鍵值對後,若再存取鍵值對,會回傳 undefined

    使用集合 (Set)

    建立集合物件

    使用 Set() 建構函式即可建立集合物件:

    let set = new Set([1, 1, 2, 3, 3, 4, 4, 4, 4, 5, 5]);
    
    assert(set.size === 5, "The size should be 5");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    在建立集合物件時,會自動去除重覆的元素。以本例來說,其物件長度為 5

    也可以先建立空的集合物件後再逐一增加元素:

    let set = new Set();
    
    set.add(1);
    
    set.add(2);
    set.add(2);
    
    set.add(3);
    set.add(3);
    set.add(3);
    
    set.add(1);
    
    assert(set.size === 3, "The size should be 3");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    確認集合的元素存在

    使用 has() 函式可確認集合中的元素是否存在。參考下例:

    let set = new Set([1, 2, 3, 4, 5]);
    
    assert(set.has(1), "1 should exist");
    assert(!set.has(6), "6 should not exist");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    走訪集合

    由於集合只有值,沒有鍵,故使用 values() 函式搭配 for 迴圈來走訪集合。參考下例:

    let set = new Set([1, 2, 3, 4, 5]);
    
    for (let v of set.values()) {
        console.log(v);
    }
    

    移除集合中的元素

    使用 delete() 函式可移除集合中的元素。參考下例:

    let set = new Set([1, 2, 3, 4, 5]);
    
    assert(set.has(2), "2 should exist");
    
    // Delete one element from the set.
    set.delete(2);
    
    assert(!set.has(2), "2 should not exist");
    
    // Home-made assertion.
    function assert(cond, msg) {
        if (!(cond)) {
            throw msg;
        }
    }
    

    結語

    透過本文,相信讀者可以學會映射和集合的用法。這兩種容器不僅在概念上相近,在 API 也有相似之處,故我們放在同一篇文章中。讀者可相互比較一下。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk