javascript 遍历函数的进阶用法、组合用法
javascript 遍历函数的进阶用法、组合用法
前言
上一个文章 《js 数组、对象常见的遍历用法大全》 是为大家介绍了js的常用遍历用法;
本章的话是为大家讲解一下,遍历中的进阶使用和组合使用。结合了本人实战项目经验中所遇到的,为大家详细讲解一下关于通过遍历来数据处理。关于本章里面提到的一些函数,大家可以去看我上个文章,在这边就不另做解释了。
数组去重
关于数组去重,日常开发应该也是挺常见的一种数据处理场景,
例如:
1、增加用户权限时,需要对现有权限的去重
2、对项目增加标签时,需要对现有的标签的去重
3、类似以上等等这类型的场景,经常会遇到
1、对象型数组去重
假设有数据如下:
const data1 = [{ id: 1, name: '数据1' },{ id: 2, name: '数据2' },{ id: 3, name: '数据3' }
];
const data2= [{ id: 1, name: '数据1' },{ id: 2, name: '数据2' }
];
思路1:先合并数组,再去重
const data = [...data1,...data2];
用法1: Array + for形式的遍历 + some
const list = [];
data.forEach(item => {//判断是否已经存在list数组const isHas = list.some(curr => curr.id === item.id);if (!isHas) {list.push(item);}
})
console.log(list)
//[ { id: 1, name: "数据1" },
// { id: 2, name: "数据2" },
// { id: 3, name: "数据3" } ]
解析:
1、根据id的唯一性,使用some来判断每次遍历出来的数据,是否存在我们新的数组里面
用法2:Object + for形式的遍历 + Object.keys + Array.map
const obj = {};
data.forEach(item => {// 判断obj对象是否已存在[item.id]属性if (!obj[item.id]) {obj[item.id] = item;}
});
console.log(obj);
// { 1: { id: 1, name: "数据1" },
// 2: { id: 2, name: "数据2" },
// 3: { id: 3, name: "数据3" } }
Object.keys(obj).map(key => {return obj[key];
});
//[ { id: 1, name: "数据1" },
// { id: 2, name: "数据2" },
// { id: 3, name: "数据3" } ]
解析:
1、根据对象属性的唯一性,我们可以把数据唯一性的id作为obj对象的属性,由此来过滤掉id重复的数据
2、通过Object.keys和map再次把对象给转化成数组
用法3:Map结构 + for形式的遍历 + Map.values
const list = new Map();
data.forEach(item => {// 判断map对象是否已存在item.id的key值if (!list.has(item.id)) {list.set(item.id, item)}
});[...list.values()];
//[ { id: 1, name: "数据1" },
// { id: 2, name: "数据2" },
// { id: 3, name: "数据3" } ]
解析
1、根据Map结构(值-值)的结构,我们可以把数据的id作为Map的key值,然后根据Map原型提供的has函数,用来过滤掉重复的数据
2、最后通过Map原型提供的values函数,直接转化成数组
说明:
1、关于Map的values函数返回的是键值的遍历器,而不是像Object.values() 一样返回的是一个值数组,Map返回的这个遍历器可用于for…of遍历写法,详情请看下面链接
2、关于ES6的Map数据结构,大家可以看一下ECMAScript 6 入门-Map数据结构,关于Map结构,我这里就不做另外的说明了
思路2:data1和data2进行对比,过滤重复数据
data2.forEach(item => {//判断是否已经存在data1数组const isHas = data1.some(curr => curr.id === item.id);if (!isHas) {data1.push(item);}
})
console.log(data1)
//[ { id: 1, name: "数据1" },
// { id: 2, name: "数据2" },
// { id: 3, name: "数据3" } ]
解析:
1、其实使用到的函数基本是跟上面的差不多,只不过思路不太一样而已,具体我就不做另外用法的举例了,大家有空的可以自己去试试
<=================================================================>
其他补充说明:
其实对于上面的做法,可以有很多其他函数可以代替的,比如:
1、forEach的替换成 for…of 、for,推荐用forEach、for…of,原始写的for循环还是比较麻烦
//forEach
data.forEach(item => { });//for...of
for (let item of data) { }//常规写法
for (let { id, name } of data) { }//解构写法//for
for (let i = 0; i < data.length; i++) {const item = data[i];
}
2、解法1里面的some可以替换成every、find、filter、findIndex
//查询是否存在id相同的数据
const isHas = list.find(curr => curr.id === item.id);
//返回跟匹配到的数据所在数组的下标
const hadIndex = list.findIndex(curr => curr.id === item.id);
//查询是否全部数据的id都跟当前数据id不相同
const isNotHas = list.every(curr => curr.id !== item.id);
//返回id相同的数据数组
const hasList = list.filter(curr => curr.id === item.id);if (!isHas || hadIndex !== -1 || isNotHas || hasList.length == 0) {list.push(item);
}
2、常量型数组去重
假设有下面数据
const data = [1, 2, 3, 4, 5, 6, 4, 3, 2, 7];
用法1: Set数据结构
const set = new Set(data);
[...set]//[1, 2, 3, 4, 5, 6, 7]
解析
1、根据ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值,因此可以用来过滤掉重复的常量数据
说明:
1、关于ES6的Set数据结构,大家可以看一下ECMAScript 6 入门-Set数据结构,关于Set结构,我这里就不做另外的说明了
其他用法
其实对于常量型的数组,对象型的用法,常量型的基本都适用。比如:
const list = [];
data.forEach(item => {//判断是否已经存在list数组const isHas = list.some(value => value === item);if (!isHas) {list.push(item);}
})
console.log(list)//[1, 2, 3, 4, 5, 6, 7]
补充说明:
1、对于常量型数组,some可替换的不止every、find、filter、findIndex,还有indexOf、includes,一般对于常量型数组,还是推荐使用indexOf、includes,比如:
//查询list数组是否存在item,没有则返回-1
const isHas = list.indexOf(item) !== -1;//查询list数组是否存在item,返回一个布尔值
const isHas = list.includes(item);if (!isHas) {list.push(item);
}
数据格式转换、数组筛选、字段提取
关于数据格式转换、字段提取,日常开发是非常、非常、常见的一种数据处理场景,比如:
1、对于前端控件要求的数据结构跟后台返回的数据结构不一致
2、发送请求参数的时候,只需要获取到数据的某个字段的集合,比如id的集合
3、以上出现的场景几乎是在每个项目中100%会出现的
场景1-数据格式转换
有一个下拉框用来展示所有的产品,前端控件要求的数据结构是:
const options = [{ label: '产品1', value: '1' }
];
而后台给我们返回的数据结构是:
const reuslt = [{ name: '产品1', id: '1' }
];
这时候我们就需要进行数据结构的转化了
转化方式1
const options = [];
result.forEach(({ id, name }) => {options.push({label: item.name,value: item.id})
});
console.log(options);
//[
// { label: '产品1', value: '1' },
//];
解析:
1、使用forEach函数遍历result数组,再用ES6结构方式提取对应字段
2、遍历result数组,重新组装一个新对象,然后push到一个新数组options里面
转化方式2
const options = result.map(({ id, name }) => {return { label: name, value: id };
});
console.log(options);
//[
// { label: '产品1', value: '1' },
//];
解析:
1、使用map函数遍历result数组,再用ES6结构方式提取对应字段
2、遍历result数组,return一个重新组装一个的对象,最终返回新的数组
2种方式对比:
1、map函数对比forEach函数少了一个push的操作
2、map函数对比forEach函数看起来更加简洁
总结: 推荐使用map函数进行数组的数据结构转化
场景2-数组过滤筛选、字段提取
根据场景1来说,比如说:下来选择框正常是有2种,单选、多选,然后往往我们发送请求的时候,往往其实只需要发送单个id或者是一个id数组,那就可以用下面的做法了:
const options = [{ label: '产品1', value: '1', checked: true },{ label: '产品2', value: '2', checked: false },{ label: '产品3', value: '3', checked: false },{ label: '产品4', value: '4', checked: false },
];// 单选情况下
const checked = options.find(item => item.checked)['id'];
const id = checked ? checked.id : '';
console.log(id);//1//多选情况下获取选中的选项
const checkedList = options.filter(item => item.checked);
// 获取选中项的id集合
const idList = checkedList.map(item => item.id);
console.log(idList);//[1]
解析:
1、单选情况下,用find函数直接获取选中的选项的id
2、单选情况下,如果下拉框没有选中的话,find函数返回的是undefined,则获取id的时候需要先判断find函数返回数据
3、多选情况下,用filter函数获取多个选中的选项,再用map直接return选中选项的id,组成id数组
数据结构转换之进阶《递归Tree树形结构》
在实战中会出现树形组件,而树形组件需要的就是树形的数据结构了,而后台不一定返回给你的就是树形结构了,这时候就是需要我们自己进行转化了。
假设后台返回的数据结构是:
const data = [{ id: '1', name: '产品1', parentId: '' },{ id: '2', name: '产品1-1', parentId: '1' },{ id: '3', name: '产品1-2', parentId: '1' },{ id: '4', name: '产品4', parentId: '' },{ id: '5', name: '产品4-1', parentId: '4' }
];
这个时候就需要用到我们的filter、map函数和递归函数了
const getTreeData = (dataList, parentId) => {// 获取所有匹配到parentId的数据const list = dataList.filter(item => item.parentId === parentId);// 递归再次返回带children,此处用了ES6解构写法return list.map(item => ({ ...item, children: getTreeData(dataList, item.id) }));
}
const treeData = getTreeData(data, '');
console.log(treeData);
// [
// {
// id: '1', name: '产品1', parentId: '',
// children: [
// { id: '2', name: '产品1-1', parentId: '1', children: [] },
// { id: '3', name: '产品1-2', parentId: '1', children: [] }
// ]
// },
// {
// id: '4', name: '产品2', parentId: '',
// children: [
// { id: '5', name: '产品2-1', parentId: '4', children: [] }
// ]
// },
// ]
解析:
1、getTreeData 递归函数接收2个参数,1是后台返回给我们的data数组,2是进行数据匹配的一个条件parentId。
2、一开始第二个参数传了 ‘’ 空字符进去,就是循环遍历顶级的父类。
3、第一次第一步filter的结果得出的顶级父类数组就是:
[{ id: '1', name: '产品1', parentId: '' },{ id: '4', name: '产品2', parentId: '' }
]
4、第一次第二步map函数就是往2个顶级父类中加多了children属性,而children属性值就是再一次递归函数,进入第二次的递归过程。
5、递归终结的条件就是map函数对空数组不会遍历,也就是当data数组中匹配不到传进来parentId的时候,filter返回的结果就是空数组了,也是递归的终结
总结:
以上基本就是实战中,我经常遇到的几种场景和处理方式,写出来也是自己的一次总结和分享
另外以上也是可以用其他方式函数代替,只不过既然有更简洁的函数提供给我们使用,干嘛还要去写那么多麻烦的函数咧?
最后其实还有个几乎万能的函数《reduce》,在这里我没写出来,因为它可以当上面很多函数来用的,关于这个我后续会单独写个reduce函数的续章
- PHP调用API接口
- matlab stats里的f值,MATLAB 回归分析regress,nlinfit,stepwise函数
- platform
- 非诚勿扰24灯全灭php,收二手货小伙上非诚勿扰,24盏灯全灭还遭羞辱,最后才知道是收二手豪车身价上亿...
- Lion蠕虫
- Maven的三种packaging方式(pom、jar、war)
- 再谈Revit二次开发的可靠性和前景
- 如何下载并使用HTK工具包
- CA6140车床拨叉工艺及铣30X80面夹具设计
- 函数popen()
- 2009奥巴马的秋季开学演讲稿
- [机器学习算法]支持向量机SVM原理简介
- SVM算法实现(一)
- html弹出div弹窗
- pip升级报错:def read(rel
- STM32外部中断干扰解决方案
- C语言程序设计之通讯录