數組進階 JS數組奇巧淫技

JS數組奇巧淫技

數組進階方法~

數組使用方法比較多。什麼時候使用什麼方法,用對方法,不用對很大的原因就是數組方法使用不熟練,導致寫出了很多代碼質量問題,其實很多地方稍加改動就可以變得簡單高效又優雅。讀了這篇文章使用數組在也苦惱,百度了(奇巧淫技主要是reduce~)。

數組操作首先要注意且牢記splice、sort、reverse這3個常用方法是對數組自身的操作,會改變數組自身。其他會改變自身的方法是增刪push/pop/unshift/shift、填充fill和複製填充copyWithin

先說數組常用方法,後說使用誤區。

數組常用方法

先獻上數組方法懶人圖一張祭天 (除了Array.keys()/Array.values()/Array.entries()基本都有):

[圖片上傳失敗...(image-394e86-1594174858373)]

<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>

生成類似[1-100]這樣的的數組:

測試大量數據的數組時可以這樣生成:

// fill
const arr = new Array(100).fill(0).map((item, index) => index + 1)

// Array.from() 評論區大佬指出
const arr = Array.from(Array(100), (v, k) => k + 1)

// ... + array.keys() 評論區大佬指出 生成的是0-99的數組
const ary = [...Array(100).keys()] 
複製代碼

數組最大值:

const ary = [12,13,14,15]
//Math.max(...ary)  15
//Math.max.apply(null,ary)  15

複製代碼

去掉數組中的空字符:

const ary = ['','a','b','','d']
for(var obj = 0 ;obj<ary.length;obj++){  //是否有空訂單
    if(ary[obj] == ''"){
        ary.splice(obj,1)
        obj= Number(obj)-1; //改變原數組上去除
    }
}
//['a','b','d']
let r = ary.filter(function (s) { //返回去掉控制符數組
    return s && s.trim();
});
//['a','b','d']
複製代碼

new Array(100) 會生成一個有100空位的數組,這個數組是不能被map(),forEach(), filter(), reduce(), every() ,some()遍歷的,因爲空位會被跳過(for of不會跳過空位,可以遍歷)。 [...new Array(4)] 可以給空位設置默認值undefined,從而使數組可以被以上方法遍歷。

數組解構賦值應用

// 交換變量
[a, b] = [b, a]
[o.a, o.b] = [o.b, o.a]
// 生成剩餘數組
const [a, ...rest] = [...'asdf'] // a:'a',rest: ["s", "d", "f"]
複製代碼

數組淺拷貝

const arr = [1, 2, 3]
const arrClone = [...arr]
// 對象也可以這樣淺拷貝
const obj = { a: 1 }
const objClone = { ...obj }
  Map 
 //1. 方法返回一個新數組,數組中的元素爲原始數組元素調用函數處理後的值。
 //2.不會對空數組進行檢測。
 //3.不會改變原始數組。
  var arraySort = ['a','b','c','d'];
  var arrayCopy = arraySort.map((item,index)=>{ return item});
  Array.from()
  //Array.from:允許在 JavaScript 集合(如: 數組、類數組對象、或者是字符串、 map 、set 等可迭代對象) 上進行有用的轉換。
  //array,from()方法拷貝數組。
   var arraySort = ['a','b','c','d'];
   var arrayFromCopy = Array.from(arraySort);
複製代碼

淺拷貝方法有很多如arr.slice(0, arr.length)/Arror.from(arr)等,但是用了...操作符之後就不會再想用其他的了~

數組合並

const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr3 = [7, 8, 9]
const arr = [...arr1, ...arr2, ...arr3]
複製代碼

arr1.concat(arr2, arr3)同樣可以實現合併,但是用了...操作符之後就不會再想用其他的了~

數組去重

const arr = [1, 1, 2, 2, 3, 4, 5, 5]
const newArr = [...new Set(arr)]
複製代碼

new Set(arr)接受一個數組參數並生成一個set結構的數據類型。set數據類型的元素不會重複且是Array Iterator,所以可以利用這個特性來去重。

數組取交集

