Set和Map

產生起因

JavaScript的默認對象表示方式{}可以視爲其他語言中的MapDictionary的數據結構,即一組鍵值對。

但是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其他數據類型作爲鍵也是非常合理的。爲了解決這個問題,最新的ES6規範引入了新的數據類型Mapset.

Set是一種叫做集合的數據結構,Map是一種叫做字典的數據結構

應用場景

數組去重和數據存儲

Set (集合)[類數組]

  • 集合是由一組無序且唯一(即不能重複)的項組成的,可以想象成集合是一個既沒有重複元素,也沒有順序概念的數組

    var a = new Set([1, 2, 3, {"1": "2"}, ["3","4"]])
    
  • ES6提供了新的數據結構Set。它類似於數組,但是成員的值都是唯一的,沒有重複的值

  • Set 本身是一個構造函數,用來生成 Set 數據結構

    這裏說的Set其實就是我們所要講到的集合,先來看下基礎用法

    const s = new Set();
    
    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
    
    for (let i of s) {
      console.log(i);   // 2 3 5 4
    }
    
    // 去除數組的重複成員
    let array = [1,2,1,4,5,3];
    [...new Set(array)]     // [1, 2, 4, 5, 3]
    
    
方法和屬性總結
  • 屬性

    1. add(value):添加某個值,返回 Set 結構本身。

    2. delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。

    3. has(value):返回一個布爾值,表示該值是否爲 Set 的成員。

    4. clear():清除所有成員,無返回值。

    5. size: 返回set數據結構的數據長度

      let set = new Set();
      console.log(set.add(1).add(2)); // Set [ 1, 2 ]
      
      console.log(set.delete(2)); // true
      console.log(set.has(2)); // false
      
      console.log(set.clear()); // undefined
      console.log(set.has(1)); // false
      console.log(set.size);  // 0
      
  • 遍歷方法有

    1. keys():返回鍵名的遍歷器

    2. values():返回鍵值的遍歷器

    3. entries():返回鍵值對的遍歷器

    4. forEach():使用回調函數遍歷每個成員,無返回值

      let set = new Set(['a', 'b', 'c']);
      console.log(set.keys()); // SetIterator {"a", "b", "c"}
      console.log([...set.keys()]); // ["a", "b", "c"]
      
      let set = new Set(['a', 'b', 'c']);
      console.log(set.values()); // SetIterator {"a", "b", "c"}
      console.log([...set.values()]); // ["a", "b", "c"]
      
      let set = new Set(['a', 'b', 'c']);
      console.log(set.entries()); // SetIterator {"a", "b", "c"}
      console.log([...set.entries()]); // [["a", "a"], ["b", "b"], ["c", "c"]]
      
      let set = new Set([1, 2, 3]);
      set.forEach((value, key) => console.log(key + ': ' + value));
      // 1: 1
      // 2: 2
      // 3: 3
      
