这是@theprtk提供的代码的更新版本。举个例子,稍稍整理一下以显示通用版本。
注意:我会将其添加为他的帖子的评论,但我的声誉还不够
/**
* @see http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html
* @description functions that transform reducing functions
*/
const transduce = {
/** a generic map() that can take a reducing() & return another reducing() */
map: changeInput => reducing => (acc, input) =>
reducing(acc, changeInput(input)),
/** a generic filter() that can take a reducing() & return */
filter: predicate => reducing => (acc, input) =>
predicate(input) ? reducing(acc, input) : acc,
/**
* a composing() that can take an infinite # transducers to operate on
* reducing functions to compose a computed accumulator without ever creating
* that intermediate array
*/
compose: (...args) => x => {
const fns = args;
var i = fns.length;
while (i--) x = fns[i].call(this, x);
return x;
},
};
const example = {
data: [{ src: 'file.html' }, { src: 'file.txt' }, { src: 'file.json' }],
/** note: `[1,2,3].reduce(concat, [])` -> `[1,2,3]` */
concat: (acc, input) => acc.concat([input]),
getSrc: x => x.src,
filterJson: x => x.src.split('.').pop() !== 'json',
};
/** step 1: create a reducing() that can be passed into `reduce` */
const reduceFn = example.concat;
/** step 2: transforming your reducing function by mapping */
const mapFn = transduce.map(example.getSrc);
/** step 3: create your filter() that operates on an input */
const filterFn = transduce.filter(example.filterJson);
/** step 4: aggregate your transformations */
const composeFn = transduce.compose(
filterFn,
mapFn,
transduce.map(x => x.toUpperCase() + '!'), // new mapping()
);
/**
* Expected example output
* Note: each is wrapped in `example.data.reduce(x, [])`
* 1: ['file.html', 'file.txt', 'file.json']
* 2: ['file.html', 'file.txt']
* 3: ['FILE.HTML!', 'FILE.TXT!']
*/
const exampleFns = {
transducers: [
mapFn(reduceFn),
filterFn(mapFn(reduceFn)),
composeFn(reduceFn),
],
raw: [
(acc, x) => acc.concat([x.src]),
(acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src] : []),
(acc, x) => acc.concat(x.src.split('.').pop() !== 'json' ? [x.src.toUpperCase() + '!'] : []),
],
};
const execExample = (currentValue, index) =>
console.log('Example ' + index, example.data.reduce(currentValue, []));
exampleFns.raw.forEach(execExample);
exampleFns.transducers.forEach(execExample);