foop
Version:
interfaces that describe their intentions.
98 lines (87 loc) • 2.48 kB
JavaScript
const isNil = require('../../is/nullOrUndefined')
const isFunction = require('../../is/function')
const hasOwnProperty = require('../../util/hasOwnProperty')
const symIterator = require('../../symbols/iterator')
const isArrayLike = require('../../is/arrayLike')
const bind = require('../../fp/bind')
const ON_INIT = '@@transducer/init'
const ON_STEP = '@@transducer/step'
const ON_REDUCED = '@@transducer/reduced'
const ON_VALUE = '@@transducer/value'
const ON_RESULT = '@@transducer/result'
const FANTASY_LAND_REDUCE = 'fantasy-land/reduce'
var _xwrap = (function() {
function XWrap(fn) {
this.f = fn
}
XWrap.prototype[ON_INIT] = function() {
throw new Error('init not implemented on XWrap')
}
XWrap.prototype[ON_RESULT] = function(acc) { return acc }
XWrap.prototype[ON_STEP] = function(acc, x) {
return this.f(acc, x)
}
// @TODO construct?
return function _xwrap(fn) {
return new XWrap(fn)
}
})()
/**
* @name _reduce
* @since 5.0.0-beta.6
* @memberOf loop
* @return {Function}
*/
// list aka functor
const reduce = (function() {
function _arrayReduce(xf, acc, list) {
let idx = 0
const len = list.length
while (idx < len) {
acc = xf[ON_STEP](acc, list[idx])
if (acc && acc[ON_REDUCED]) {
acc = acc[ON_VALUE]
break
}
idx += 1
}
return xf[ON_RESULT](acc)
}
function _iterableReduce(xf, acc, iter) {
let step = iter.next()
while (!step.done) {
acc = xf[ON_STEP](acc, step.value)
if (hasOwnProperty(acc, ON_REDUCED)) {
acc = acc[ON_VALUE]
break
}
step = iter.next()
}
return xf[ON_RESULT](acc)
}
function _methodReduce(xf, acc, obj, methodName) {
return xf[ON_RESULT](obj[methodName](bind(xf[ON_STEP], xf), acc))
}
return function _reduce(fn, acc, list) {
if (isFunction(fn)) {
fn = _xwrap(fn)
}
if (isArrayLike(list)) {
return _arrayReduce(fn, acc, list)
}
else if (isFunction(list[FANTASY_LAND_REDUCE])) {
return _methodReduce(fn, acc, list, FANTASY_LAND_REDUCE)
}
else if (!isNil(list[symIterator])) {
return _iterableReduce(fn, acc, list[symIterator]())
}
else if (isFunction(list.next)) {
return _iterableReduce(fn, acc, list)
}
else if (isFunction(list.reduce)) {
return _methodReduce(fn, acc, list, 'reduce')
}
throw new TypeError('reduce: list must be array or iterable')
}
})()
module.exports = reduce