php array_reduce 降维,数组的reduce方法的应用场景及相关面试题

reduce 最大的作用:从一个数组得到一个值,对数组中的每个元素执行 reducer 函数(升序执行)

reduce 打开相当于:

```js

// 这里为了更加方便理解 统一加上initValue

[x1, x2, x3].reduce(f,initValue)

// f起码是两个参数

f(f(f(initValue,x1), x2), x3);

```

写reduce的核心其实很明了:

- 找到`initValue`

- 找到`f`,`f`始终有两个参数 `acc`和`item`

这样,能解决大部分的问题了,而且新手也有了线索,容易写reduce。

以下既是应用场景,也是很好练习的例子。

## 累加数组所有值

```js

const sumFn = (acc, item) => acc + item;

const sum = [1, 2, 3, 4].reduce(sumFn, 0);

// 6

console.log(sum);

```

## 累加对象数组里的值

```js

const sumFn = (acc, item) => acc + item.x;

const sum = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }].reduce(sumFn, 0);

// 6

console.log(sum);

```

## 降维数组,二维变成一维

```js

// 之所以用concat不用push 是concat返回合并后的数组,而push返回数组长度

const flat = (acc, item) => acc.concat(item);

const res = [

[0, 1],

[2, 3],

[4, 5]

].reduce(flat, []);

// [0, 1, 2, 3, 4, 5]

console.log(res);

```

## 扁平化任意维数组 --- 面试常考

```js

const flat = (acc, item) => acc.concat(item);

const res = [ 0, 1, [2, 3, [4, 5]] ].reduce(flat, []);

// [0, 1, 2, 3, 4, 5]

console.log(res)

// 这里更新了,稍微抽象出一个函数,当前项是数组的话 flatten(item) 不是数组的话用concat合并下

const flatten = arr =>

arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);

```

## 计算数组中每个元素出现的次数 --- 面试常考

```js

const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];

const getCount = (obj, key) => {

key in obj || (obj[key] = 0);

obj[key]++;

return obj;

};

const res = names.reduce(getCount, {});

// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

console.log(res);

```

## 按属性对 object 分类 --- 面试常考

```js

const people = [

{ id: 1, name: "Alice", age: 21 },

{ id: 2, name: "Max", age: 20 },

{ id: 3, name: "Jane", age: 20 }

];

const getType = (obj, item) => {

const key = item.age;

key in obj || (obj[key] = []);

obj[key].push(item);

return obj;

};

const res = people.reduce(getType, {});

// { 20: [ { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ], 21: [{ name: 'Alice', age: 21 }] }

console.log(res);

```

这里可以稍微升级下,可以对任意字段进行分类

```js

const classifyArray = (arr, keyClassified) => {

const getType = (obj, item) => {

const key = item[keyClassified];

key in obj || (obj[key] = []);

obj[key].push(item);

return obj;

};

return arr.reduce(getType, {});

};

const people = [

{ id: 1, name: "Alice", age: 21, gender: 0 },

{ id: 2, name: "Max", age: 20, gender: 1 },

{ id: 3, name: "Jane", age: 20, gender: 0 }

];

// { '0': [ { id: 1, name: 'Alice', age: 21, gender: 0 }, { id: 3, name: 'Jane', age: 20, gender: 0 } ], '1': [ { id: 2, name: 'Max', age: 20, gender: 1 } ] }

console.log(classifyArray(people, "gender"));

```

## 使用扩展运算符处理对象数组中的数组

```js

// 合并所有的书

var friends = [

{

name: "Anna",

books: ["Bible", "Harry Potter"],

age: 21

},

{

name: "Bob",

books: ["War and peace", "Romeo and Juliet"],

age: 26

},

{

name: "Alice",

books: ["The Lord of the Rings", "The Shining"],

age: 18

}

];

const sumArr = (oldArr, item) => {

return [...oldArr, ...item.books];

};

const res = friends.reduce(sumArr, []);

// [ 'Bible', 'Harry Potter', 'War and peace', 'Romeo and Juliet', 'The Lord of the Rings', 'The Shining' ]

console.log(res);

```

