ES6的Set和Map你都知道嗎?一文了解集合和字典在前端中的應用

一文了解集合和字典在前端中的應用

這是我參與更文挑戰的第20天,活動詳情查看:更文挑戰前端

在前端突飛猛進的大背景下, ES6 也基本已經覆蓋性地全民普及。而數據結構集合和字典,也被運用於 ES6 的語法當中。 ES6 經過使用 SetMap 這兩個函數,來實現集合和字典的思想。而集合和字典,又是怎麼被靈活應用的呢?面試

下面的這篇文章中,將帶領你們來一塊兒瞭解集合和字典在前端中應用,並使用 MapSet 來實現前端的一些常見場景。一塊兒來學習吧~🙋‍♀️數組

1、📝集合

一、集合是什麼?

  • 集合是一種無序且惟一的數據結構;
  • ES6 中有集合,名爲 Set
  • 集合的經常使用操做: 去重、判斷某元素是否在集合中、求交集……

二、前端與集合:使用ES6中的Set

ES6 中的 Set 能夠作什麼呢?markdown

  • 使用 Set 對象: newadddeletehassize
  • 迭代 Set :多種迭代方法、 SetArray 互轉、求並集/交集/差集

三、用Set模擬並集、交集和差集

(1)模擬並集運算

咱們能夠建立一個函數,來返回包含 setAsetB 中全部元素的新集合。迭代這兩個集合,並把全部元素都添加到並集的集合中。以下代碼所示:數據結構

//模擬並集運算
const union = (setAe, setB) => {
    const unionab = new Set()
    setA.forEach(value => unionab.add(value))
    setB.forEach(value => unionab.add(value))
    return [...unionab]
}

