map/reduce/filter用法

这三种方法都定义在Array中,主要作用是遍历和处理数组元素,平时用的不多,但并不代表不好用,因为是Array原型下的方法,所以任何数组对象都可以调用。

map

map方法会循环遍历数组,对每一个元素进行函数式的处理。注意:原来数组不会发生变化,会返回一个新的数组。

比如有这样一个函数f(x)=x*x,用map实现如下:

1
2
3
4
5
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

arr.map(function(x){
return x*x;
});

运行结果:[1, 4, 9, 16, 25, 36, 49, 64, 81]

此功能完全可以自己封装一个函数来完成,但是不容易一眼就看出是函数作用于元素,也就是可读性不高。map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以f(x)=x*x,还可以计算任意复杂的函数。

此方法还可以转换数组元素类型:

1
2
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.map(String);

运行结果: [“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”]

把字符串转化为数组:

1
2
3
Array.prototype.map.call("String", function(x) {
return x;
});

运行结果:[“S”, “t”, “r”, “i”, “n”, “g”]

map和forEach异同点:

  • map和forEach非常相似,都是用来遍历数组中的每一项,并不对原数组进行修改;
  • 区别在于map支持return返回(返回的是进行修改后的原数组的克隆版本),而forEach不支持;
  • 两者都支持第二个参数,这第二个参数的意思是把匿名回调函数中的this进行修改。
1
2
3
4
var obj = {key:12};
[1,2,3].map(function(x){
return x*this.key;
},obj);

运行结果:[12, 24, 36]


reduce

直接上代码,看看运行结果:

1
2
3
4
5
6
var  arr = [1, 2, 3, 4, 5];

sum = arr.reduce(function(res, cur, index, self) {
console.log(res, cur, index);
return res + cur;
});

运行结果:

1
2
3
4
5
1 2 1
3 3 2
6 4 3
10 5 4
15 // reduce回调函数返回值

reduce回调函数中有四个参数:

res: 第一项的值或者上一次叠加的结果值
cur: 当前会参与叠加的项
index:当前参与叠加项的索引
self: 数组本身

从这个例子可以看出reduce能实现累加运算,那么能进行累积运算吗,当然可以,这个自己下去试试就知道了。

给reduce参数添加第二个参数,通过打印我发现,reduce会遍历5次。

1
2
3
4
5
6
var  arr = [1, 2, 3, 4, 5];

sum = arr.reduce(function(res, cur, index, self) {
console.log(res, cur, index);
return res + cur;
}, 0);

运行结果:

1
2
3
4
5
6
0 1 0
1 2 1
3 3 2
6 4 3
10 5 4
15 // reduce回调函数返回值

这第二个参数就是设置 res 的数据类型和初始值,设置为0,就表示 res 的初始值为number类型,值为0,因此,reduce的最终结果也会是number类型。

那么,如何计算一串字符串中每个字母出现的次数呢?

1
2
3
4
5
6
var str = 'abcdaabce';

str.split('').reduce(function(res, cur) {
res[cur] ? res[cur] ++ : res[cur] = 1
return res;
}, {});

运行结果:{a: 3, b: 2, c: 2, d: 1, e: 1}

由于可以通过第二个参数设置叠加结果的类型和初始值,我们可以灵活的运用它来进行各种各样的类型转换,比如将数组按照一定规则转换为对象,也可以将一种形式的数组转换为另一种形式的数组。

这种特性使得reduce在实际开发中大有可为!但是需要注意点,在IE9以下的浏览器中,并不支持该方法!

应用:

  1. reduce还可以去除字符串中的重复字符,类似于数组去重
1
2
3
4
5
6
var str = 'abc12122adc';

str.split('').reduce(function(res, cur, i, self) {
self.indexOf(cur) === i ? res.push(cur) : null;
return res;
}, []);
  1. 递归方法将多维数组转一维:
1
2
3
4
5
6
7
var arr = [1,[2,3,4],[[5,6],7],8];
const flatten = arr => arr.reduce((res, cur) =>
res.concat(Array.isArray(cur) ? flatten(cur) : cur), []
);

// 注意:ES9中将新增flat方法,flat()方法会递归到指定深度将所有子数组连接,并返回一个新数组。例:
arr.flat(Infinity) // [1,2,3,4,5,6,7,8,]
  1. 将树形json结构扁平化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// reduce 第二个参数为当前参与计算的项,也就是数组的某个元素 (Object)
// 参数里的 { name } 为es6中解构赋值的特性,下面的{ name } 相当于 { name: name }
function flatten(data) {
return data.reduce( (arr, {name, children = []}) =>
arr.concat([{name}], flatten(children)), []
);
}

// Result:
[
{name: "Juery"},
{name: "LiLy"},
{name: "Shyane"},
{name: "Taylor"},
{name: "Choy"},
{name: "Fusion"},
{name: "Simon"},
{name: "Sunshine"},
{name: "Meton"},
{name: "Adele"}
]

Json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[
{
name: "Juery",
children: [
{
name: "LiLy",
children: [
{
name: "Shyane"
},{
name: "Taylor"
}
]
},{
name: "Choy"
}
]
},{
name: "Fusion"
},{
name: "Simon",
children: [
{
name: "Sunshine",
children: [
{
name: "Meton"
},{
name: "Adele"
}
]
}
]
}
]

filter

顾名思义,它用于过滤某些元素,返回剩下的元素。和map()有点类似,filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

1
2
3
4
5
6
var s = "i love you very very very much,do you love me?";

s.slice(0,-1).split(/[\s\,]+/).filter(function (ele, index, self) {
// slice去掉末尾'?'
return self.indexOf(ele) === index;
});

运行结果:[“i”, “love”, “you”, “very”, “much”, “do”, “me”]

ele: 当前遍历的数组元素
index:当前遍历元素的位置
self: 数组本身

由此可看出filterreduce的去重效果是一样的,不过这种方法更简洁,逻辑更清晰。

其实还有更简便的方法:

1
2
Array.from(new Set(arr))    //ES6
Array.from(arr) //注意: 此数组方法可将伪数组转真数组

参考文档:Array方法

✧(≖‿≖)✧ 谢谢您的支持 (●'◡'●)ノ♥
0%