方法剖析(實現原理)
  • 建一個集合

    function Set(arr = []) {    // 可以傳入數組
        let items = {};
        this.size = 0;  // 記錄集合中成員的數量
    }
    
    module.exports = Set;
    

    這裏用{}對象來表示集合,也是因爲對象不允許一個鍵指向兩個不同的屬性,保證了集合裏的元素都是唯一的

  • has方法

    // has(val)方法
    this.has = function (val) {
      // 對象都有hasOwnProperty方法,判斷是否擁有特定屬性
      return items.hasOwnProperty(val);
    };
    
  • add方法

    // add(val)方法
    this.add = function (val) {
      if (!this.has(val)) {
        items[val] = val;
        this.size++; // 累加集合成員數量
        return true;
      }
      return false;
    };
    
    
  • keys、values方法

    // keys()方法
    this.keys = function () {
      return Object.keys(items); // 返回遍歷集合的所有鍵名的數組
    };
    // values()方法
    this.values = function () {
      return Object.values(items); // 返回遍歷集合的所有鍵值的數組
    };
    
  • forEach方法

    // forEach(fn, context)方法
    this.forEach = function(fn, context = this) {
        for (let i = 0; i < this.size; i++) {
            let item = Object.keys(items)[i];
            fn.call(context, element, sameElement, set);     
        }
    };
    
  • Array.from()

    將Set類數組轉化爲真實的數組

    let arrayLike = {
        0: 'tom', 
        1: '65',
        2: '男',
        3: ['jane','john','Mary'],
        'length': 4
    }
    let arr = Array.from(arrayLike)
    console.log(arr) // ['tom','65','男',['jane','john','Mary']]
    
  • 與Array之間的關係

  • var myArray = ["value1", "value2", "value3"];
    
    // 用Set構造器將Array轉換爲Set
    var mySet = new Set(myArray);
    
    mySet.has("value1"); // returns true
    
    // 用...(展開操作符)操作符將Set轉換爲Array
    console.log([...mySet]); // 與myArray完全一致
    
  • 高級用法

    let set = new Set([2, 1, 3]);
    console.log(set.keys());    // [ '1', '2', '3' ]
    console.log(set.values());  // [ 1, 2, 3 ]
    console.log(set.size);      // 3
    set.delete(1);
    console.log(set.values());  // [ 2, 3 ]
    set.clear();
    console.log(set.size);      // 0
    
    // 並集
    let a = [1, 2, 3];
    let b = new Set([4, 3, 2]);
    let union = new Set(a).union(b).values();
    console.log(union);     // [ 1, 2, 3, 4 ]
    //或者
    let a  = new Set([1, 2, 3, 4])
    let b = new Set([4, 5, 6])
    let union = new Set([...a, ...b])
    console.log(union) //1 2 3 4 5 6 去掉重複的4
    
    
    // 交集
    let c = new Set([4, 3, 2]);
    let intersect = new Set([1,2,3]).intersect(c).values();
    console.log(intersect); // [ 2, 3 ]
    //或者
    let a = new Set([1, 2, 3, 4])
    let b = new([4, 5, 6])
    let difference = [...a].filter(function(ele) {
      return b.has(ele);
    }) // 4
    //箭頭函數
    let difference = [...a].filter(x => x.has(x)) // 4
    
    // 差集
    let d = new Set([4, 3, 2]);
    let difference = new Set([1,2,3]).difference(d).values();
    // [1,2,3]和[4,3,2]的差集是1
    console.log(difference);    // [ 1 ]
    //或者
    let a = new Set([1, 2, 3, 4])
    let b = new([4, 5, 6])
    let difference = [...a].filter(function (ele) {
      return !b.has(ele);
    }) // 1, 2, 3
    

Map (字典)[類對象]

它類似於對象,也是鍵值對的集合,但是“鍵”的範圍不限於字符串,各種類型的值(包括對象)都可以當作鍵,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適;

那麼集合又和字典有什麼區別呢:

  • 共同點:集合、字典可以存儲不重複的值

  • 不同點:集合是以[值,值]的形式存儲元素,字典是以[鍵,值]的形式存儲

    這裏說的Set其實就是我們所要講到的集合,先來看下基礎用法

    const m = new Map();
    const o = {p: 'Hello World'};
    m.set(o, 'content')
    m.get(o) // "content"
    
    m.has(o) // true
    m.delete(o) // true
    m.has(o) // false
    