console.log(union([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[1, 2, 5, 8,9, 4, 10]
複製代碼

(2)模擬交集運算

模擬交集運算須要建立一個輔助函數,來生成包含 setAsetB 兩個集合中共同擁有元素的新集合。具體代碼以下:函數

// 模擬交集運算
const intersection = (setA, setB) => {
    const intersectionSet = new Set()
    const arrSetB = new Set(setB)
    setA.forEach(value => {
        if (arrSetB.has(value)) {
            intersectionSet.add(value)
        }
    })
    return [...intersectionSet]
}

console.log(intersection([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[5,8,9]
複製代碼

(3)模擬差集運算

差集運算是建立集合 setA 有,而集合 setB 沒有的元素。簡單來講,就是 setA 減去和 setB 相交的部分,剩餘的部分便是差集的部分。oop

// 模擬差集運算
const difference = (setA, setB) => {
    const differenceSet = new Set()
    const arrSetB = new Set(setB)
    setA.forEach(value => {
        if (!arrSetB.has(value)) {
            differenceSet.add(value)
        }
    })
    return [...differenceSet]
}

console.log(difference([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[1, 2]
複製代碼

四、使用擴展運算符來模擬並集、交集和差集

上面咱們實現了用Set來模擬並集、交集和差集,但這彷佛有一點點冗餘,若是遇到數據量大的時候還每一次都要執行這麼多行代碼,彷佛這樣子聽起來就那麼友好了。post

所以,咱們引入了一種新的方法來解決,ES6的擴展運算符學習

若是使用擴展運算符來進行運算的話,整個過程只須要三個步驟:ui

  • 將集合轉化爲數組;
  • 執行須要的運算;
  • 將結果轉化回集合。

接下來咱們就用擴展運算符,來一一實現並集、交集和差集

(1)用擴展運算符實現並集

來看下面一段代碼:

// 模擬並集運算
const union = (setA, setB) => {
    return new Set([...setA, ...setB]);
}
console.log(union([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[1, 2, 5, 8,9, 4, 10]
複製代碼

經過以上代碼咱們能夠看到,使用擴展運算符,只須要短短一行代碼,便可實現具體的並集運算,這樣看起來簡潔了許多。

接下來咱們繼續用這種方法,來實現交集差集

(2)用擴展運算符實現交集

// 模擬交集運算
const intersection = (setA, setB) => {
    const arrB = new Set(setB);
    return [...new Set([...setA].filter(x => arrB.has(x)))]
}
console.log(intersection([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[5, 8, 9]
複製代碼

與並集同樣的效果,運用擴展運算符,很簡潔的就實現了交集的功能。

(3)用擴展運算符實現差集

// 模擬差集運算
const difference = (setA, setB) => {
    const arrB = new Set(setB)
    return [...new Set([...setA].filter(x => !arrB.has(x)))]
}
console.log(difference([1, 2, 5, 8, 9], [4, 5, 8, 9, 10])) //[1, 2]
複製代碼

一樣地,使用擴展運算符的方法,但與交集相反的是,交集是篩選出集合setB擁有的元素,而差集是篩選出集合setB沒有的元素,從而最終達到具體效果。

五、leetcode案例題分析

(1)leetcode349兩個數組的交集

1)題意

這裏附上原題連接

給定兩個數組,編寫一個函數來計算它們的交集。

輸入輸出示例:

  • 輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
  • 輸出: [9,4]
  • 解釋:
    • nums1nums2 兩個數組的相交部分爲 [9, 4] 。

2)解題思路

  • 求交集且無序惟一。
  • 使用集合。

3)解題步驟

  • 用集合對 nums1 去重。
  • 遍歷 nums1 ,篩選出 nums2 中也包含的值。

4)代碼實現

/** * @param {Array} nums1 數組1 * @param {Array} nums2 數組2 * @returns  */
let intersection = function(nums1, nums2){

    // 先進行數組去重
    const arr1 = new Set(nums1);
    const arr2 = new Set(nums2);
    
    // 過濾掉arr1在arr2中已經有的元素,過濾結果即爲交集
    // has可改成includes
    const arr3 = [...arr1].filter(item => arr2.has(item));

    return arr3;
}
console.log(intersection([1,2,3,4],[4,6,8]))
複製代碼

2、📂字典

一、字典是什麼?

  • 字典與集合類似,字典也是一種存儲惟一值的數據結構,但它是以鍵值對的形式來存儲。
  • 注意:字典必定是以鍵值對的形式存儲!!

二、前端與集合:使用ES6中的Map

ES6中的Map能夠作什麼呢?

  • 使用 Map 對象: newsetdeleteclear
  • 字典的經常使用操做,鍵值對的增刪改查

三、使用Map類的API

下面展現一段代碼,來展現 map 的相關 API

const map = new Map()

//增
map.set('monday', '星期一')
map.set('Tuesday', '星期二')
map.set('Wednesday', '星期三')

console.log(map.has('monday')) //true
console.log(map.size) //3
console.log(map.keys()) //輸出{'monday', 'Tuesday', 'Wednesday'}
console.log(map.values()) //輸出{'星期一', '星期二', '星期三'}
console.log(map.get('monday')) //星期一

//刪
map.delete('monday')

//清空
map.clear()

//改
map.set('monday', '星期四')

複製代碼

四、leetcode案例題分析

(1)leetcode349兩個數組的交集

1)題意

這裏附上原題連接

給定兩個數組,編寫一個函數來計算它們的交集。

輸入輸出示例:

  • 輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
  • 輸出: [9,4]
  • 解釋:
    • nums1nums2 兩個數組的相交部分爲 [9, 4] 。

2)解題思路

  • nums1nums2 都有的值。
  • 字典創建一個映射關係,記錄 nums1 裏有的值。
  • 遍歷 nums2 ,找出 nums1 裏也有的值。

3)解題步驟

  • 新建一個字典,遍歷 nums1 ,填充字典
  • 遍歷 nums2 ,遇到字典裏的值就選出,並從字典中刪除。

4)代碼實現

/** * @param {Array} nums1 數組1 * @param {Array} nums2 數組2 * @returns  */
let intersection = function(nums1, nums2){
    // 先創建一個字典
    const map = new Map();
    // 遍歷nums1的每個,並放入數組中
    nums1.forEach(n => {
        map.set(n, true);
    });

    const res = [];
    // 遍歷nums2中的每個
    nums2.forEach(n => {
        // 與nums1中的對比,若是同樣則push進res
        if(map.get(n)){
            res.push(n);
            map.delete(n);
        }
    });
    return res;
}

console.log(intersection([1, 2, 2, 1], [2, 2]))
複製代碼

(2)leetcode20有效地括號

1)題意

這裏附上原題連接

給定一個只包括 '('')''{''}''['']' 的字符串 s ,判斷字符串是否有效。

輸入輸出示例:

  • 輸入: s = "()[]{}"
  • 輸出: true
  • 解釋:
    • 左括號和右括號對應開閉。

2)解題步驟

  • 判斷字符串的個數是否爲偶數,不是則直接返回。
  • 創建一個字典,分別將三個括號映射進入。
  • 遍歷字符串
  • 遍歷 nums1 ,篩選出 nums2 中也包含的值。

3)代碼實現

/** * * @param {String} s 括號字符串 * @returns boolean */
 let isValid = function(s){
    if(s.length % 2 === 1){
        return false;
    }

    const stack = [];
    const map = new Map();
    map.set('(', ')');
    map.set('{', '}');
    map.set('[', ']');

    for(let i = 0; i < s.length; i++){
        const c = s[i];
        if(map.has(c)){
            stack.push(c);
        }else{
            // 獲取棧中最後一個括號的值
            const t = stack[stack.length - 1];
            if(map.get(t) === c){
                stack.pop();
            }else{
                return false;
            }
        }
    }
    return stack.length === 0;
}

console.log(isValid('[]')) //true
console.log(isValid('(]')) //false
console.log(isValid('([)]')) //false

複製代碼

(3)leetcode1兩數之和

1)題意

這裏附上原題連接

給定一個整數數組 nums 和一個整數目標值 target,請你在該數組中找出 和爲目標值 target 的那兩個整數,並返回它們的數組下標。

你能夠假設每種輸入只會對應一個答案。可是,數組中同一個元素在答案裏不能重複出現。

你能夠按任意順序返回答案。

輸入輸出示例:

  • 輸入: nums = [2,7,11,15], target = 9
  • 輸出: [0,1]
  • 解釋:
    • 由於 nums[0] + nums[1] == 9 ,返回 [0, 1]

2)解題思路

  • nums 想象成相親者;
  • target 想象成匹配條件;
  • 用字典創建一個婚姻介紹所,存儲相親者的數字和下標。

3)解題步驟

  • 新建一個字典做爲婚姻介紹所;
  • nums 的值,逐個來介紹所找的對象,沒有合適的就先登記,有合適的就牽手成功;
  • 鍵值對,鍵 key 表明此對象的值,值 value 表明對象的聯繫方式,即下標。

4)代碼實現