## 数组去重 --- 面试常考

```js

var myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];

const addArr = (acc, item) => (acc.includes(item) ? acc : [...acc, item]);

const res = myArray.reduce(addArr, []);

// [ 'a', 'b', 'c', 'e', 'd' ]

console.log(res);

```

当然还有更简单粗暴的法子

```js

const unique = arr => Array.from(new Set(arr));

// [ 'a', 'b', 'c', 'e', 'd' ]

console.log(

unique(["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"])

);

```

## 按顺序运行 promise --- 面试常考

```js

// promise function 1

function p1(a) {

return new Promise(resolve => {

resolve(a * 5);

});

}

// !!这个是普通函数哟

function f(a) {

return a * 2;

}

// promise function 2

function p2(a) {

return new Promise(resolve => {

resolve(a - 2);

});

}

const arr = [p1, f, p2];

const runOrderly = (acc, item) => acc.then(item);

const res = arr.reduce(runOrderly, Promise.resolve(10));

// 98

console.log(res);

```

再抽象下写个通用的顺序执行的函数

```js

const runOrderly = (arr, initValue) =>

arr.reduce((acc, item) => acc.then(item), Promise.resolve(initValue));

```

## 生成组合函数compose --- 面试常考

举个例子,就明白啥是组合函数了

```js

const sum = (a, b) => a + b;

const len = str => str.length;

const addCurrency = str => "$" + str;

// 现在想要先 将两字符求和,然后求长度,再然后再长度前加个$,这就是一个新的函数,是已知函数的组合

const newFn = (a, b) => addCurrency(len(sum(a, b)));

console.log(newFn("xyz", "abc"));

```

但是不想像上面那样,地狱式嵌套的生成`newFn`,希望能 `compose(addCurrency,len,sum)`,这里可以试试`reduceRight`

```js

// compose(addCurrency,len,sum)

const compose = (...fns) => {

return (...args) => {

// 这里因为最后一个函数是两个参数,和别的不一样,所以这边单独把它扔出来,这样其他的都符合fn(acc)

const lastFn = fns.pop();

let initValue = lastFn(...args);

const f = (acc, fn) => fn(acc);

return fns.reduceRight(f, initValue);

};

};

const newFn = compose(addCurrency, len, sum);

console.log(newFn("xyz", "abc"));

// 用箭头函数简化

const compose = (...fns) => (...args) =>

fns.reduceRight((acc, fn) => fn(acc), fns.pop()(...args));

```

当然如果思维再厉害点,也可以用reduce

```js

// compose(addCurrency,len,sum)

const compose = (...fns) =>

fns.reduce((acc, cur) => (...args) => acc(cur(...args)));

```

这个法子很不容易想,但是假设现在只组合两个函数,len和sum

- compose就相当于`(...args) => len(sum(...args))`

- 而用reduce的话相当于`(...args)=>[len,sum].reduce((acc,curFn)=>(...args) => acc(curFn(...args)))`

- 将`[len,sum]`换成别的数组也一样

- 我这也是马后炮啦,下次我自己写估计又只会reduceRight

## reduce 的坑

- 空数组不能 reduce

- `reduce(f)`没 initValue 的话,f 的第一次参数是`arr[0] arr[1] 1 arr`

- `reduce(f,initValue)`有 initValue 的话,f 的第一次参数是`initValue arr[0] 0 arr`

- `f`必须至少有两个参数,总共四个参数`acc(累计器) cur(当前值) curIndex (当前索引) src (源数组)`

可读性更高的话,可以传入 initValue,这样每次的操作都是一致的不容易出错。本文也是如此操作

## 引用

- [mdn 的 map](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map)

- [廖雪峰的map解析](https://www.liaoxuefeng.com/wiki/1022910821149312/1024322552460832)

- [MapReduce: Simplified Data Processing on Large Clusters 原文](http://research.google.com/archive/mapreduce.html)

- [MapReduce翻译文](https://www.cnblogs.com/fuzhe1989/p/3413457.html)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值