• 盘盘在项目中你常用的那些数组API


    数组在业务中是一个非常高频的API,在业务中基本都有用它,必不可少,本文是一篇数组常用API的总结。

    正文开始…

    前置

    首先我们看下数组本身有哪些方法

    console.log(JSON.stringify(Reflect.ownKeys(Array.prototype), null, 2)) 
    
    • 1

    结果:

    ["length","constructor","at","concat","copyWithin","fill","find","findIndex","lastIndexOf","pop","push","reverse","shift","unshift","slice","sort","splice","includes","indexOf","join","keys","entries","values","forEach","filter","flat","flatMap","map","every","some","reduce","reduceRight","toLocaleString","toString","findLast","findLastIndex",
    ] 
    
    • 1
    • 2

    reduce

    这是一个项目上非常有用,但是代码看起来不是很直白的一个API

    • 场景 我需要根据数组中的某个值,用对象与原数组建立映射关系
    var sourceArr = [{name: 'Maic',age: 18,arr: ['a', 'b']},{name: 'Tom',age: 20,arr: ['a', 'b', 'c']},{name: 'Jack',age: 15,arr: ['e', 'd', 'f']}
    ] 
    
    • 1
    • 2

    我想通过数组访问数组的某个name或者value就能找到当前原数据的item,前置条件namevalue不会为相同

    function getMap(key, arr) {return arr.reduce((prev, cur) => {if (key) {prev[cur[key]] = cur;return prev}}, {})
    }
    /*
     getMap('name', sourceArr)
    {Jack: {name: 'Jack', age: 15, arr: Array(3)}Maic: {name: 'Maic', age: 18, arr: Array(2)}Tom: {name: 'Tom', age: 20, arr: Array(3)}
    }
    */
    console.log(getMap('name', sourceArr)['Maic'])
    /* {name: 'Maic',age: 18,arr: ['a', 'b']},
    */
    console.log(getMap('age', sourceArr)[15])
    /*{name: 'Jack',age: 15,arr: ['e', 'd', 'f']}
    */
    console.log(getMap('arr', sourceArr)['a,b'])
    /*
     {name: 'Maic',age: 18,arr: ['a', 'b']},
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们可以让这个方法getMap变成更通用,只需要挂载原型上即可

    ...
    Array.prototype.$getMap = function(key) {return this.reduce((prev, cur) => {if (key) {prev[cur[key]] = cur;return prev}}, {})
    }
    sourceArr.$getMap('name')
    /*
    {Jack: {name: 'Jack', age: 15, arr: Array(3)}Maic: {name: 'Maic', age: 18, arr: Array(2)}Tom: {name: 'Tom', age: 20, arr: Array(3)}
    }
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过以上例子,我们分析一下reduce这个在数组中所谓的累计计算,我们以最简单的方式来深刻理解reduce这个方法

    const sourceArr = [{name: 'Maic',age: 18,arr: ['a', 'b']},{name: 'Tom',age: 20,arr: ['a', 'b', 'c']},{name: 'Jack',age: 15,arr: ['e', 'd', 'f']}
    ]
    const res = sourceArr.reduce((prev, cur) => {prev[cur.name] = cur;return prev}, {}) 
    
    • 1
    • 2
    • 3

    arr.reduce(callback, init)的第一个参数是回调函数,第二参数previnit的值,callbackprev就是{},cur是当前的数组的item

    第一次累计的结果prev的值是:

    {'Maic': {name: 'Maic',age: 18,arr: ['a', 'b']}
    } 
    
    • 1
    • 2

    这个结果会当成第二次累计的prev值,记住cur是当前原元素累计次数的item,比如从下标0次开始累计,那么cur就是数组的第一个item

    第二次累计的结果就是

    {'Maic': {name: 'Maic',age: 18,arr: ['a', 'b']},'Tom': {name: 'Tom',age: 20,arr: ['a', 'b', 'c']}
    } 
    
    • 1
    • 2

    依次类推…

    所以我通过对象,将数组的值最为对象的key,通过建立对象与原数据对应的关系,用reduce这个方法可以快捷的达到这样的需求效果,关于数组reduce后续会单独写一篇文章总结更多在实际业务上的一些思考。也可参考官方文档MDN讲解reduce这篇好文章

    有人说reduce实现这功能有点秀了,for循环不是更好理解吗

    forEach

    forEach也是一个循环数组的的方法,循环方法我们知道在jsfor..of,for(let i=0;i或者while条件,这些都是可以条件中断,但是forEach不能中断【非常规操作除外,比如throw抛出异常是可以中断forEach的】

    我们用同样的一个例子来实现reduce一样的功能

    ...
    
    function getMap2 (key, arr) {const res = {}arr.forEach(v => {res[v[key]] = v;})return res;
    }
    getMap2('name', sourceArr)['Maic'] 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当然是可以的,条条大路通罗马,forEach貌似看起来比reduce写的那段代码阅读负担要小得多,但是同样的效果forEach执行效率也比reduce更高点

    所以复杂的事情,尽量简单化,没有好坏高低之分,对于搬砖工来说,哪种熟悉就用哪个了。

    push

    这是一个比较常用的方法,也是向数组中添加数据

    场景:假设现在有一个需求,如何将一个一维数组变成一个树结构,并且还要按照指定分类进行分组

    原数据大概就是这样

    var sourcesData = [{bookType: '文学类',type: 1,bookName: '基督山伯爵',id: 'x123'},{bookType: '财商类',type: 2,bookName: '穷爸爸与富爸爸',id: 'x45622'},{bookType: '经济学',type: 3,bookName: '货币战争',id: 'ssxdede'},{bookType: '文学类',type: 1,bookName: '百年孤独',id: '1234562sx'}
    ] 
    
    • 1
    • 2

    后端给的数据是一维的,我们需要变成一个tree结构进行分类

    const transformTree = (sourceArr, result) => {// 1、先根据type字段进行分组const typeData = [1, 2, 3].map(type => sourceArr.filter(v => v.type === type * 1))// 2、分别含有type字段进行分类后for (data of typeData) {data.forEach(item => {// 3、根据bookType进行归组,文件夹分类,同一文件夹的归到一类去 const target = result.find(v => v.label === item.bookType);if (target) {// 如果找到了就原数组数据添加到children里去target.children.push({label: item.bookName,...item})} else {result.push({label: item.bookType,children: [{...item,label: item.bookName}]})}})}return result
    }
    console.log('push:res', JSON.stringify(transformTree(sourcesData, []), null, 2)); 
    
    • 1
    • 2
    • 3

    打印的结果:

    [{"label": "文学类","children": [{"bookType": "文学类","type": 1,"bookName": "基督山伯爵","id": "x123","label": "基督山伯爵"},{"label": "百年孤独","bookType": "文学类","type": 1,"bookName": "百年孤独","id": "1234562sx"}]},{"label": "财商类","children": [{"bookType": "财商类","type": 2,"bookName": "穷爸爸与富爸爸","id": "x45622","label": "穷爸爸与富爸爸"}]},{"label": "经济学","children": [{"bookType": "经济学","type": 3,"bookName": "货币战争","id": "ssxdede","label": "货币战争"}]}
    ] 
    
    • 1
    • 2

    因此我们就将一个一围数组变成了一个tree结构

    我们可以将上面一段forEach改成reduce,感受下理解的难度,最后的效果是一样,但是reduce对新手不太友好,这里就是为了使用而使用,好像没太必要

    const transformTree2 = (sourceArr, result) => {// 1、先根据type字段进行分组const typeData = [1, 2, 3].map(type => sourceArr.filter(v => v.type === type * 1))// 2、分别含有type字段进行分类后for (data of typeData) {data.reduce((prev, cur) => {// 3、根据bookType进行归组,文件夹分类,同一文件夹的归到一类去const target = result.find(v => v.label === cur.bookType);if (target) {target.children.push({label: cur.bookName,...cur})} else {result.push({label: cur.bookType,children: [{...cur,label: cur.bookName}]})}}, sourceArr[0])}return result
    }
    console.log(transformTree2(sourcesData, [])) 
    
    • 1
    • 2
    • 3

    some

    只要条件有一个满足就返回true,否则就返回false 场景: 我需要在原数组大于某个值,一旦满足,就返回true

    const arraySome = (arr, num) => {return arr.some(v => v > num)
    }
    console.log('some:', arraySome([1, 2, 3], 2), arraySome([4, 5, 6], 7)) // true, false 
    
    • 1
    • 2
    • 3

    every

    恰好与some相反,必须所有条件满足,才会返回true

    场景: 在业务中你想一个原数据的每一项都满足一个指定条件,此时会返回true,否则就是false

    const arrayEvery = (arr, num) => {return arr.every(v => v.includes(num))
    }
    console.log('every:', arrayEvery(['abc', 'cdabc', 'efg'], 'ab'), arrayEvery(['abc', 'cdabc', 'aefg'], 'a')) // false true 
    
    • 1
    • 2
    • 3

    at

    比较罕见,与通过下标取值等价

    const arrayAt = (arr = [], index) => {return arr.at(index)
    } 
    
    • 1
    • 2

    concat

    在原有数组浅拷贝一份新的数据,然后在新数据添加对应的内容

    /**
     * arr: [1,2,3]
     * concat: 在原数组基础上浅拷贝一份新的数据,然后在新数据上追加对应的内容
     * 示例:ret = arr.concat(4) ----- ret: [1,2,3,4]
     *ret = arr.concat('a', {a:'Maic'}, ['abc',{a: 'Tom'}])ret: [1,2,3,'a',{a:'Maic'},'abc', {a: 'Tom'}]
     *ret = arr.concat(1).concat(2) [1,2,3,1,2]
     * 场景:不太想影响原数据,又想在原数据上添加数据时,但是注意这个方法是一个浅拷贝,如果数组中是引用数据类型,修改新值会影响原有的值
     * */
    const arrayConcat = () => {const arr = [1, 2, 3, { name: 'Maic' }]const newArr = arr.concat('abc')newArr[3].name = 'Tom'const arr2 = arr.concat('a', { a: 'Maic' }, ['abc', { a: 'Tom' }])const arr3 = arr.concat([1, 2, 3])const arr4 = [1, 2, 3].concat(4).concat(5)return {newArr,arr,arr2,arr3,arr4}
    }
    console.log('concat:', arrayConcat()) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    fill

    填充一份相同的数据

    场景: 你想mock一份测试数据

    const arrayFill = (num, val) => {return Array(num).fill(val)
    }
    console.log('fill:', arrayFill(3, 'Maic'))
    /*
     [ 'Maic', 'Maic', 'Maic' ]
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    find

    /**
     * find: 寻找数组的item,并返回其寻找到的结果, 如果没有找到就返回undefined
     * 场景:需要根据某个条件值找到数据中的当前item数据时
     */
    const arrayFind = (sourceData, key, target) => {return sourceData.find(v => v[key] === target)
    }
    console.log('find:', arrayFind([{ name: 'Maic', age: 18 }, { name: 'Tom', age: 25 }], 'name', 'Maic')) // {name: 'Maic', age: 18} 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    findIndex

    寻找原数据匹配的目标值下标

    /**
     * findIndex: 寻找目标值的当前索引,如果没找到就返回-1
     * 场景:在你根据某个条件想获取当前条件的索引值,比如进行删除,或者插入,替换等操作
     */
    
    const arrayFindIndex = (sourceData, key, target) => {return sourceData.findIndex(v => v[key] === target)
    }
    console.log('findIndex', arrayFindIndex([{ name: 'Maic', age: 18 }, { name: 'Tom', age: 25 }], 'name', 'Jack')) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    lastIndexOf

    找到目标元素的当前索引

    /**
     * lastIndexOf: 找到元素的当前下标索引
     * 场景:功能与findIndex类似,根据其值寻找目标值的当前下标索引
     */
    const arrayLastIndexOf = (sourceData, val) => {return sourceData.lastIndexOf(val)
    }
    console.log('lastIndexOf', arrayLastIndexOf(['a', 'b', 'c', 'd'], 'b')) // 1 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    pop

    获取数组元素的最后一只元素的值,会改变原数组的长度,每一次pop操作将会把数组的最后一只值弹出去,原数组长度会减一

    const arrayPop = function (sourceData) {return sourceData.pop()
    }
    console.log('pop:', arrayPop(['a', 'b', 'c'])) // c 
    
    • 1
    • 2
    • 3

    reverse

    将原数据进行倒叙排列

    const arrayReverse = (sourceData) => {return sourceData.reverse()
    }
    console.log('reverse', arrayReverse([1, 2, 3, 4])) // [4,3,2,1] 
    
    • 1
    • 2
    • 3

    shift

    获取数组的第一个元素,会改变原数组的长度,会改变原数组 场景:模拟队列

    const arrayShift = (sourceData) => {return {data: sourceData.shift(),sourceData}
    }
    console.log('shift', arrayShift([1, 2, 3, 4])) // {data:1, sourceData: [2,3,4]} 
    
    • 1
    • 2
    • 3

    unshift

    向原数据添加数据,每次操作都会往数组的首位添加,会改变原数组的长度,返回值是当前数组的长度

    const arrayUnshift = (sourceData, val) => {return {result: sourceData.unshift(val),sourceData}
    }
    console.log('unshift:', arrayUnshift([1, 2, 3], 'a')) 
    
    • 1
    • 2
    • 3

    slice

    获取原数据指定索引范围的值,不会影响原有值

    /**
     * slice: 获取原数据指定索引范围的值,不会影响原有值
     * 场景:应用很多
     * arr: [1,2,3,4]
     * 
     * arr.slice(0) --- [1,2,3,4] 浅拷贝
     * arr.slice(1) ---[2,3,4]
     * 
     * arr.slice(1,3) --- [2,3]
     * 
     * arr.slice(-1) ---[4]
     * arr.slice(-2)----[3,4]
     * 
     */
    const arraySlice = (sourceArr = []) => {const arr1 = sourceArr.slice(0);const arr2 = sourceArr.slice(1);const arr3 = sourceArr.slice(1, 3);const arr4 = sourceArr.slice(-1);const arr5 = sourceArr.slice(-2);return {arr1,arr2,arr3,arr4,arr5}
    }
    console.log('slice:', arraySlice([1, 2, 3, 4]));
    /*
    slice: {arr1: [ 1, 2, 3, 4 ],arr2: [ 2, 3, 4 ],arr3: [ 2, 3 ],arr4: [ 4 ],arr5: [ 3, 4 ]
    }
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    sort

    对数组进行排序

    /**
     * sort: 排序
     * arr.sort((a, b) => a - b) // 升序
     * arr.sort((a,b) => b-a) // 降序
     * 场景:对数据的某个字段进行排序
     */
    
    const arraySort = (sourceArr = []) => {const upSort = sourceArr.sort((a, b) => a - b)const downSort = sourceArr.sort((a, b) => b - a)return {upSort,downSort}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    splice

    对原数组进行删除替换截取操作,会影响原数组的值

    const arraySplice = () => {const arr1 = [1, 2, 3, 4].splice(1); // [2,3,4]const arr2 = [1, 2, 3, 4].splice(0, 2); // [1,2]const arr3 = [1, 2, 3, 4].splice(2, 1); // [1,2,4] 删除了索引为2的元素,返回剩下的元素const arr4 = [1, 2, 3, 4].splice(-1); // [4]return {arr1,arr2,arr3,arr4}
    }
    console.log('splice:', arraySplice()) 
    
    • 1
    • 2
    • 3

    filter

    过滤数组操作,根据某个条件,返回一个过滤结果的数组

    /**
     * filter: 根据条件进行过滤,返回过滤后的结果
     * 场景: 需要过滤原数据中某些值时
     */
    const arrayFilter = (sourceData, val) => {return sourceData.filter(v => v === val)
    }
    console.log('filter:', arrayFilter([1, 2, 3, 4], 4)) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    map

    根据原数组重新返回一个新的数组

    /*
     * map: 在原有基础上重新返回一个新数组
     */
    const arrayMap = (sourceArr) => {return sourceArr.map(v => v + 1)
    }
    console.log('map:', arrayMap([1, 2, 3]))
    // [2,3,4] 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    flatMap

    场景:可以根据原数组组合成一个你想要的数据结构

    比如原数据有不想要的字段

    const arrayFlatMap = (sourceArr, arr) => {return {source: sourceArr.flatMap(v => typeof v === 'number' ? [v] : []),narr: arr.flatMap(v => [{ name: v.name, value: v.value }])}
    }
    console.log('flatMap:', arrayFlatMap([1, 2, [3, 4]], [{ id: 1, name: 'Maic', value: 0 }, { id: 2, name: 'Tom', value: 1 }]))
    
    /**
     * flatMap: {source: [ 1, 2 ],narr: [ { name: 'Maic', value: 0 }, { name: 'Tom', value: 1 } ]}
     */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    toString

    将数组转换成字符串

    /**
     * toString: 将数组进行转换
     * 场景:想将一个数组变成字符串
     */
    const arrayTostring = (sourceArr) => {return sourceArr.toString()
    }
    console.log('toString:', arrayTostring([1, 2, 3, 4]))
    /*
    1,2,3,4
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    includes

    判断一个数组是否包含某个元素

    /**
     * includes: 包含
     * 场景:一个元素是否在数组中存在
     */
    const arrayIncludes = (arr, val) => {return arr.includes(val)
    }
    console.log('arrayIncludes:', arrayIncludes([1, 2, 3], 1)) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    indexOf

    获取一个元素的下标,如果没有就返回-1

    const arrayIndexOf = (arr, val) => {return arr.indexOf(val)
    }
    console.log(arrayIndexOf([1, 2, 3], 1)); 
    
    • 1
    • 2
    • 3

    join

    将一个数组以特殊字符进行拼接,变成一个字符串

    const arrayJoin = (arr, split) => {return arr.join(split)
    }
    console.log(arrayJoin([1,2,3], '-'))
    // join: 1-2-3 
    
    • 1
    • 2
    • 3
    • 4

    最后

    为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



    有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 相关阅读:
    如何看待区块链技术是数据存储的未来
    pytorch使用 基础解惑
    MyCat-web安装文档:安装Zookeeper、安装Mycat-web
    【视频】Python用LSTM长短期记忆神经网络对不稳定降雨量时间序列进行预测分析|数据分享...
    java实际项目反射、自定义注解的运用实现itext生成PDF的详细应用教程
    设计模式-责任链模式(Chain of Responsibility)
    bootstrap 布局大屏,pc,小屏,移动屏自适应
    rust学习——操作字符串、字符串转义、操作UTF8-字符串 (操作中文字符串)
    JAVA中使用CompletableFuture进行异步编程
    计算机毕业设计(附源码)python智慧校园防疫管理平台
  • 原文地址:https://blog.csdn.net/web2022050901/article/details/127787202