/** * * @param {Array} nums 數組 * @param {Number} target 目標和 * @returns [] */
let twoSum = function(nums, target){
    const map = new Map();
    for(i = 0; i < nums.length; i++){
        // 來找對象的人的值
        const n = nums[i];
        // 想找的目標對象的值
        const n2 = target - n;
        // 找到對象時
        if(map.has(n2)){
            return [map.get(n2), i];
        }
        // 找不到對象時,放進字典裏等待對象的到來
        else{
            map.set(n, i);
        }
    }
    return '沒有目標匹配';
}

console.log(twoSum([1, 2, 3, 4], 6)) //[1, 3]
複製代碼

(4)leetcode3無重複字符的最長子串

1)題意

這裏附上原題連接

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

輸入輸出示例:

  • 輸入: s = "abcabcbb"

  • 輸出: 3

  • 解釋:

    • 由於無重複字符的最長子串是 "abc" ,因此其長度爲 3
  • 2)解題思路

    • 先找出全部的不包含重複字符的子串。
    • 找出長度最大的那個子串,返回其長度便可。

    3)解題步驟

    • 用雙指針維護一個滑動窗口,用來剪切子串
    • 不斷移動右指針,遇到重複字符,就把左指針移動到重複字符的下一位。
    • 過程當中,記錄全部窗口的長度,並返回最大值