const a = [0, 1, 2, 3, 4, 5]
const b = [3, 4, 5, 6, 7, 8]
const duplicatedValues = [...new Set(a)].filter(item => b.includes(item))
duplicatedValues // [3, 4, 5]
複製代碼

數組取差集

const a = [0, 1, 2, 3, 4, 5]
const b = [3, 4, 5, 6, 7, 8]
const diffValues = [...new Set([...a, ...b])].filter(item => !b.includes(item) || !a.includes(item)) // [0, 1, 2, 6, 7, 8]
複製代碼

數組轉對象

const arr = [1, 2, 3, 4]
const newObj = {...arr} // {0: 1, 1: 2, 2: 3, 3: 4}
const obj = {0: 0, 1: 1, 2: 2, length: 3}
// 對象轉數組不能用展開操作符,因爲展開操作符必須用在可迭代對象上
let newArr = [...obj] // Uncaught TypeError: object is not iterable...
// 可以使用Array.form()將類數組對象轉爲數組
let newArr = Array.from(obj) // [0, 1, 2]
複製代碼

數組攤平

const obj = {a: '羣主', b: '男羣友', c: '女裙友', d: '未知性別'}
const getName = function (item) { return item.includes('羣')}
// 方法1
const flatArr = Object.values(obj).flat().filter(item => getName(item))
// 經大佬指點,更加簡化(發現自己的抽象能力真的差~)
const flatArr = Object.values(obj).flat().filter(getName)
複製代碼

二維數組用array.flat(),三維及以上用array.flatMap()array.flat(2)可以傳參數字如 2,表示要攤平的層數。

數組常用遍歷

數組常用遍歷有 forEach、every、some、filter、map、reduce、reduceRight、find、findIndex 等方法,很多方法都可以達到同樣的效果。數組方法不僅要會用,而且要用好。要用好就要知道什麼時候用什麼方法。

遍歷的混合使用

filtermap方法返回值仍舊是一個數組,所以可以搭配其他數組遍歷方法混合使用。注意遍歷越多效率越低~

const arr = [1, 2, 3, 4, 5]
const value = arr
    .map(item => item * 3)
    .filter(item => item % 2 === 0)
    .map(item => item + 1)
    .reduce((prev, curr) => prev + curr, 0)
複製代碼

檢測數組所有元素是否都符合判斷條件

const arr = [1, 2, 3, 4, 5]
const isAllNum = arr.every(item => typeof item === 'number')
複製代碼

檢測數組是否有元素符合判斷條件

const arr = [1, 2, 3, 4, 5]
const hasNum = arr.some(item => typeof item === 'number')
複製代碼

找到第一個符合條件的元素/下標

const arr = [1, 2, 3, 4, 5]
const findItem = arr.find(item => item === 3) // 返回子項
const findIndex = arr.findIndex(item => item === 3) // 返回子項的下標

// 我以後再也不想看見下面這樣的代碼了😂
let findIndex
arr.find((item, index) => {
    if (item === 3) {
        findIndex = index
    }
})
複製代碼

數組使用誤區

數組的方法很多,很多方法都可以達到同樣的效果,所以在使用時要根據需求使用合適的方法。

垃圾代碼產生的很大原因就是數組常用方法使用不當,這裏有以下需要注意的點:

array.includes() 和 array.indexOf()

array.includes() 返回布爾值,array.indexOf() 返回數組子項的索引。indexOf 一定要在需要索引值的情況下使用。

const arr = [1, 2, 3, 4, 5]

// 使用indexOf,需要用到索引值
const index = arr.indexOf(1) // 0
if (~index) { // 若index === -1,~index得到0,判斷不成立;若index不爲-1,則~index得到非0,判斷成立。
    arr.spilce(index, 1)
}

// 使用includes,不需要用到索引值
// 此時若用indexOf會造成上下文上的閱讀負擔:到底其他地方有沒有用到這個index?
const isExist = arr.includes(6) // true
if (!isExist) {
    arr.push(6)
}
複製代碼

另外評論區大佬指出,array.indexOf()NaN 會找不到,返回-1array.includes()能找到,返回true~

[NaN].includes(NaN) // true
[NaN].indexOf(NaN) // -1
複製代碼

array.find() 、 array.findIndex() 和 array.some()

