Skip to content

Instantly share code, notes, and snippets.

@ChillyBwoy
Last active March 30, 2017 16:02
Show Gist options
  • Save ChillyBwoy/1d933c298d5d199fa8b48ed43a4cbde7 to your computer and use it in GitHub Desktop.
Save ChillyBwoy/1d933c298d5d199fa8b48ed43a4cbde7 to your computer and use it in GitHub Desktop.
Transducers JavaScript
class Reduced {
static isReduced(inst) {
return (inst instanceof Reduced);
}
constructor(wrapped) {
this._wrapped = wrapped;
}
unwrap() {
return this._wrapped;
}
}
function curryArgs() {
return (first) => (all) => (last) => (...args) => {
if (args.length === 0) {
return first(...args);
}
if (args.length === 1) {
return last(...args);
}
if (args.length === 2) {
return all(...args);
}
};
}
function prepareArgs(first, all, last) {
return (...args) => {
if (args.length === 0) {
return first(...args);
}
if (args.length === 1) {
return last(...args);
}
if (args.length === 2) {
return all(...args);
}
};
}
function reduce(collection, fn, seed) {
let result = seed;
for (let item of collection) {
result = fn(result, item);
if (Reduced.isReduced(result)) {
return result.unwrap();
}
}
return result;
}
function append(result, item) {
if (arguments.length === 2) {
return result.concat([item]);
}
if (arguments.length === 1) {
return result;
}
if (arguments.length === 0) {
return [];
}
}
function transduce(collection, transducer, append) {
const step = transducer(append);
const seed = step();
const result = reduce(collection, step, seed);
return step(result);
}
// transducers constructors:
function take(n) {
return (step) => {
let count = 0;
const onFirst = () => step();
const onLast = result => step(result);
const onAll = (result, item) => {
if (count++ < n) {
return step(result, item);
}
return new Reduced(result);
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function map(fn) {
return (step) => (...args) => {
if (args.length === 2) {
const [result, item] = args;
return step(result, fn(item));
}
if (args.length === 1) {
const [result] = args;
return step(result);
}
if (args.length === 0) {
return step();
}
};
}
function filter(predicate) {
return (step) => {
const onFirst = () => step();
const onLast = result => step(result);
const onAll = (result, item) => {
if (predicate(item)) {
return step(result, item);
}
return result;
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function flatten() {
return (step) => {
const onFirst = () => step();
const onLast = result => step(result);
const onAll = (result, item) => {
for (let i = 0; i < item.length; i++) {
result = step(result, item[i]);
}
return result;
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function partition(n) {
if (n < 1) {
throw new Error("n > 1");
}
return (step) => {
let current = [];
const onFirst = () => step();
const onLast = result => {
if (current.length > 0) {
result = step(result, current);
return step(result);
}
};
const onAll = (result, item) => {
current.push(item);
if (current.length === n) {
result = step(result, current);
current = [];
}
return result;
};
return prepareArgs(onFirst, onAll, onLast);
};
}
// sample
const initial = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
const addOneT = map(x => x + 1);
const lessTnan4T = filter(x => x < 4);
const flattenT = flatten();
const first5T = take(5);
const by3ItemsT = partition(3);
const addOne_lessTnan4 = (step) => addOneT(lessTnan4T(step));
console.log(transduce(initial, addOneT, append));
console.log(transduce(initial, lessTnan4T, append));
console.log(transduce(initial, addOne_lessTnan4, append));
console.log(transduce([[1, 2], [], [3]], flattenT, append));
console.log(transduce(initial, first5T, append));
console.log(transduce(initial, by3ItemsT, append));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment