/** @module collections */
const { curry } = require('../fp');
/** Reduces an array/object based on the reduction function
* and the initialization value passed
* @argument {function} fn Reduce function
* @argument {*} init Initial value to accumulate
* @argument {*} collection iterable collection to traverse
* @example
* //Arrays
* reduce((a,b)=> a+b,0,[1,2,3]) // -> 6
* reduce((a,b)=> a.concat(b+1),[],[1,2,3]) // -> [2,3,4]
* //Objects
* reduce((a,v,k)=> ({..a,[k],v+1}),{},{a:1,b:2}) // -> {a:2,b:3}
* @method
* */
const reduce = curry((fn, init, collection) => {
if (Array.isArray(collection)) {
return collection.reduce(fn, init);
}
return Object.entries(collection).reduce((acc, [k, v]) => fn(acc, v, k), init);
});
/** Maps over an array/object applying the passed function
* @argument {function} fn Reduce function
* @argument {*} collection iterable collection to traverse
* @example
* //Arrays
* map(x => x+1, [1,2,3]) // -> [2,3,4]
* //Objects
* map(x => x+1, {a:1,b:2}) // -> {a:2,b:3}
* @method
* */
const map = curry((fn, collection) => {
if (Array.isArray(collection)) {
return collection.map(fn);
}
return reduce((acc, v, k) => ({ ...acc, [k]: fn(v, k) }), {}, collection);
});
/** Filters an array/object based on the boolean evaluation of the passed function.
* @argument {function} fn Reduce function
* @argument {*} collection iterable collection to traverse
* @example
* //Arrays
* filter(x => x > 1, [1,2,3]) // -> [2,3]
* //Objects
* filter(x => x > 1, {a:1,b:2}) // -> {b:2}
* @method
* */
const filter = curry((fn, collection) => {
if (Array.isArray(collection)) {
return collection.filter(fn);
}
return reduce((acc, v, k) => (fn(v, k) && { ...acc, [k]: v }) || {}, {}, collection);
});
module.exports = { map, filter, reduce };