array.reduce的使用方法及常用场景

522 阅读4分钟

一、语法

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

_如果没有initialValue,则currentIndex从1开始
_

二、使用场景

1. 数组求和/求积

const arr = [2, 3, 6, 7]
// 求和,初始传0
const sum = arr.reduce((total, current) => {
  return total + current
}, 0) // 18
// 求积,初始传1
const product = arr.reduce((total, current) => {
  return total * current
}, 1) // 252

2. 数组扁平化

const arr1 = [1, 2, [2,3],[[4, 5]], 6]
const flatten = (arr) => {
  return arr.reduce((total, current) => {
    return total.concat(Array.isArray(current) ? flatten(current) : current)
  }, [])
}
// 当然,es6 的flat方法也可以
const flatArr = arr.flat() // 但flat会移除数组中的空项

3. 计算数组中元素出现的次数

let arr = [a, b, c, a]
const countObj = arr.reduce((pre, cur) => {
  if(cur in pre) {
    pre[cur]++
  } else {
    pre[cur] = 1
  }
  return pre
}, {})
// countObj: {a:2,b:1,c:1}

4. 数组去重

let arr = [1,2,3,1]
let newArr = arr.reduce((pre, cur) => {
  if(!pre.includes(cur)) {
    return pre.concat(cur)
  } else {
    return pre
  }
}, [])

5. 链式获取对象的属性值

const obj = {
  a: 1,
  b: {
    c: {
      d: 2
    }
  }
}
// 获取对象中的d值
const str = 'b.c.d'
const attrs = str.split('.')
const value = attrs.reduces((newObj, key) => newObj[key], obj) // 2

6. 根据属性把对象分类

const obj = [  {name: 'a', age: 15, gender: '男'},  {name: 'b', age: 16, gender: '男'},  {name: 'c', age: 15, gender: '女'},]
function groupBy(arr, property) {
  return arr.reduce((resObj, curObj) = > {
    const key = curObj[property]
    if(!resObj[key]) {
      resObj[key] = []
    } else {
      resObj[key].push(curObj)
    }
    return resObj
  }, {})
}
const ageGroup = groupBy(obj, 'age') 
// {15: [{name: 'a', age: 15, gender: '男'}, {name: 'c', age: 15, gender: '女'}], 16:{name: 'b', age: 16, gender: '男'}}

7.求最值

const arr = [12, 42, 12, 61, 73, 19]
const max = arr.reduce((prev, cur) => {
  return prev > cur ? prev : cur
})
console.log(max) // 73
// 当然Math.max也能实现
// Math.max(...arr)

8. 类型转换

数组转对象

const goods = [{type: 'plate', No: 123}, {type: 'bowl', No: 321}]
 
const result = goods.reduce((prev, cur) => {
  !prev[cur.No] && (prev[cur.No] = cur)
  return prev
}, {})
 
console.log(result ) // {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}

对象转数组

const obj = {123: {type: "plate", No: 123}, 321: {type: "bowl", No: 321}}
 
const result = Object.keys(obj).reduce((prev, cur) => {
  prev.push(obj[cur])
  return prev
}, [])
 
console.log(result) // [{type: "plate", No: 123}, {type: "bowl", No: 321}}]

三、高级用法

1. 对象中选取对应于给定键的键值对pick

使用Array.reduce()将筛选/选取的密钥转换回具有相应键值对的对象 (如果在 obj 中存在该键)。

const pick = (obj, arr) =>
  arr.reduce((acc, cur) => (cur in obj && (acc[cur] = obj[cur]), acc), {});
  
pick({ 'a': 1, 'b': '2', 'c': 3 }, ['a', 'c'])
// { 'a': 1, 'c': 3 }

2. 从数组中移除给定函数返回false的元素

使用Array.filter()查找返回 truthy 值的数组元素和Array.reduce()以使用Array.splice()删除元素。使用三参数 (func value, index, array调用函数)

const remove = (arr, func) =>
  Array.isArray(arr) ? 
    arr.filter(func).reduce((acc, val) => {
      arr.splice(arr.indexOf(val), 1); 
      return acc.concat(val);
    }, [])
    : [];
// remove([1, 2, 3, 4], n => n % 2 == 0) -> [2, 4]

3. 获取url参数

使用match()与适当的正则表达式来获取所有键值对,Array.reduce()可将它们映射并合并到单个对象中。将location.search作为要应用于当前url的参数传递.

const getURLParameters = url =>
  url.match(/([^?=&]+)(=([^&]*))/g).reduce(
    (a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a), {}
  );
getURLParameters('http://url.com/page?name=Adam&surname=Smith')
  // {name: 'Adam', surname: 'Smith'}

4. 执行从右向左/从左向右的函数组合。

从右向左

使用Array.reduce()执行从右向左的函数组合。最后一个 (最右边) 的函数可以接受一个或多个参数;其余的函数必须是一元的。

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

const add5 = x => x + 5
const multiply = (x, y) => x * y
const multiplyAndAdd5 = compose(add5, multiply)
multiplyAndAdd5(5, 2)
// 15

从左向右

使用Array.reduce()与扩展运算符 (...) 执行从左向右的函数组合。第一个 (最左边的) 函数可以接受一个或多个参数;其余的函数必须是一元的。

const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));

const add5 = x => x + 5
const multiply = (x, y) => x * y
const multiplyAndAdd5 = pipeFunctions(multiply, add5)
multiplyAndAdd5(5, 2)
// 15

5. 按顺序运行多个promise

const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());

const delay = (d) => new Promise(r => setTimeout(r, d))
const a = () => delay(1000)
const b = () => delay(2000)
runPromisesInSeries([a, b])
// executes each promise sequentially, taking a total of 3 seconds to complete

6. 生成一个数组, 包含斐波那契数列

创建一个指定长度的空数组, 初始化前两个值 (01)。使用Array.reduce()可将值添加到数组中, 方法是使用前两个值的总和, 但前两个数值除外。

const fibonacci = n =>
  Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []);
fibonacci(5)
// [0,1,1,2,3]

7. 从给定的键值对创建对象。

const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
objectFromPairs([['a',1],['b',2]])
// {a: 1, b: 2}

8. 生成字符串的所有字谜

使用递归。对于给定字符串中的每个字母, 为其其余字母创建所有部分字谜。使用Array.map()将字母与每个部分变位词组合在一起, 然后将Array.reduce()组合在一个数组中的所有字谜。基本情况为字符串length等于21.

const anagrams = str => {
  if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
  return str.split('').reduce((acc, letter, i) =>
    acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []);
};
anagrams('abc')
// ['abc','acb','bac','bca','cab','cba']