underarm
Version:
Transducers for JavaScript with Underscore API and Async extensions
310 lines (269 loc) • 8.88 kB
JavaScript
var dispatcher = require('redispatch')
module.exports = function(_r){
var _ = _r._,
as = _r.as,
// sentinel to ignore wrapped objects (maintain only last item)
IGNORE = _r.IGNORE = {}
// Transducer Functions
// --------------------
var core = require('transduce/core'),
value = _r.value = dispatcher(),
wrap = _r.wrap = dispatcher(),
unwrap = _r.unwrap = dispatcher(),
empty = _r.empty = dispatcher(),
append = _r.append = dispatcher(),
reduce = _r.reduce = dispatcher(),
_reduce = core.reduce,
_unreduced = core.unreduced,
transduce = _r.transduce = dispatcher(),
_transduce = core.transduce,
into = _r.into = dispatcher(),
transducer = _r.transducer = dispatcher(),
iterator = _r.iterator = dispatcher(),
_iterable = core.iterable,
_protocols = core.protocols,
toArray = _r.toArray = dispatcher(),
_toArray = core.into([]),
_util = require('./util-internal'),
iteratee = _r.iteratee = dispatcher()
_r.resolveSingleValue = resolveSingleValue
_r.resolveMultipleValues = resolveMultipleValues
_r.reduced = core.reduced
_r.isReduced = core.isReduced
_r.foldl = reduce
_r.inject = reduce
_r.deref = unwrap
_r.conj = append
_r.conjoin = append
_r.dispatch = dispatch
var compose = _r.compose = core.compose
_r.transformer = core.transformer
_r.iterable = _iterable
_r.protocols = _protocols
_r.isFunction = _util.isFunction
var isArray = _r.isArray = _util.isArray
var isString = _r.isString = _util.isString
_r.isRegExp = _util.isRegExp
_r.isNumber = _util.isNumber
_r.isUndefined = _util.isUndefined
_r.arrayPush = _util.arrayPush
_r.objectMerge = _util.objectMerge
_r.stringAppend = _util.stringAppend
var identity = _r.identity = _util.identity
// Dispatchers
// -----------
// Resolves the value of the wrapped object, similar to underscore.
// Returns an array, or single value (to match underscore API)
// depending on whether the chained transformation resolves to single value.
value.register(function(self){
if(!self._opts.resolveSingleValue){
return self.into()
}
var ret = self.into(IGNORE)
return ret === IGNORE ? void 0 : ret
})
_r.prototype.value = function(){
return value(this)
}
// Helper to mark transducer to expect single value when
// resolving. Only valid when chaining, but this should be passed
// when called as a function
function resolveSingleValue(self){
_resolveSingleValue(self, true)
}
// Helper to mark transducer to expect multiple values when
// resolving. Only valid when chaining, but this should be passed
// when called as a function.
function resolveMultipleValues(self){
_resolveSingleValue(self, false)
}
function _resolveSingleValue(self, single){
if(as(self)){
self._opts.resolveSingleValue = single
}
}
// Composes and returns the underlying wrapped functions for give chained object
transducer.register(function(self){
var fns = self._wrappedFns
return fns.length ? compose.apply(null, fns) : identity
})
_r.prototype.transducer = _r.prototype.compose = function() {
return transducer(this)
}
reduce.register(function(xf, init, coll) {
if(as(xf)){
xf = transducer(xf)
}
if (coll === null || coll === void 0) coll = empty(coll)
return _reduce(xf, init, coll)
})
// Calls transduce using the chained transformation if function not passed
_r.prototype.reduce = function(init, coll){
if(coll === void 0){
coll = this._wrapped
}
return reduce(this, init, coll)
}
transduce.register(function(xf, f, init, coll){
if(as(xf)){
xf = transducer(xf)
}
return unwrap(_transduce(xf, f, init, coll))
})
// Calls transduce using the chained transformation
_r.prototype.transduce = function(f, init, coll){
if(coll === void 0){
coll = this._wrapped
}
return transduce(this, f, init, coll)
}
// Returns a new coll consisting of to-coll with all of the items of
// from-coll conjoined. A transducer (step function) may be supplied.
into.register(function(to, xf, from){
if(from === void 0){
from = xf
xf = void 0
}
if(from === void 0){
from = empty()
}
if(as(xf)){
xf = transducer(xf)
}
if(to === void 0){
to = empty(from)
}
if(xf === void 0){
return reduce(append, to, from)
}
return transduce(xf, append, to, from)
})
// Calls into using the chained transformation
_r.prototype.into = function(to, from){
if(from === void 0){
from = this._wrapped
}
return into(to, this, from)
}
// Returns a new collection of the empty value of the from collection
toArray.register(function(xf, from){
if(as(xf)){
xf = transducer(xf)
}
if(arguments.length === 1){
return _toArray(xf)
}
return _toArray(xf, from)
})
// calls toArray with chained transformation and optional wrapped object
_r.prototype.toArray = function(from){
if(from === void 0){
from = this._wrapped
}
return toArray(this, from)
}
// Wraps a value used as source for use during chained transformation.
//
// Default returns value, or _r.empty() if undefined.
//
// Dispatch function. To support different types,
// call _r.unwrap.register
wrap.register(function(value){
if(isString(value)){
value = [value]
} else if(value === null || value === void 0){
value = empty()
}
return value
})
// Unwraps (deref) a possibly wrapped value
// Default unwraps values created with _r.reduced,
// or calls value() on chained _r transformations,
// otherwise returns parameter.
//
// Dispatch function. To support different types,
// call _r.unwrap.register
unwrap.register(function(value){
if(as(value)){
return value.value()
}
return _unreduced(value)
})
// Returns an iterator that has next function
// and returns {value, done}. Default looks for
// object with iterator Symbol (or '@@iterator').
// This is available with _r.iterator.Symbol
//
// Dispatch function. To support different types
// call _r.iterator.register and supply function that returns
// an iterator after checking the input using appropriate
// predicates. Return undefined if not supported, so other
// dispatched functions can be checked
iterator.register(function(value){
return _iterable(value)[_protocols.iterator]()
})
// Mostly internal function that generates a callback from the given value.
// For use with generating callbacks for map, filter, find, etc.
//
// Default returns _.iteratee.
//
// Dispatch function. To support different types
// call _r.iteratee.register and supply function that returns
// a callback after checking the input using appropriate
// predicates. Return undefined if not supported, so other
// dispatched functions can be checked
iteratee.register(function(value){
if(as(value)){
return _riteratee(value)
}
return _.iteratee(value)
})
function _riteratee(value){
return function(item){
return value.withSource(item).value()
}
}
// Returns empty object of the same type as argument.
// Default returns [] if isArray or undefined, {} if _.isObject
// and an internal sentinel to ignore otherwise
//
// Dispatch function. To support different types
// call _r.empty.register and supply function that returns
// an empty object after checking the input using appropriate
// predicates. Return undefined if not supported, so other
// dispatched functions can be checked
empty.register(function(obj){
if(obj === void 0 || isArray(obj) || iterator(obj)){
return [] // array if not specified or from array
} else if(_.isObject(obj)){
return {} // object if from object
}
// ignore by default. Default append just maintains last item.
return IGNORE
})
// Appends (conjoins) the item to the collection, and returns collection
//
// Dispatch function. To support different types
// call _r.append.register and supply function that append to the object
// (first param) with the item and optional key after checking the input
// using appropriate predicates.
//
// Return undefined if not supported, so other dispatched functions can be checked
append.register(function(obj, item){
if(isArray(obj)){
obj.push(item)
return obj
}
// just maintain last item
return item
})
// Reducer that dispatches to empty, unwrap and append
function Dispatch(){}
Dispatch.prototype.init = empty
Dispatch.prototype.result = unwrap
Dispatch.prototype.step = append
function dispatch(){
return new Dispatch()
}
}