去重的方法
去重是开发中经常会碰到的一个热点问题,不过目前项目中碰到的情况都是后台接口使用SQL去重,简单高效,基本不会让前端处理去重。
当然,这并不是说前端去重就没有必要了,依然需要会熟练使用。下面主要介绍几种常见的数组去重的方法。
1. hasOwnProperty()+ filter + 对象属性不能重复
所有的都去重了
hasOwnProperty()
方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
let obj = {}
arr.filter(item => obj.hasOwnProperty(typeOf item + item) ? false : obj[typeOf item + item] = true)
}
2. ES6 set + 解构赋值(常用)
对象没有去重
-
最简单,最常用的方法,没有之一。
-
Set的一个最大的特点就是数据不重复。
-
Set函数可以接受一个数组(或类数组对象)作为参数来初始化。
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return [...new Set(arr)]
}
3. ES6 set + Array.from
对象没有去重
Array.from()
方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return Array.from(new Set(arr))
}
4. includes
对象没有去重
-
includes()
方法判断数组是否包含一个指定的值,包含则返回 true,否则返回false。 -
[NaN].includes(NaN)
为 true
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
let res = []
arr.forEach(item => {
if (!res.includes(item)) {
res.push(item)
}
})
return res
}
5. reduce + includes
对象没有去重
-
reduce()
方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。 -
reduce()
包含两个参数:回调函数,传给total的初始值 -
[NaN].includes(NaN)
为 true
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return arr.reduce((total, cur) => total.includes(cur) ? pre : [...pre, cur], [])
}
6. 利用for嵌套for,然后splice去重(ES5中最常用)
NaN和对象没有去重
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
for (let i = 0; i < arr.length - 1; i++) {
for (let j = arr.length; j > i; j--) {
if (arr[i] === arr[j]) arr.splice(j, 1)
}
}
return arr
}
j 从最后向前找比较好,因为:数组
splice
一个元素后,后面的元素索引都向前一位。
7. indexOf
NaN和对象没有去重
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
let res = []
arr.forEach(item => {
if (res,indexOf(item) === -1) {
res.push(item)
}
})
return res
}
8. indexOf + filter
NaN和对象没有去重
filter()
方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return arr.filter((item, index, arr) => arr.indexOf(item) === index)
}
unique([NaN, NaN])
返回[]
,因为arr.indexOf(NaN)
都为 -1
9. sort()
NaN和对象没有去重
-
利用
sort()
排序方法,然后根据排序后的结果进行遍历及相邻元素比对。 -
sort()
方法用原地算法对数组的元素进行排序。详细请移步:Array.prototype.sort()
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
arr = arr.sort()
let res = arr[0] && [arr[0]]
for (let i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
res.push(arr[i])
}
}
return res
}
总结
按照去重的结果来看
-
所有都去重:
- 1.hasOwnProperty()+ filter + 对象属性不能重复
-
对象没有去重
- 2.ES6 set + 解构赋值去重(常用)
- 3.ES6 set 与 Array.from 去重
- 4.includes
- 5.reduce + includes
-
NaN和对象没有去重
- 6.利用for嵌套for,然后splice去重(ES5中最常用)
- 7.indexOf
- 8.indexOf + filter
- 9.sort()
按照去重的方式来看
除了 6.利用for嵌套for,然后splice去重(ES5中最常用) 在原来的数组上修改,其他的都是创建一个新数组,然后通过各种方法将不重复的元素追加到新的数组中,返回新数组。
扁平化的方法
数组扁平化即将一个嵌套多层的数组array(嵌套可以是任意层数)转换为只有一层的数组,如将数组[1,[2,[3,[4,5]]]]转换为[1,2,3,4,5]。
最直接的数组扁平化方案是使用 Array.prototype.flat()
方法(兼容性差),其次是通过遍历数组元素递归实现每一层的数组拉平。
1. ES6 flat
-
语法:
res = arr.flat([depth])
-
说明:
- depth为指定要提取嵌套数组的结构深度,默认值为1。
- 参数
depth <= 0
时返回原数组; - 参数depth为
Infinity
关键字时,无论多少层嵌套,都会转为一维数组, flat()
方法会移除数组中的空项,即原数组有空位,会跳过这个空位。
2. reduce
- 遍历数组每一项,若值为数组则递归遍历,否则concat。
function flatten(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return arr.reduce((total, cur) => total.concat(Array.isArray(cur) ? flatten(cur) : cur), [])
}
3. toString (join) & split
-
如果数组的项全为数字,可以使用
join(),toString()
把数组转换成字符串,这个过程会把[] 都去掉,然后再调用split()
方法转换成数组。数组的每一项都执行了
toString()
,所以数字也变成了字符串。
function flatten(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
return arr.toString().split(',').map(item => +item)
---------------------------------------------------
return arr.join(',').split(',').map(item => +item)
}
字符串就不需要
map()
了。
4. 递归(也可以用栈)
和 reduce
一样,用 reduce
比这个简洁。
function flatten(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('arr must be an Array')
}
let res = []
arr.forEach(item => res.concat(Array.isArray(item) ? flatten(item) : item))
return res
}
总结
除直接使用 flat()
,其他的方法都是:遍历数组元素递归实现每一层的数组拉平。
tree扁平化
1. 递归实现
遍历tree
,每一项加进结果集,如果有children
且长度不为0,则递归遍历。
这里需要用解构赋值将每个节点的
children
属性去除。
function treeToArray(tree) {
let res = []
for (const item of tree) {
const { children, ...i } = item
if (children && children.length) {
res = res.concat(treeToArray(children))
}
res.push(i)
}
return res
}
2. reduce
思路同递归实现,简洁一点
function treeToArray(tree) {
return tree.reduce((res, item) => {
const { children, ...i } = item
return res.concat(i, children && children.length ? treeToArray(children) : [])
}, [])
}