array.find()返回值是第一個符合條件的數組子項,array.findIndex() 返回第一個符合條件的數組子項的下標,array.some() 返回有無複合條件的子項,如有返回true,若無返回false。注意這三個都是短路操作,即找到符合條件的之後就不在繼續遍歷。

在需要數組的子項的時候使用array.find() ;需要子項的索引值的時候使用 array.findIndex() ;而若只需要知道有無符合條件的子項,則用 array.some()

const arr = [{label: '男', value: 0}, {label: '女', value: 1}, {label: '不男不女', value: 2}]

// 使用some
const isExist = arr.some(item => item.value === 2)
if (isExist) {
    console.log('哈哈哈找到了')
}

// 使用find
const item = arr.find(item => item.value === 2)
if (item) {
    console.log(item.label)
}

// 使用findIndex
const index = arr.findIndex(item => item.value === 2)
if (~index) {
    const delItem = arr[index]
    arr.splice(index, 1)
    console.log(`你刪除了${delItem.label}`)
}
複製代碼

建議在只需要布爾值的時候和數組子項是字符串或數字的時候使用 array.some()

// 當子包含數字0的時候可能出錯
const arr = [0, 1, 2, 3, 4]

// 正確
const isExist = arr.some(item => item === 0)
if (isExist) {
    console.log('存在要找的子項,很舒服~')
}

// 錯誤
const isExist = arr.find(item => item === 0)
if (isExist) { // isExist此時是0,隱式轉換爲布爾值後是false
    console.log('執行不到這裏~')
}

// 當子項包含空字符串的時候也可能出錯
const arr = ['', 'asdf', 'qwer', '...']

// 正確
const isExist = arr.some(item => item === '')
if (isExist) {
    console.log('存在要找的子項,很舒服~')
}

// 錯誤
const isExist = arr.find(item => item === '')
if (isExist) { // isExist此時是'',隱式轉換爲布爾值後是false
    console.log('執行不到這裏~')
}
複製代碼

array.find() 和 array.filter()

只需要知道 array.filter() 返回的是所有符合條件的子項組成的數組,會遍歷所有數組;而 array.find() 只返回第一個符合條件的子項,是短路操作。不再舉例~

合理使用 Set 數據結構

由於 es6 原生提供了 Set 數據結構,而 Set 可以保證子項不重複,且和數組轉換十分方便,所以在一些可能會涉及重複添加的場景下可以直接使用 Set 代替 Array,避免了多個地方重複判斷是否已經存在該子項。

const set = new Set()
set.add(1)
set.add(1)
set.add(1)
set.size // 1
const arr = [...set] // arr: [1]
複製代碼

強大的reduce(奇巧淫技)

array.reduce 遍歷並將當前次回調函數的返回值作爲下一次回調函數執行的第一個參數。

利用 array.reduce 替代一些需要多次遍歷的場景,可以極大提高代碼運行效率。

  1. 利用reduce 輸出一個數字/字符串

假如有如下每個元素都由字母's'加數字組成的數組arr,現在找出其中最大的數字:(arr不爲空)

const arr = ['s0', 's4', 's1', 's2', 's8', 's3']

// 方法1  進行了多次遍歷,低效
const newArr = arr.map(item => item.substring(1)).map(item => Number(item))
const maxS = Math.max(...newArr)

// 方法2  一次遍歷
const maxS = arr.reduce((prev, cur) => {
  const curIndex = Number(cur.replace('s', ''))
  return curIndex > prev ? curIndex : prev
}, 0)
複製代碼
  1. 利用reduce 輸出一個數組/對象
const arr = [1, 2, 3, 4, 5]

 // 方法1  遍歷了兩次,效率低
const value = arr.filter(item => item % 2 === 0).map(item => ({ value: item }))

// 方法1  一次遍歷,效率高
const value = arr.reduce((prev, curr) => {
    return curr % 2 === 0 ? [...prev, { value: curr }] : prev
}, [])
複製代碼

掌握了上面兩種用法,結合實際需要,就可以用 reduce/reduceRight 實現各種奇巧淫技了。
作者
https://juejin.im/post/5d71fff5f265da03e4678328

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章