位元詩人 現代 [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 也有相似之處,故我們放在同一篇文章中。讀者可相互比較一下。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。