4)代碼實現

/** * @param {string} s * @return {number} */
let lengthOfLongestSubstring = function(s){
    let l = 0;
    let res = 0;
    const map = new Map();
    for(let r = 0; r < s.length; r++){
        // map.get(r) >= l 確保下標在左指針右邊
        if(map.has(s[r]) && map.get(s[r]) >= l){
            l = map.get(s[r]) + 1;
        }
        res = Math.max(res, r - l + 1);
        map.set(s[r], r);
    }
    return res;
}

console.log(lengthOfLongestSubstring('ssdfsf')) // 3
複製代碼

(5)leetcode76最小覆蓋子串

1)題意

這裏附上原題連接

給你一個字符串 s 、一個字符串 t 。返回 s 中涵蓋 t 全部字符的最小子串。若是 s 中不存在涵蓋 t 全部字符的子串,則返回空字符串 ""

**注意:**若是 s 中存在這樣的子串,咱們保證它是惟一的答案。

輸入輸出示例:

  • 輸入: s = "ADOBECODEBANC", t = "ABC"
  • 輸出: "BANC"
  • 解釋:
    • BANC 覆蓋 ABC 三個字母,而且是最小子串

2)解題思路

  • 先找出全部包含 T 的子串。
  • 找出長度最小的那個子串,返回便可。

3)解題步驟

  • 雙指針維護一個滑動窗口。
  • 移動右指針,找到包含 T 的子串,移動左指針,儘可能減小包含 T 的子串的長度。

4)代碼實現

/** * @param {string} s * @param {string} t * @return {string} */
let minWindow = function(s, t){
    // 用雙指針維護一個滑動窗口
    // 指針初始位置都在第一個位置,即下標索引爲0
    let l = 0;
    let r = 0;
    // 創建一個字典,用來表示子串須要的字符以及它的個數
    const need = new Map();
    for(let c of t){
        // 遍歷子串的字符,存放到字典裏
        need.set(c, need.has(c) ? need.get(c) + 1 : 1);
    }

    // 須要的類型數量
    let needType = need.size;
    let res = '';
    // 移動右指針
    while(r < s.length){
        // 在右指針不斷移動的過程當中,咱們不斷獲取當前的字符
        const c = s[r];
        // 判斷當前字符是否在咱們的需求列表裏面
        if(need.has(c)){
            need.set(c, need.get(c) - 1);
            if (need.get(c) === 0){
                needType -= 1;
            }
        }
        while(needType === 0){
            // 打印滑動窗口及表示的子串
            // substring方法遵循左閉右開原則
            // console.log(s.substring(l, r + 1));
            const newRes = s.substring(l, r + 1);
            // 找出最小的子串
            if(!res || newRes.length < res.length){
                res = newRes;
            }
            // c2表明左指針當前的字符
            const c2 = s[l];
            // 若是左指針在需求列表裏面
            if(need.has(c2)){
                // 字符的需求數量就須要增長
                need.set(c2, need.get(c2) + 1);
                // 若是字符需求數量爲1,原來是0,如今是1,那麼就從新須要此字符
                if (need.get(c2) === 1){
                    needType += 1;
                }
            }
            l += 1;
        }//當跳出while循環時,意味着左指針不能再移動,要開始移動右指針
        // 右指針移動則遞增1
        r += 1;
    }
    return res;
}

console.log(minWindow('ASDFFGFGCX','AFG')) // ASDFFG
複製代碼

3、📆結束語

字典和集合能夠算是前端面試的必考題了,同時在平常開發中的使用頻率也相對較高,所以掌握字典和集合的內容是較爲重要的。

關於字典和集合在前端中的應用就講到這裏啦!但願對你們有幫助!

  • 關注公衆號 星期一研究室 ,第一時間關注學習乾貨,更多精彩專欄待你解鎖~
  • 若是這篇文章對你有用,記得 一鍵三連 再走哦~
  • 不期而遇,咱們下期見!🥂🥂🥂
相關文章
相關標籤/搜索