方法和屬性總結
  • 屬性

    size:返回字典所包含的元素個數

  • 操作方法

    1. set(key, val): 向字典中添加新元素

    2. get(key):通過鍵值查找特定的數值並返回

    3. has(key):如果鍵存在字典中返回true,否則false

    4. delete(key): 通過鍵值從字典中移除對應的數據

    5. clear():將這個字典中的所有元素刪除

    6. let m = new Map();
      m.set('Jay', 'Jay的Chou');
      m.set(true, '真的');
      console.log(m.has('Chou')); // false
      console.log(m.size); // 2
      console.log(m.keys()); // [ 'Jay', 'true' ]
      console.log(m.values()); // [ 'Jay的Chou', '真的' ]
      console.log(m.get('jay')); // undefined
      
      m.delete(true);
      console.log(m.keys()); // [ 'Jay' ]
      console.log(m.values()); // [ 'Jay的Chou' ]
      

      由於一個key只能對應一個value,所以,多次對一個key放入value,後面的值會把前面的值沖掉:

      var m = new Map();
      m.set('Adam', 67);
      m.set('Adam', 88);
      m.get('Adam'); // 88
      

      Map方法接受一個二維數組當做參數

      var m = new Map([['name', 'zhangsan'],['sex', 'male']]);
      console.log(m); //Map {"name" => "zhangsan", "sex" => "male"}
      
  • 遍歷方法有

    1. keys():將字典中包含的所有鍵名以數組形式返回

    2. values():將字典中包含的所有數值以數組形式返回

    3. entries():返回鍵值對的遍歷器

    4. forEach():遍歷字典的所有成員

      var map = new Map([['age', 19],['height', 180]]);
      
      for (let key of map.keys()) {
        console.log(key); // age height
      }
      
      for (let value of map.values()) {
        console.log(value); //19 180
      }
      
      for (let item of map.entries()) {
        console.log(`${ item[0] }: ${ item[1] }`);  //age: 19  height: 180
      }
      
      for (let [key, value] of map.entries()) {
        console.log(`${ key }: ${ value }`); //age: 19  height: 180
      }
      
      for (let [key, value] of map) {
        console.log(`${ key }: ${ value }`); //age: 19  height: 180
      }
      
      map.forEach((value, key, map) => {
        console.log(`${ key }: ${ value }`); //age: 19  height: 180
      });
      
方法剖析(實現原理)
  • 建一個字典

    function Map() {
        let items = {};
    }
    
    module.exports = Map;   // 導出
    
  • has方法

    // has(key)方法
    this.has = function (val) {
      return items.hasOwnProperty(val);
    };
    
  • set和get方法

    // set(key, val)方法
    // set相同key時,後面聲明的會覆蓋前面
    // 如: new Map().set({}, 'a')
    this.set = function (key, val) {
      items[key] = val;
    };
    // get(key)方法
    this.get = function (key) {
      // 判斷是否有key,如果有的話直接返回對應的值
      // 如果讀取一個未知的鍵,則返回undefined
      return this.has(key) ? items[key] : undefined;
    };
    
  • keys、values方法

    // keys()方法
    this.keys = function () {
      return Object.keys(items); // 返回遍歷集合的所有鍵名的數組
    };
    // values()方法
    this.values = function () {
      return Object.values(items); // 返回遍歷集合的所有鍵值的數組
    };
    
    
  • forEach方法

    // forEach(fn, context)方法
    this.forEach = function(fn, context = this) {
        for (let i = 0; i < this.size; i++) {
            let item = Object.keys(items)[i];
            fn.call(context, element, sameElement, set);     
        }
    };
    
  • 與Array之間的關係

    //Map轉數組
    const map = new Map();
    map.set('name' , 'hello').set({},'world');
    
    [...map] //[["name","hello"],[{},"world"]]
    [...map.value()] //[["hello","world"]
    
    //數組轉Map
    const map = new Map([["name","hello"],[{},"world"]]);
    
    map // {"name" => "hello", Object {} => "world"}
    
  • 與對象之間的關係

    //Map轉爲對象的方法
    function strMapToObj(strMap) {
      let obj = Object.create(null);
      for (let [k,v] of strMap) {
        obj[k] = v;
      }
      return obj;
    }
    let myMap = new Map().set('name', 'Virgo').set('old', '18');
    console.log(strMapToObj(myMap));// { name: "Virgo", old: "18"}
    
    //對象轉爲Map的方法
    function objToStrMap(obj) {
      let strMap = new Map();
      for (let k of Object.keys(obj)) {
        strMap.set(k, obj[k]);
      }
      return strMap;
    }
    var a = {name: 'Virgo', old: '18'}
    console.log(objToStrMap(a))//Map(2){"name" => "Virgo", "old" => "18"}
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章