ECMAScript 5在2009年引入了许多很棒的功能 ,其中大多数是数组方法,例如isArray , forEach , map , filter , every , some 。 但是,让我们谈谈我最喜欢的一个: reduce
。
reduce
方法
reduce
方法在数组的每个元素上执行reducer
回调函数(由用户提供),从而产生单个输出值。
减速器
reducer
函数采用四个参数:
- 累加器(acc)
- 当前值(当前)
- 当前索引(idx)
- 源数组(src)
reducer
函数的返回值分配给累加器,累加器的值在整个数组的每次迭代中都会被记住,并最终成为最终的单个结果值。
重要提示:在每次迭代中,您必须 返回下一次迭代的累加器值(最终将是最终的返回值),否则累加器的下一个(最终是最终的)值将undefined
。
初始值
reduce
方法采用第二个可选参数: initialValue
。
如果未提供,则累加器的初始值将是数组的第一个元素,并且第一次迭代将指向第二个元素。 如果initialValue
提供时,它将是累加器的初始值,并且第一次迭代将指向数组的第一个元素。
例子
有或没有初始值的数字求和
const numbers = [ 1 , 2 , 3 ];
// Without initialValue
const sum = numbers.reduce(
( accumulator, currentValue ) => accumulator + currentValue
);
// Prints 6
console .log(sum);
// With initialValue
const initialValue = 3 ;
const sumWithInitialValue = numbers.reduce(
( accumulator, currentValue ) => accumulator + currentValue
, initialValue);
// Prints 9
console .log(sumWithInitialValue);
如果没有初始值,则第一次迭代将使accumulator
指向数组的第一个元素(1),而currentValue
指向数组的第二个元素(2)。
给定初始值,第一次迭代将具有一个具有给定初始值(3)的值的accumulator
,而currentValue
将指向数组的第一个元素(1)。
计算数组中的出现次数
让我们计算以下单词的出现次数,并将结果存储在地图中:
土拨鼠卡盘要多少木头
如果土拨鼠能夹木头?
他会发抖,他会尽可能
和土拨鼠一样多
如果土拨鼠可以夹木头。
const sentence = "how much wood would a woodchuck chuck" +
"if a woodchuck could chuck wood " +
"he would chuck he would as much as he could " +
"and chuck as much as a woodchuck would " +
"if a woodchuck could chuck wood" ;
const words = sentence.split( " " );
const occurencesMap = words.reduce(
( occurences, word ) => {
const numOfOccurences = (occurences.get(word) || 0 ) + 1 ;
occurences.set(word, numOfOccurences);
return occurences;
}
, new Map ());
const numOfWoodchucks = occurencesMap.get( "woodchuck" );
// 4
console .log(numOfWoodchucks);
我们初始化一个空映射,并将其用作累加器的初始值,在迭代句子中的单词时初始化或更新每个单词的出现次数。
这只是两个例子,但是现在您必须已经意识到reduce
是多么的出色,对吗?
它允许您采用数组并将其值减小为基本上可以从其保存的数据派生的任何值。 它还允许您返回任何类型的数据,而与数组元素的类型无关。
一种统治所有人的方法?
回顾其他ES5数组方法,我们可以看到每个方法在数组上使用给定的回调函数并返回某种结果。
例如:
-
map
转换数组的每个元素,并返回一个新的 数组。 -
every
检查给定条件是否适用于数组中的每个元素,并返回相应的布尔值。
看起来很熟悉吧?
使用我们已经知道的知识,让我们尝试使用reduce
来实现其他ES5数组方法。
注意:在每个示例中,我们都会将新方法添加到Array
的原型中, this
将指向我们正在操作的数组。
地图
map()方法创建一个新数组,其中每个原始元素都由给定的transformer
回调进行transformer
。
用法
const array = [ 1 , 2 , 3 ];
const doubled = array.map( num => num * 2 );
// Prints [2, 4, 6]
console .log(doubled);
通过使用转换器回调将数组中的每个数字加倍,我们得到一个新数组,其中每个元素都是其原始值的两倍。
与reduce
map
在一个数组map
操作并返回一个新数组,因此累加器必须是一个数组。
Array .prototype.mapWithReduce = function ( transformer ) {
return this .reduce( ( newArray, currentElement ) => {
const newElement = transformer(currentElement);
newArray.push(newElement);
return newArray;
}, []);
}
const array = [ 1 , 2 , 3 ];
const doubled = array.mapWithReduce( num => num * 2 );
// Prints [2, 4, 6]
console .log(doubled);
使用reduce
,我们从一个空的数组累加器开始,然后遍历该数组。 然后,我们将转换器回调应用于每个元素,并将其推入累加数组。
过滤
filter()方法创建一个新数组 通过提供的功能实现的所有通过测试的元素。
用法
const array = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ];
const evenOnly = array.filter( num => num % 2 === 0 );
// Prints [2, 4, 6, 8, 10]
console .log(evenOnly);
使用一个测试回调过滤掉所有的奇数,我们得到一个包含原始数组所有偶数元素的新数组。
与reduce
就像前面的示例一样,filter也在数组上操作并返回一个新数组,因此累加器必须是数组。
Array .prototype.filterWithReduce = function ( tester ) {
return this .reduce( ( newArray, currentElement ) => {
if (tester(currentElement)) {
newArray.push(currentElement);
};
return newArray;
}, []);
}
const array = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ];
const evenOnly = array.filterWithReduce( num => num % 2 === 0 );
// Prints [2, 4, 6, 8, 10]
console .log(evenOnly);
使用reduce
,我们从一个空的数组累加器开始,然后遍历该数组。 然后,我们使用测试器回调检查是否应将每个元素都推到累积数组中。
每一个
every()方法测试数组中的所有元素是否通过提供的函数实现的测试。 它返回一个布尔值。
用法
const array = [ 1 , 2 , 3 , 4 , 5 ];
const result = array.every( num => num < 10 );
// Prints true
console .log(result);
使用回调函数测试数组中的每个元素,我们得到一个布尔值,指示是否所有元素都通过了测试。 在这种情况下,所有元素都小于10,因此every
元素都返回true
。
与reduce
every
运算符都对数组进行运算并返回布尔值,因此累加器必须为布尔值。
Array .prototype.everyWithReduce = function ( tester ) {
return this .reduce( ( acc, currentElement ) =>
acc && tester(currentElement)
, true );
}
const array = [ 1 , 2 , 3 , 4 , 5 ];
const result = array.everyWithReduce( num => num < 10 );
// Prints true
console .log(result);
使用reduce
,我们从布尔累加器值为true
(稍后将讨论原因),然后遍历数组。 然后,我们使用逻辑AND( &&
)将测试器回调的结果链接到累加器,以在所有元素均通过测试后最终返回true
, otherwise
返回false。
为什么从true
开始?
如果数组为空,则无论测试回调如何, every
返回true
(即使该回调返回false
)。
否则,如果所有元素都满足条件,则使用逻辑AND链接初始true
值最终将解析为true
。 否则,链接最终将解析为false
。
一些
some()方法测试数组中的至少一个元素是否通过了由提供的函数实现的测试。 它返回一个布尔值。
用法
const array = [ 1 , 2 , 3 , 4 , 5 ];
const result = array.some( num => num > 3 );
// Prints true
console .log(result);
使用回调函数测试数组中的每个元素,我们得到一个布尔值,指示是否有任何元素通过测试。 在这种情况下,第四个元素大于3,因此some
元素返回true
。
与reduce
some
对数组进行运算并返回布尔值,因此累加器必须为布尔值。
Array .prototype.someWithReduce = function ( tester ) {
return this .reduce( ( acc, currentElement ) =>
acc || tester(currentElement)
, false );
}
const array = [ 1 , 2 , 3 , 4 , 5 ];
const result = array.someWithReduce( num => num > 3 );
// Prints true
console .log(result);
使用reduce
,我们从布尔累加器值false
(稍后将讨论原因),然后遍历数组。 然后,我们使用逻辑OR( ||
)将测试器回调的结果链接到累加器,以在任何元素通过测试时最终返回true
,否则返回false
。
为什么从false
开始?
如果数组为空,则不管测试回调如何,即使返回回调true
, some
返回false
。
否则,如果任何元素满足条件,则使用逻辑OR链接初始false
值最终将解析为true
。 否则,链接最终将解析为false
。
免责声明(每部分)
every
方法都会对数组中存在的每个元素执行一次提供的回调函数,直到找到回调返回虚假值(当该值转换为布尔值时该值为false
的那个函数。 如果找到了这样的元素,则every
元素立即返回false
。
类似地, some
,直到它找到地方回调返回truthy值(即变成值的一个方法执行一次为每个存在于阵列中元件的回调函数true
时转换成布尔)。 如果找到了这样的元素,则some
元素立即返回true
。
但是,没有终止 reduce
mid-loop .
This means that while both implementations (both the original every/some
method and the corresponding implementations using reduce
) have a runtime of O(n)
, the original implementations are likely to terminate without having to iterate over the entire array, making them more efficient.漂亮方法mid-loop .
This means that while both implementations (both the original every/some
method and the corresponding implementations using reduce
) have a runtime of O(n)
, the original implementations are likely to terminate without having to iterate over the entire array, making them more efficient. mid-loop .
This means that while both implementations (both the original every/some
method and the corresponding implementations using reduce
) have a runtime of O(n)
, the original implementations are likely to terminate without having to iterate over the entire array, making them more efficient.
筛选器+地图
给定一个数字数组,最有效的方法是过滤出所有偶数元素并将其余的元素平方(使用ES5方法)?
让我们先尝试filter
然后再尝试map
:
const array = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ];
const tester = num => num % 2 === 1 ;
const transformer = num => num * num;
const result = array.filter(tester).map(transformer);
// Prints [1, 9, 25, 49, 81]
console .log(result);
我们创建一个仅保留奇数元素的测试器函数和一个将给定元素平方的变压器函数。 然后在链接filter
和map
方法时使用这两个回调函数,并返回所需的数组。
让我们用我们了解的通过reduce
实现filter
和map
,只有这次我们将它们一次组合起来。
const array = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ];
const tester = num => num % 2 === 1 ;
const transformer = num => num * num;
const result = array.reduce( ( newArray, currentElement ) => {
if (tester(currentElement)) {
const newElement = transformer(currentElement);
newArray.push(newElement);
}
return newArray;
}, []);
// Prints [1, 9, 25, 49, 81]
console .log(result);
我们使用相同的测试器和转换器函数来测试每个元素是否应保留在数组中,并对其进行转换。
这种方法省去了创建过滤值中间数组的需要,并且由于不必遍历两个不同的数组(原始数组和中间数组),我们得到了一种效率更高的算法。
结论
这些是如何使用功能强大的reduce
方法实现其他ES5方法的一些示例。
您如何在日常编码中使用它? 分享评论!
资料来源
- w3schools的ES5功能
- MDN Web文档提供的Javascript数组方法 。
本文中的每个方法文档均来自此处。
From: https://hackernoon.com/the-superpowers-of-arrayreduce-jn1536p6