// A function which calculates the square
const square = x => x * x
// Use `map` to get the square of each number
console.log([1, 2, 3, 4, 5].map(square))
reduce
使用数组作为输入,您可以根据获取 accumulator和 current_element参数的回调函数(第一个参数)获取单个元素(比如 Object、 Number 或另一个 Array) :
const numbers = [1, 2, 3, 4, 5]
// Calculate the sum
console.log(numbers.reduce(function (acc, current) {
return acc + current
}, 0)) // < Start with 0
// Calculate the product
console.log(numbers.reduce(function (acc, current) {
return acc * current
}, 1)) // < Start with 1
var originalArr = [1,2,3,4]
//[1,2,3,4]
var squaredArr = originalArr.map(function(elem){
return Math.pow(elem,2);
});
//[1,4,9,16]
Filter返回一个元素数等于或少于元素数的新数组 than the original array. It returns those elements in the array which have passed some condition. This method is used when we want to apply a filter on the original array therefore the name filter. For eg,
var originalArr = [1,2,3,4]
//[1,2,3,4]
var evenArr = originalArr.filter(function(elem){
return elem%2==0;
})
//[2,4]
reduce(f, a, [b, c, d])
// is equivalent to
f(f(f(a, b), c), d)
// or if you squint a little
((a ❋ b) ❋ c) ❋ d
This should seem familiar: you know arithmetic operations obey rules such as "associativity" or "commutativity". What I want to convey here is that the same kind of rules apply.
reduce可能会去掉周围的结构,这些数值在转换的时候仍然被绑定在一个代数结构中。
还原代数
代数结构远远超出了这个答案的范围,所以我将只触及它们是如何相关的。
((a ❋ b) ❋ c) ❋ d
Looking at the expression above, it is self-evident that there is a constraint that ties all the values together : ❋ must know how to combine them the same way + must know how to combine 1 + 2 and just as importantly (1 + 2) + 3.
最脆弱的安全结构
One way to ensure this is to enforce that these values belong to a same set on which the reducer is an "internal" or "closed" binary operation, that is to say: combining any two values from this set with the reducer produces a value which belongs to the same set.
An example of this is function composition. One of the following functions returns a string, which constrains the order in which you can combine them:
const a = x => x * 2;
const b = x => x ** 2;
const c = x => x + ' !';
// (a ∘ b) ∘ c
const abc = x => c(b(a(x)));
abc(5); // "100 !"
// (a ∘ c) ∘ b
const acb = x => b(c(a(x)));
acb(5); // NaN
像许多二进制运算一样,复合函数可以用作减速器。
知道我们是否处于重新排序或从数组中删除元素可能导致 reduce中断的情况是有价值的。
所以,岩浆: 不是绝对必要的,但是非常重要。
初始值呢
假设我们想通过引入一个初始值来防止在数组为空时抛出异常:
array.reduce(f, init)
// which is really the same as doing
[init, ...array].reduce(f)
// or
((init ❋ a) ❋ b) ❋ c...
We now have an additional value. No problem.
"No problem"!? We said the purpose of the reducer was to combine the array values, but init is not a 没错 value: it was forcefully introduced by ourselves, it should not affect the result of reduce.
问题是:
我们应该选择哪个 init使得 f(init, a)或 init ❋ a返回 a?
我们需要一个初始值,它的作用就像它不存在一样。我们需要一个中立的元素(或“身份”)。
你可以查找 单位岩浆或者 幺半群(和结合性一样) ,这些词都是指含有中性元素的岩浆。
一些中性元素
你已经知道一些中性元素了
numbers.reduce((a, b) => a + b, 0)
numbers.reduce((a, b) => a * b, 1)
booleans.reduce((a, b) => a && b, true)
strings.reduce((a, b) => a.concat(b), "")
arrays.reduce((a, b) => a.concat(b), [])
vec2s.reduce(([u,v], [x,y]) => [u+x,v+y], [0,0])
mat2s.reduce(dot, [[1,0],[0,1]])
const map = f => (acc, x) =>
acc.concat(f(x))
;
const double = x => x * 2;
[1, 2, 3].reduce(map(double), []) // [2, 4, 6]
再进一步推进,就产生了诸如传感器之类的巧妙技巧。
我不会详细介绍它们,但是我希望你们注意到一些事情,它们将重复我们之前所说的。
传感器
首先让我们看看我们要解决什么问题
[1, 2, 3, 4].filter(x => x % 2 == 0)
.map(x => x ** 2)
.reduce((a, b) => a + b)
// 20
我们将迭代3次并创建2个中间数据结构。此代码是声明性的,但效率不高。传感器试图调和这两者。
首先介绍一些使用 reduce组合函数的实用工具,因为我们不打算使用方法链接:
const composition = (f, g) => x => f(g(x));
const identity = x => x;
const compose = (...functions) =>
functions.reduce(composition, identity)
;
// compose(a, b, c) is the same as x => a(b(c(x)))
Also notice that we have full control over the initial value and the final reducer. We used 0 and add, but we could have used [] and concat (more realistically push performance-wise) or any other data-structure for which we can implement a concat-like operation.