Skip to content

Instantly share code, notes, and snippets.

@PartyLich
Last active January 14, 2020 17:33
Show Gist options
  • Save PartyLich/fbda0f35432710e1b4113f33c9b7039c to your computer and use it in GitHub Desktop.
Save PartyLich/fbda0f35432710e1b4113f33c9b7039c to your computer and use it in GitHub Desktop.
{
const compose = (...fns) => (val) => fns.reduceRight((x, fn) => fn(x), val);
const sum = (result, item) => result + item;
const mult = (a, b) => a * b;
function wrap(xf){
return {
// 1. We require init as arg, so do not need here
init: function(){
throw new Error('init not supported');
},
// 2. Input one item at a time, passing
// each result to next iteration
step: xf,
// 3. Output last computed result
result: function(result){
return result;
}
}
}
/**
* @param xf A transformer or reducing function to be wrapped as a transformer
* @param init An initial value (like [])
* @param input An input source (like an array to reduce)
*/
function reduce(xf, init, input) {
if(typeof xf === 'function'){
// make sure we have a transformer
xf = wrap(xf);
}
const result = input.reduce(xf.step, init);
return xf.result(result);
}
function append(result, item) {
result.push(item);
return result;
}
const plus1 = (item) => item + 1;
/**
* transducer A transducer that defines the transformation
* stepper A stepper function or transformer (like append)
* init An initial value for the the stepper function (like [])
* input The input source (e.g. an array to transform)
*/
function transduce(transducer, stepper, init, input){
if (typeof stepper === 'function') {
// make sure we have a transformer for stepping
stepper = wrap(stepper);
}
// pass in stepper to create transformer
const xf = transducer(stepper);
// xf is now a transformer
// we now can use reduce defined above to
// iterate and transform input
return reduce(xf, init, input);
}
// define the mapping transducer
/**
* @param {function} fn a mapping function
* @param {object} xf transformer
*/
const map = (fn) => (xf) => ({
init() {
return xf.init();
},
step(result, item) {
const mapped = fn(item);
return xf.step(result, mapped);
},
result(result) {
return xf.result(result);
}
});
const plus2 = (input) => input + 2;
const transducerPlus2 = map(plus2);
// filter transducer
const filter = (predicate) => (xf) => ({
init() {
return xf.init();
},
step(value, item) {
const allow = predicate(item);
return allow
? xf.step(value, item)
: value;
},
result(result) {
return xf.result(result);
}
});
const isOdd = (num) => num % 2 === 1;
{
var transducer = filter(isOdd);
var stepper = append;
var init = [];
var input = [1,2,3,4,5];
var output = transduce(transducer, stepper, init, input);
console.log(output);
}
const isEqual = (a) => (b) => a === b;
const not = (predicate) => (val) => !predicate(val);
// remove transducer
const remove = (predicate) => filter(not(predicate));
{
var transducer = compose(
filter(isOdd), // [1,3,5]
map(plus1), // [2,4,6]
remove(isEqual(4))); // [2,6]
var stepper = append;
var init = [];
var input = [1,2,3,4,5];
var output = transduce(transducer, stepper, init, input);
console.log(output);
}
// stateful transformer using a stateless transducer
// skip n items at the beginning of the iteration
const drop = (n) => (xf) => {
let left = n;
return {
init() {
return xf.init();
},
step(value, item) {
if (left <= 0) {
return xf.step(value, item);
}
left--;
return value;
},
result(result) {
return xf.result(result);
}
}
}
{
var transducer = drop(2);
var stepper = append;
var init = [];
var input = [1,2,3,4,5];
var output = transduce(transducer, stepper, init, input);
console.log(output);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment