-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
js数组高级函数之reduce() #210
Comments
初识reduce()概念
最简reduceconst array1 = [1, 2, 3, 4];
const reducer = (acc, cur) => acc + cur;
// 1+2+3+4 => 10
console.log(array1.reduce(reducer));
// 5+1+2+3+4 => 15
console.log(array1.reduce(reducer, 5)); reducer函数的4个参数
|
语法arr.reduce(callback( accumulator, currentValue[, index[, array]] ), [, initialValue]) 参数
返回值由reduce产生的单个值。 如何理解initialValue和acc,cur的关系?如果指定initialValue的话,index从 0 开始;否则从 1 开始。 看了下面的demo就明白了: /* 没有指定initialValue。
* 0未出现,说明没有指定initialValue时,跳过第一次遍历。
* acc初始值为'foo',说明跳过第一次遍历后,acc将首次的cur作为自己的值。
*/
var arr = ['foo','bar'];
arr.reduce((acc,cur,idx,iarr)=>{
console.log(acc, cur, idx);
return acc+cur;
})
VM209:3 foo bar 1
"foobar" /* 有指定initialValue。
* 0出现,首次acc值为baz。说明有指定initialValue时,第一次遍历将initialValue作为acc的初始值。
* 1时acc值为'bazfoo',说明经过第一次遍历后,acc将上一次计算的acc+cur作为新的acc传递进来。
*/
var arr = ['foo','bar'];
arr.reduce((acc,cur,idx,iarr)=>{
console.log(acc, cur, idx);
return acc+cur;
},'baz')
VM211:3 baz foo 0
VM211:3 bazfoo bar 1
"bazfoobar" 没指定initialValue的空数组reduce,抛什么错?[].reduce(()=>{},)
TypeError: Reduce of empty array with no initial value |
实际用途对一个数组中的元素求和let total = [ 0, 1, 2, 3 ].reduce(
( accumulator, currentValue ) => accumulator + currentValue,
0
)
// total为6 对对象中的数字求和如果想让数组中的每个元素都进入迭代,必须设置一个initialValue。 let initialValue = 0
let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(
(accumulator, currentValue) => accumulator + currentValue.x
, initialValue
)
console.log(sum) // logs 6 如果想返回对象的形式呢? let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(
(accumulator, currentValue) => ({x: accumulator.x + currentValue.x})
)
console.log(sum) // logs {x: 6} 抹平数组中的数组let flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( accumulator, currentValue ) => accumulator.concat(currentValue),
[]
) // [0, 1, 2, 3, 4, 5, 6] 统计数组中元素出现的次数Object形式let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
let countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++
}
else {
allNames[name] = 1
}
return allNames
}, {})
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 } Map形式let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
let countedNames = names.reduce(function (map, name) {
if (map.has(name)) {
map.set(name , map.get(name)+1);
}
else {
map.set(name, 1);
}
return map
}, new Map())
// countedNames is:
// {"Alice" => 2, "Bob" => 1, "Tiff" => 1, "Bruce" => 1} 通过属性分组对象之前的热力图表格数据其实可以使用reduce计算,可以简化大量的数组forEach,map和filter等等数组遍历。 let people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
return objectArray.reduce((acc, obj)=>{
let key = obj[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
let groupedPeople = groupBy(people, 'age')
// groupedPeople is:
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// } 使用...和初始值绑定对象数组中包含的数组抽取数组中的对象中的数组。 // friends - an array of objects
// where object field "books" is a list of favorite books
let 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
}]
// allbooks - list which will contain all friends' books +
// additional list contained in initialValue
let allbooks = friends.reduce(function(accumulator, currentValue) {
return [...accumulator, ...currentValue.books]
}, ['Alphabet'])
// allbooks = [
// 'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
// 'Romeo and Juliet', 'The Lord of the Rings',
// 'The Shining'
// ] 移除数组中的重复项reduce可以实现类似Set&&Array.form()的方式去重。 Set&&Array.form()去重let orderedArray = Array.from(new Set(myArray)) reduce去重let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myOrderedArray = myArray.reduce(function (accumulator, currentValue) {
if (accumulator.indexOf(currentValue) === -1) {
accumulator.push(currentValue)
}
return accumulator
}, [])
console.log(myOrderedArray) 使用.reduce()移除.filter()和.map()使用map和filter会遍历数组两次,可以用reduce一次遍历即可。 map,filter普通写法const numbers = [-5, 6, 2, 0,];
const doubledPositiveNumbers = numbers.map((num)=>{if(num>0) return num*2}).filter((num)=>num)
console.log(doubledPositiveNumbers); // [12, 4] reduce写法const numbers = [-5, 6, 2, 0,];
const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
if (currentValue > 0) {
const doubled = currentValue * 2;
accumulator.push(doubled);
}
return accumulator;
}, []);
console.log(doubledPositiveNumbers); // [12, 4] 按照序列运行Promise链式运行Promise,可用于顺序异步队列。 /**
* Runs promises from array of functions that can return promises
* in chained manner
*
* @param {array} arr - promise arr
* @return {Object} promise object
*/
function runPromiseInSequence(arr, input) {
return arr.reduce(
(promiseChain, currentFunction) => promiseChain.then(currentFunction),
Promise.resolve(input)
)
}
// promise function 1
function p1(a) {
return new Promise((resolve, reject) => {
resolve(a * 5)
})
}
// promise function 2
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2)
})
}
// function 3 - will be wrapped in a resolved promise by .then()
function f3(a) {
return a * 3
}
// promise function 4
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4)
})
}
const promiseArr = [p1, p2, f3, p4]
runPromiseInSequence(promiseArr, 10)
.then(console.log) // 1200 管道式组合函数pipeline式运行函数,可用于流水线性任务。 // Building-blocks to use for composition
const double = x => x + x
const triple = x => 3 * x
const quadruple = x => 4 * x
// Function composition enabling pipe functionality
const pipe = (...functions) => input => functions.reduce(
(acc, fn) => fn(acc),
input
)
// Composed functions for multiplication of specific values
const multiply6 = pipe(double, triple)
const multiply9 = pipe(triple, triple)
const multiply16 = pipe(quadruple, quadruple)
const multiply24 = pipe(double, triple, quadruple)
// Usage
multiply6(6) // 36
multiply9(9) // 81
multiply16(16) // 256
multiply24(10) // 240 使用reduce写map这个写法很谜。 if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function(callback, thisArg) {
return this.reduce(function(mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array)
return mappedArray
}, [])
}
}
[1, 2, , 3].mapUsingReduce(
(currentValue, index, array) => currentValue + index + array.length
) // [5, 7, , 10] |
leetcode reduce 解法题目
136.只出现一次的数字(easy)题目:https://leetcode-cn.com/problems/single-number/ var singleNumber = function (nums) {
/**解法5:reduce
* 性能:80ms 37.1MB
*/
let countedNums = nums.reduce((acc, cur) => {
if (!(cur in acc)) {
acc[cur] = 1;
} else {
delete acc[cur];
}
return acc;
}, {});
return Object.keys(countedNums)[0];
} 202.快乐数(easy)题目:https://leetcode-cn.com/problems/happy-number/ // 解法二:递归+Set
var isHappy = function(n) {
if (n == 1) return true;
var nextNums = arguments[1] || [];
var nextNumsSet = new Set(nextNums);
// 通过Set对数组去重,比较size和length的大小
if (nextNumsSet.size !== nextNums.length) {
return false;
}
function quadratic(num) {
return Math.pow(parseInt(num), 2);
}
var nextNum = -999;
if (`${n}`.length === 1) {
// 7 true, 2 false
nextNum = quadratic(n);
} else {
// 19 true, 78 false
nextNum = `${n}`
.split("")
.map(quadratic)
.reduce((acc, cur) => acc + cur);
}
while (nextNum !== 1) {
nextNums.push(nextNum);
// 递归
nextNum = isHappy(nextNum, nextNums);
return nextNum;
}
return nextNum === 1;
}; 258.各位相加(easy)题目:https://leetcode-cn.com/problems/add-digits/ var addDigits = function (num) {
/**
* 解法1:递归 reduce
* 性能:92ms 36.4MB
*/
let nums = `${num}`.split("").map((item) => BigInt(item));
let reduce = nums.reduce((acc, cur) => acc + cur);
if (`${reduce}`.length === 1) {
return reduce;
} else {
return addDigits(reduce);
}
}; 500.键盘行(easy)题目:https://leetcode-cn.com/problems/keyboard-row/ var findWords = function (words) {
/**
* 解法2: Array Set
* 性能:60ms 33.9MB
* 思路:Set优化indexOf
*/
// 构建字母表
const lines = [
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
['z', 'x', 'c', 'v', 'b', 'n', 'm']
]
lines.forEach((line, i, arr) => {
let reduceArr = line.reduce((acc, cur) => {
acc.push(cur);
acc.push(cur.toUpperCase());
return acc
}, [])
arr[i] = new Set(reduceArr)
})
// 拆分单词
const wordsMap = words.map((word) => word.split(""))
const validWords = [];
wordsMap.forEach((word) => {
lines.forEach((line) => {
let isValid = word.every((char) => line.has(char));
if (isValid) validWords.push(word.join(""));
})
})
return validWords;
}; |
reduce()是一个js数组原型链上的高级函数,没个几年经验估计对这个方法的用法会很模糊。
我最常用的就是用recude()求和,但其实它还有很多值得挖掘的好用之处。
所以开这个issue来系统性学习下reduce()吧。
The text was updated successfully, but these errors were encountered: