Work in progress sketch based on following along with the video course at https://egghead.io/courses/quickly-transform-data-with-transducers.
const id = (value) => value;
const isArray = (value) => Array.isArray(value);
const isNumber = (value) => typeof value === 'number';
const isObject = (value) => Object.prototype.toString.call(value) === '[object Object]';
const compose = (...fns) => fns.reduce((prevFn, nextFn) => (...args) => nextFn(prevFn(...args)), id);
const map = (mapper) => (reducer) => (acc, value) => reducer(acc, mapper(value));
const filter = (isNeeded) => (reducer) => (acc, value) => (isNeeded(value) ? reducer(acc, value) : acc);
const reduceAddable = (value, nextValue) => value + nextValue;
const reduceObject = (obj, value) => Object.assign(obj, value);
const reduceArray = (array, value) => array.concat(value);
const transduce = (mapper, reducer, initialValue, iterable) => {
let acc = initialValue;
const transformedReducer = mapper(reducer);
if (isObject(iterable)) {
for (const key in iterable) {
acc = transformedReducer(acc, [key, iterable[key]]);
}
} else {
for (const value of iterable) {
acc = transformedReducer(acc, value);
}
}
return acc;
};
const into = (mapper, initialValue, iterable) => {
if (isArray(initialValue)) return transduce(mapper, reduceArray, initialValue, iterable);
if (isObject(initialValue)) return transduce(mapper, reduceObject, initialValue, iterable);
throw new Error('into: only arrays and objects as `initialValue` are supported');
};
const seq = (mapper, iterable) => {
if (isArray(iterable)) return transduce(mapper, reduceArray, [], iterable);
if (isObject(value)) return transduce(mapper, reduceObject, {}, iterable);
throw new Error('seq: Unsupported collection type');
};
const filterIsEven = filter((a) => a % 2 === 0);
const mapDouble = map((a) => a * 2);
const mapToObject = map((value) => ({ [value]: value }));
const withDoubledEvenNumber = compose(
mapDouble,
filterIsEven
);
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const resultAsArray1 = numbers.reduce(withDoubledEvenNumber(reduceArray), []);
const resultAsArray2 = transduce(withDoubledEvenNumber, reduceArray, [], numbers);
const resultAsArray3 = into(withDoubledEvenNumber, [], numbers);
const resultAsArray4 = seq(withDoubledEvenNumber, numbers);
numbers.forEach((number) => {
console.log('withDoubledEvenNumber([], %s) === %s', number, withDoubledEvenNumber(reduceArray)([], number));
});
console.log('resultAsArray1', resultAsArray1);
console.log('resultAsArray2', resultAsArray2);
console.log('resultAsArray3', resultAsArray3);
console.log('resultAsArray4', resultAsArray4);
const withDoubledEvenNumberAsObject = compose(
mapToObject,
withDoubledEvenNumber
);
const resultAsObject1 = numbers.reduce(withDoubledEvenNumberAsObject(reduceObject), {});
const resultAsObject2 = transduce(withDoubledEvenNumberAsObject, reduceObject, {}, numbers);
const resultAsObject3 = into(withDoubledEvenNumberAsObject, {}, numbers);
numbers.forEach((number) => {
console.log(
'withDoubledEvenNumberAsObject({}, %s) === %s',
number,
withDoubledEvenNumberAsObject(reduceObject)({}, number)
);
});
console.log('resultAsObject1', resultAsObject1);
console.log('resultAsObject2', resultAsObject2);
console.log('resultAsObject3', resultAsObject3);
withDoubledEvenNumber([], 1) ===
withDoubledEvenNumber([], 2) === 4
withDoubledEvenNumber([], 3) ===
withDoubledEvenNumber([], 4) === 8
withDoubledEvenNumber([], 5) ===
withDoubledEvenNumber([], 6) === 12
withDoubledEvenNumber([], 7) ===
withDoubledEvenNumber([], 8) === 16
withDoubledEvenNumber([], 9) ===
resultAsArray1 [ 4, 8, 12, 16 ]
resultAsArray2 [ 4, 8, 12, 16 ]
resultAsArray3 [ 4, 8, 12, 16 ]
resultAsArray4 [ 4, 8, 12, 16 ]
withDoubledEvenNumberAsObject({}, 1) === {}
withDoubledEvenNumberAsObject({}, 2) === {"4":4}
withDoubledEvenNumberAsObject({}, 3) === {}
withDoubledEvenNumberAsObject({}, 4) === {"8":8}
withDoubledEvenNumberAsObject({}, 5) === {}
withDoubledEvenNumberAsObject({}, 6) === {"12":12}
withDoubledEvenNumberAsObject({}, 7) === {}
withDoubledEvenNumberAsObject({}, 8) === {"16":16}
withDoubledEvenNumberAsObject({}, 9) === {}
resultAsObject1 { '4': 4, '8': 8, '12': 12, '16': 16 }
resultAsObject2 { '4': 4, '8': 8, '12': 12, '16': 16 }
resultAsObject3 { '4': 4, '8': 8, '12': 12, '16': 16 }