(acc, val) => acc.concat([val])
. If the drive passed to it is an array [1, 2, 3]
, and the element is the number 4
, it will return the array [1, 2, 3, 4]
. const acc = [1, 2, 3]; const val = 4; const reducer = (acc, val) => acc.concat([val]); reducer(acc, val) ///=> 1, 2, 3, 4
(acc, val) => acc.add(val)
. It is suitable for any object that has a .add()
method that, among other things, returns this object (like Set.prototype.add () ). Our reducer adds the element transferred to it to the drive using the method. add()
drive. const acc = new Set([1, 2, 3]); const val = 4; const reducer = (acc, val) => acc.add(val); reducer(acc, val) ///=> Set{1, 2, 3, 4}
const toArray = iterable => { const reducer = (acc, val) => acc.concat([val]); const seed = []; let accumulation = seed; for (value of iterable) { accumulation = reducer(accumulation, value); } return accumulation; } toArray([1, 2, 3]) //=> [1, 2, 3]
reducer
and seed
parameters of the new function (the host, by analogy with the just considered, and the argument iterable
), getting a universal reducing function. const reduce = (iterable, reducer, seed) => { let accumulation = seed; for (const value of iterable) { accumulation = reducer(accumulation, value); return accumulation; } reduce([1, 2, 3], (acc, val) => acc.concat([val]), []) //=> [1, 2, 3]
reduce
, the first parameter of which is the reducer. If you rewrite this function in JavaScript Allongé style, you get the following. const reduceWith = (reducer, seed, iterable) => { let accumulation = seed; for (const value of iterable) { accumulation = reducer(accumulation, value); } return accumulation; } reduce([1, 2, 3], (acc, val) => acc.concat([val]), []) //=> [1, 2, 3] // : reduceWith((acc, val) => acc.concat([val]), [], [1, 2, 3]) //=> [1, 2, 3]
.reduce
method. This method behaves in the same way as the above functions reduce
and reduceWith
. [1, 2, 3].reduce((acc, val) => acc.concat([val]), []) //=> [1, 2, 3]
(acc, val) => acc.concat([val])
creates an unnecessary load on the memory, so we can replace it with such a reducer: (acc, val) => { acc.push(val); return acc; }
(acc, val) => { acc.push(val); return acc; }
(acc, val) => { acc.push(val); return acc; }
.(acc, val) => (acc.push(val), acc)
looks better from a semantic point of view, but the comma operator can confuse those who are not familiar with the peculiarities of its use. Usually in production code this is best avoided.reduceWith
function. const arrayOf = (acc, val) => { acc.push(val); return acc; }; reduceWith(arrayOf, [], [1, 2, 3]) //=> [1, 2, 3]
const sumOf = (acc, val) => acc + val; reduceWith(sumOf, 0, [1, 2, 3]) //=> 6
const joinedWith = separator => (acc, val) => acc == '' ? val : `${acc}${separator}${val}`; reduceWith(joinedWith(', '), '', [1, 2, 3]) //=> "1, 2, 3" reduceWith(joinedWith('.'), '', [1, 2, 3]) //=> "1.2.3"
const incrementSecondArgument = binaryFn => (x, y) => binaryFn(x, y + 1); const power = (base, exponent) => base ** exponent; const higherPower = incrementSecondArgument(power); power(2, 3) //=> 8 higherPower(2, 3) //=> 16
higherPower
function is a power
function, decorated by adding a unit to its exponent
argument. Thus, calling higherPower(2,3)
gives the same result as power(2,4)
. We have already worked with similar functions, our reducers are also binary functions. They can be decorated. reduceWith(incrementSecondArgument(arrayOf), [], [1, 2, 3]) //=> [2, 3, 4] const incremented = iterable => reduceWith(incrementSecondArgument(arrayOf), [], iterable); incremented([1, 2, 3]) //=> [2, 3, 4]
incrementSecondArgument
function. const incrementSecondArgument = binaryFn => (x, y) => binaryFn(x, y + 1);
const incrementValue = reducer => (acc, val) => reducer(acc, val + 1);
incrementValue
takes a reducer as an argument and returns another reducer, which, before processing the element passed to it, adds one to it. The logic of "incrementing" can be made in the parameter. const map = fn => reducer => (acc, val) => reducer(acc, fn(val)); const incrementValue = map(x => x + 1); reduceWith(incrementValue(arrayOf), [], [1, 2, 3]) //=> [2, 3, 4]
map(x => x + 1)
construction map(x => x + 1)
everywhere, where you can use incrementValue
. Thus, we can write the following. reduceWith(map(x => x + 1)(arrayOf), [], [1, 2, 3]) //=> [2, 3, 4]
map
decorator can decorate any reducer, it is permissible to combine the results of incrementing numbers, forming a string, or summarize them. reduceWith(map(x => x + 1)(joinedWith('.')), '', [1, 2, 3]) //=> "2.3.4" reduceWith(map(x => x + 1)(sumOf), 0, [1, 2, 3]) //=> 9
const squares = map(x => power(x, 2)); const one2ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; reduceWith(squares(sumOf), 0, one2ten) //=> 385
const arrayOf = (acc, val) => { acc.push(val); return acc; }; reduceWith(arrayOf, 0, one2ten) //=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const bigUns = (acc, val) => { if (val > 5 ) { acc.push(val); } return acc; }; reduceWith(bigUns, [], one2ten) //=> [6, 7, 8, 9, 10]
reduceWith(squares(bigUns), [], one2ten) //=> [9, 16, 25, 36, 49, 64, 81, 100]
reduceWith(squares(arrayOf), [], one2ten) //=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] const bigUnsOf = reducer => (acc, val) => (val > 5) ? reducer(acc, val) : acc; reduceWith(bigUnsOf(squares(arrayOf)), [], one2ten) //=> [36, 49, 64, 81, 100]
bigUnsOf
function bigUnsOf
quite specific. We will do the same here as with the map
, namely, we extract the predicate function and make it an argument. reduceWith(squares(arrayOf), [], one2ten) //=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] const filter = fn => reducer => (acc, val) => fn(val) ? reducer(acc, val) : acc; reduceWith(filter(x => x > 5)(squares(arrayOf)), [], one2ten) //=> [36, 49, 64, 81, 100]
reduceWith(filter(x => x % 2 === 1)(arrayOf), [], one2ten) //=> [1, 3, 5, 7, 9]
reduceWith(filter(x => x % 2 === 1)(squares(sumOf)), 0, one2ten) //=> 165
const plusFive = x => x + 5; const divideByTwo = x => x / 2; plusFive(3) //=> 8 divideByTow(8) //=> 4 const compose2 = (a, b) => (...c) => a(b(...c)); const plusFiveDividedByTwo = compose2(divideByTwo, plusFive); plusFiveDividedByTwo(3) //=> 4
compose2
? This means that by giving it two any transformers, we will get a new transformer that transforms the reducer. Thus, we obtain the following. const squaresOfTheOddNumbers = compose2( filter(x => x % 2 === 1), squares ); reduceWith(squaresOfTheOddNumbers(sumOf), 0, one2ten) //=> 165
squaresOfTheOddNumbers
is a transformer that we created by applying the compose2
function to the filter and mapping functions.compose2
function compose2
, which allows you to get a composition of two functions, we will think about what to do if we need the composition of an arbitrary number of functions. The answer lies in the convolution.compose2
, making it a compositionOf
transformer. const compositionOf = (acc, val) => (...args) => val(acc(...args));
compose
function to get the composition of an arbitrary number of functions as a reduction of its arguments: const compose = (...fns) => reduceWith(compositionOf, x => x, fns);
reduceWith(squaresOfTheOddNumbers(sumOf), 0, one2ten)
const transduce = (transformer, reducer, seed, iterable) => { const transformedReducer = transformer(reducer); let accumulation = seed; for (const value of iterable) { accumulation = transformedReducer(accumulation, value); } return accumulation; } transduce(squaresOfTheOddNumbers, sumOf, 0, one2ten) //=> 165
transformer
abbreviated to xform
or even to xf
. Do not be surprised if you see a similar construct, whose entry looks like (xf, reduce, seed, coll), or xf((val, acc) => acc) -> (val, acc) => acc
. Here we can do without abbreviations, but in the production code names like xf
or xform
quite acceptable..reduce —
it takes a drive object and input data, and returns a drive into which new data is placed. A transformer is a function that transforms a reducer into another reducer. A transducer (this name is the result of combining the terms "transformer" and "reducer", here it is the answer to the question: "What are redusers?"), Is a function that accepts a transformer, reducer, drive and an iterated object, and then collapses the iterated object in a certain meaning. const arrayOf = (acc, val) => { acc.push(val); return acc; }; const sumOf = (acc, val) => acc + val; const setOf = (acc, val) => acc.add(val); const map = fn => reducer => (acc, val) => reducer(acc, fn(val)); const filter = fn => reducer => (acc, val) => fn(val) ? reducer(acc, val) : acc; const compose = (...fns) => fns.reduce((acc, val) => (...args) => val(acc(...args)), x => x); const transduce = (transformer, reducer, seed, iterable) => { const transformedReducer = transformer(reducer); let accumulation = seed; for (const value of iterable) { accumulation = transformedReducer(accumulation, value); } return accumulation; }
.map
, .filter
, .reduce
, and there are transducers suitable for composition that are not involved in creating multiple copies of the data set being processed. In fact, transducers written for real projects provide much more use .find
, for example, reproducing the functionality of the .find
method.transduse
function transduse
designed for the fact that an transduse
collection will be passed to it, in addition, we must provide it with an initial value (drive) and a reducer. In most cases, both the initial value and the reducer are the same functions for all collections of the same type. This is also characteristic of the corresponding libraries. 1a2ddc2, 5f2b932 f1a543f, 5890595 3abe124, bd11537 f1a543f, 5f2b932 f1a543f, bd11537 f1a543f, 5890595 1a2ddc2, bd11537 1a2ddc2, 5890595 3abe124, 5f2b932 f1a543f, 5f2b932 f1a543f, bd11537 f1a543f, 5890595 1a2ddc2, 5f2b932 1a2ddc2, bd11537 1a2ddc2, 5890595 ...
1a2ddc2
visited places with codes 5f2b932
, bd11537
, 5890595
, 5f2b932
, bd11537
, and 5890595
. At the same time, user f1a543f
visited places 5890595
, 5f2b932
, bd11537
, 5890595
, 5f2b932
, bd11537
, and 5890595
. And so on.1a2ddc2
as follows: 5f2b932
, bd11537
, 5890595
, 5f2b932
, bd11537
, 5890595
. This means that for him it is possible to build such a scheme of transitions from place to place: 5f2b932 -> bd11537 bd11537 -> 5890595 5890595 -> 5f2b932 5f2b932 -> bd11537 bd11537 -> 5890595
5f2b932 -> bd11537
appears in the list twice.bd11537 -> 5890595
also occurs twice.5890595 -> 5f2b932
met only once. const logContents = `1a2ddc2, 5f2b932 f1a543f, 5890595 3abe124, bd11537 f1a543f, 5f2b932 f1a543f, bd11537 f1a543f, 5890595 1a2ddc2, bd11537 1a2ddc2, 5890595 3abe124, 5f2b932 f1a543f, 5f2b932 f1a543f, bd11537 f1a543f, 5890595 1a2ddc2, 5f2b932 1a2ddc2, bd11537 1a2ddc2, 5890595`;
const asStream = function * (iterable) { yield * iterable; }; const lines = str => str.split('\n'); const streamOfLines = asStream(lines(logContents)); const datums = str => str.split(', '); const datumize = map(datums); const userKey = ([user, _]) => user; const pairMaker = () => { let wip = []; return reducer => (acc, val) => { wip.push(val); if (wip.length === 2) { const pair = wip; wip = wip.slice(1); return reducer(acc, pair); } else { return acc; } } } const sortedTransformation = (xfMaker, keyFn) => { const decoratedReducersByKey = new Map(); return reducer => (acc, val) => { const key = keyFn(val); let decoratedReducer; if (decoratedReducersByKey.has(key)) { decoratedReducer = decoratedReducersByKey.get(key); } else { decoratedReducer = xfMaker()(reducer); decoratedReducersByKey.set(key, decoratedReducer); } return decoratedReducer(acc, val); } } const userTransitions = sortedTransformation(pairMaker, userKey); const justLocations = map(([[u1, l1], [u2, l2]]) => [l1, l2]); const stringify = map(transition => transition.join(' -> ')); const transitionKeys = compose( stringify, justLocations, userTransitions, datumize ); const countsOf = (acc, val) => { if (acc.has(val)) { acc.set(val, 1 + acc.get(val)); } else { acc.set(val, 1); } return acc; } const greatestValue = inMap => Array.from(inMap.entries()).reduce( ([wasKeys, wasCount], [transitionKey, count]) => { if (count < wasCount) { return [wasKeys, wasCount]; } else if (count > wasCount) { return [new Set([transitionKey]), count]; } else { wasKeys.add(transitionKey); return [wasKeys, wasCount]; } } , [new Set(), 0] ); greatestValue( transduce(transitionKeys, countsOf, new Map(), streamOfLines) ) //=> [ "5f2b932 -> bd11537", "bd11537 -> 5890595" ], 4
Source: https://habr.com/ru/post/329536/
All Articles