JavaScript reduce 教程展示了如何在 JavaScript 语言中使用归约操作。
reduce函数
reduce
函数对数组的每个元素执行 reducer 函数,产生单个输出值。减速器由程序员提供。
reducer 函数有四个参数:
- 累加器
- 当前值
- 当前索引
- 源数组
reducer 的返回值赋值给累加器。累加器在整个数组的每次迭代中都会被记住,并最终成为最终的单一结果值。
归约运算非常强大。它们使我们能够计算总和、乘积、平均值、最大值和最小值、排序、反转、展平数组等等。
reduce函数语法
reduce
函数的语法如下:
arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])
方括号中的参数是可选的。如果没有提供initialValue
,reduce
将从索引 1 开始执行回调函数,跳过第一个索引。如果提供initialValue
,它将从索引 0 开始。
在我们的示例中,我们将调用累加器total 和当前值next。
JS reduce – 和与积
在下一个示例中,我们计算值的总和和乘积。
let vals = [1, 2, 3, 4, 5]; let sum = vals.reduce((total, next) => {return total + next}); let product = vals.reduce((total, next) => {return total * next}); console.log(`The sum is: ${sum}`); console.log(`The product is: ${product}`);
总和和乘积是根据整数数组计算的。
$ node sum_product.js The sum is: 15 The product is: 120
JS reduce – 最小值和最大值
以下示例从数组中选取最大值和最小值。
let vals = [1, 2, 3, 4, 5]; const [initial] = vals; const min = vals.reduce((total, next) => Math.min(total, next), initial); const max = vals.reduce((total, next) => Math.max(total, next), initial); console.log(`The minimum is: ${min}`); console.log(`The maximum is: ${max}`);
我们使用Math.max
和Math.min
函数来获取当前总累加器值的最大值和最小值,该值会在迭代中传递,直到返回最后一个值。初始值是数组中的第一个元素。请注意,在此上下文中,“总计”值是当前选择的最大值。
$ node min_max.js The minimum is: 1 The maximum is: 5
一次计算两个值是可能的。
let vals = [1, 2, 3, 4, 5]; const initials = { min: Number.MAX_VALUE, max: Number.MIN_VALUE, }; const min_max_vals = vals.reduce(min_max, initials); console.log(min_max_vals); function min_max(total, next) { return { min: Math.min(total.min, next), max: Math.max(total.max, next), }; }
我们 min_max
reducer 函数返回一个具有 min 和 max 属性的对象。
$ node min_max2.js { min: 1, max: 5 }
JS 缩图
在函数式映射操作中,通过对每个元素应用表达式从源数组创建一个新数组。
let vals = [1, 2, 3, 4, 5]; let mapped = vals.reduce((total, next) => {total.push(next * 2); return total}, []); console.log(mapped);
该示例从初始数组创建一个新数组。每个元素乘以二。初始值是一个空数组。 reducer 函数使用 push
函数将下一个值乘以 2 添加到初始数组。
$ node mapping.js [ 2, 4, 6, 8, 10 ]
JS 缩减过滤器
过滤器功能操作创建一个新数组,其中包含通过给定测试的所有元素。
let vals = [-1, -2, 3, 4, -5, -6]; let filtered = vals.reduce((total, next) => { if (next > 0) { total.push(next * 2); } return total; }, []); console.log(filtered);
该示例创建了一个仅包含正值的新数组。 reducer 函数仅在下一个值大于零时才将其添加到总数组。
$ node filtering.js [ 6, 8 ]
JS reduce – 展平数组
下面的例子展平了一个数组。
let vals = [[0, 1], [2, 3], [4, 5], [5, 6]]; let flattened = vals.reduce((total, next) => total.concat(next), []); console.log(flattened);
初始值是一个空数组,reducer 函数将新嵌套的数组与 concat
函数合并到该数组。
$ node flatten.js [ 0, 1, 2, 3, 4, 5, 5, 6 ]
JS 降低平均值
在计算平均值时,我们还利用了索引和源数组。
let vals = [1, 2, 3, 4, 5]; let average = vals.reduce((total, next, idx, array) => { total += next; if (idx === array.length - 1) { return total / array.length; } else { return total; } }); console.log(average);
该示例计算值的平均值。
if (idx === array.length - 1) { return total / array.length; } else { return total; }
当我们在 reducer 函数中到达数组末尾时,我们将总值(在本例中为值的总和)除以元素数。这是要返回的最终值。否则,我们将下一个值添加到总值并从 reducer 返回总值。
$ node average.js 3
值 1 到 5 的平均值是 3。
JS减少反向
在下一个示例中,我们使用扩展...
运算符。 spreadoperator 返回数组的所有元素。
let vals = [88, 28, 0, 9, 389, 420]; let reversed = vals.reduce((total, next) => {return [next, ...total]}, []); console.log(reversed);
在每次迭代中,reducer 函数返回一个新数组,当前元素放在第一个位置,后面是所有已经在总数组中的元素。
$ node reversing.js [ 420, 389, 9, 0, 28, 88 ]
JS reduce – 唯一值
以下示例创建一个仅包含唯一值的新数组。
let vals = [1, 1, 2, 2, 3, 4, 5, 5]; let unique_vals = vals.reduce((total, next) => { if (total.includes(next)) { return total; } else { return [...total, next]; } }, []); console.log(unique_vals);
reducer 函数使用includes
函数检查该值是否已经在总数组中。仅当 includes
函数返回 false 时,它才会将下一个值添加到总数组中。
$ node unique_vals.js [ 1, 2, 3, 4, 5 ]
JS减少管道
我们可以在 reducer 函数中链接函数。
function inc(val) { return val + 1; } function dec(val) { return val - 1; } function double(val) { return val * 2; } function halve(val) { return val / 2; } let pipeline = [inc, halve, dec, double]; let res = pipeline.reduce((total, fn) => { return fn(total); }, 9); console.log(res);
在这个例子中,我们在一个 reducer 中链接了四个函数。这些函数应用于初始值。
$ node piping.js 8
JS reduce – 柯里化和函数组合
柯里化是将具有多个参数的函数转换为具有单个参数的嵌套函数序列。柯里化帮助我们创建组合函数,这些函数稍后会接受参数。
阅读 JavaScript currying 教程以了解有关 currying 的更多信息。
const double = x => x * 2 const triple = x => x * 3 const quadruple = x => x * 4 const pipe = (...funs) => input => funs.reduce( (total, fn) => fn(total), input ) const fun1 = pipe(double) const fun2 = pipe(double, triple) const fun3 = pipe(triple, triple) const fun4 = pipe(double, triple, quadruple) console.log(fun1(2)) console.log(fun2(5)) console.log(fun3(7)) console.log(fun4(9))
pipe
函数采用任意数量的参数 – 函数。组合函数稍后采用函数运行的输入值。
const pipe = (...funs) => input => funs.reduce( (total, fn) => fn(total), input )
pipe
函数首先将函数应用于输入值。然后将计算出的中间值传递给链中的其他函数,直到返回最终值。
const fun1 = pipe(double) const fun2 = pipe(double, triple) const fun3 = pipe(triple, triple) const fun4 = pipe(double, triple, quadruple)
这些是特定值相乘的复合函数。
$ node fun_composition.js 4 30 63 216
JS reduce – count occurrences
reducer 函数可用于计算数组中元素的出现次数。
const words = ['sky', 'forest', 'wood', 'sky', 'rock', 'cloud', 'sky', 'forest', 'rock', 'sky']; const tally = words.reduce((total, next) => { total[next] = (total[next] || 0) + 1 ; return total; }, {}); console.log(tally);
在示例中,我们有一个单词数组。多次包含多个单词。初始值是一个空对象。 reducer 函数要么创建新属性,要么增加属性的值。
$ node tally.js { sky: 4, forest: 2, wood: 1, rock: 2, cloud: 1 }
JS reduce – 按属性分组对象
以下示例按属性对数组中的对象进行分组。
let users = [ { name: 'John', age: 25, occupation: 'gardener' }, { name: 'Lenny', age: 51, occupation: 'programmer' }, { name: 'Andrew', age: 43, occupation: 'teacher' }, { name: 'Peter', age: 52, occupation: 'gardener' }, { name: 'Anna', age: 43, occupation: 'teacher' }, { name: 'Albert', age: 46, occupation: 'programmer' }, { name: 'Adam', age: 47, occupation: 'teacher' }, { name: 'Robert', age: 32, occupation: 'driver' } ]; let grouped = users.reduce((result, user) => { (result[user.occupation] || (result[user.occupation] = [])).push(user); return result; }, {}); console.log(grouped);
我们有很多用户。我们按用户的职业对用户进行分组。初始值是一个空对象。结果对象具有职业作为属性;每个属性都包含具有相应职业的用户列表。
let grouped = users.reduce((result, user) => { (result[user.occupation] || (result[user.occupation] = [])).push(user); return result; }, {});
reducer 要么创建一个带有空数组的新属性并推送第一个用户,要么将一个新的用户对象添加到已创建的数组中。
$ node grouping.js { gardener: [ { name: 'John', age: 25, occupation: 'gardener' }, { name: 'Peter', age: 52, occupation: 'gardener' } ], programmer: [ { name: 'Lenny', age: 51, occupation: 'programmer' }, { name: 'Albert', age: 46, occupation: 'programmer' } ], teacher: [ { name: 'Andrew', age: 43, occupation: 'teacher' }, { name: 'Anna', age: 43, occupation: 'teacher' }, { name: 'Adam', age: 47, occupation: 'teacher' } ], driver: [ { name: 'Robert', age: 32, occupation: 'driver' } ] }
JS reduce – 数组转对象
以下示例将数组转换为对象。
let users = [ { id: 1, name: 'John', age: 25, occupation: 'gardener' }, { id: 2, name: 'Lenny', age: 51, occupation: 'programmer' }, { id: 3, name: 'Andrew', age: 43, occupation: 'teacher' }, { id: 4, name: 'Peter', age: 52, occupation: 'gardener' }, { id: 5, name: 'Anna', age: 43, occupation: 'teacher' }, { id: 6, name: 'Albert', age: 46, occupation: 'programmer' }, { id: 7, name: 'Adam', age: 47, occupation: 'teacher' }, { id: 8, ame: 'Robert', age: 32, occupation: 'driver' } ]; let obj = users.reduce((total, e) => { const {id, ...attrs} = e; return {...total, [id]: attrs, }; }, {}); console.log(obj);
我们有一组用户对象。使用 reducer,我们将数组转换为对象;用户 ID 成为结果对象中的标识属性。
const {id, ...attrs} = e;
从当前元素,一些用户对象,我们将 id 值复制到 id
常量中,并将其余属性复制到 attrs
中。
return {...total, [id]: attrs, };
然后我们构建中间(最后是最终)对象。首先,我们展开到目前为止在 total
中创建的所有内部属性并添加当前属性。
$ node array2object.js { '1': { name: 'John', age: 25, occupation: 'gardener' }, '2': { name: 'Lenny', age: 51, occupation: 'programmer' }, '3': { name: 'Andrew', age: 43, occupation: 'teacher' }, '4': { name: 'Peter', age: 52, occupation: 'gardener' }, '5': { name: 'Anna', age: 43, occupation: 'teacher' }, '6': { name: 'Albert', age: 46, occupation: 'programmer' }, '7': { name: 'Adam', age: 47, occupation: 'teacher' }, '8': { ame: 'Robert', age: 32, occupation: 'driver' } }
在本文中,我们研究了 JavaScript 的缩减。
列出所有 JavaScript 教程。