art-standard-lib
Version:
The Standard Library for JavaScript that aught to be.
360 lines (290 loc) • 11.1 kB
JavaScript
// Generated by CoffeeScript 1.12.7
(function() {
var Iteration, compactFlatten, deepArrayEach, isArrayOrArguments, isFunction, isObject, isPlainArray, isPlainObject, log, mergeInto, ref, ref1,
slice = [].slice;
ref = require('./Core'), compactFlatten = ref.compactFlatten, deepArrayEach = ref.deepArrayEach, isArrayOrArguments = ref.isArrayOrArguments, mergeInto = ref.mergeInto;
ref1 = require('./TypesExtended'), isPlainObject = ref1.isPlainObject, isObject = ref1.isObject, isFunction = ref1.isFunction, isPlainArray = ref1.isPlainArray;
log = function() {
var args, ref2;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return (ref2 = Neptune.Art.StandardLib).log.apply(ref2, args);
};
module.exports = Iteration = (function() {
var arrayIterableTest, emptyOptions, invokeNormalizedIteration, normalizedArray, normalizedEach, normalizedEachWhile, normalizedFind, normalizedInject, normalizedObject, returnFirst, returnSecond;
function Iteration() {}
returnFirst = function(a) {
return a;
};
returnSecond = function(a, b) {
return b;
};
arrayIterableTest = function(source) {
return (source != null ? source.length : void 0) >= 0;
};
emptyOptions = {};
/*
COMMON API:
IN: (source, withBlock = returnFirst) ->
IN: (source, options) ->
IN: (source, into, withBlock = returnFirst) ->
IN: (source, into, options) ->
source:
array-like (see arrayIterableTest)
use indexes to iterate
non-null
options:
with: withBlock
when: whenBlock
into: into
withBlock: (value, key, into, whenBlockResult) -> value
Generally, this generates the 'value' used for each part of the iteration.
When constructing a new collection, this is the value for each entry.
'find' and 'reduce' use this differently.
OUT: into
TODO:
- support ES6 iterables and iterators
- flatten: true - if source is an array, recurse into any sub-arrays
- compact: effectively `when: (v) -> v?` except you can also have your own when-function in addition, run after this one.
- skip: N - skip the first N values
- short: N - stop short N values
- until: () -> T/F - same args as withBlock, loop stops when true, executed after first withBlock
- while: () -> T/F - same args as withBlock, loop stops when false, executed before first whenBlock
- by: N -
N>0: only stop at every Nth value
N<0: iterate in reverse order, only stop at every abs(N)th value
*/
/*
each differences from the common-api:
1) into defaults to source
*/
Iteration.each = function(source, a, b) {
return invokeNormalizedIteration(normalizedEach, source, a, b);
};
normalizedEach = function(source, into, withBlock, options) {
var i, j, k, len, len1, v, w, whenBlock;
if (into === void 0) {
into = source;
}
if (options) {
whenBlock = options.when;
}
if (arrayIterableTest(source)) {
if (whenBlock) {
for (k = i = 0, len = source.length; i < len; k = ++i) {
v = source[k];
if (w = whenBlock(v, k)) {
withBlock(v, k, into, w);
}
}
} else {
for (k = j = 0, len1 = source.length; j < len1; k = ++j) {
v = source[k];
withBlock(v, k, into);
}
}
} else if (source != null) {
if (whenBlock) {
for (k in source) {
v = source[k];
if (w = whenBlock(v, k)) {
withBlock(v, k, into, w);
}
}
} else {
for (k in source) {
v = source[k];
withBlock(v, k, into);
}
}
}
return into;
};
/*
eachWhile differences from the common-api:
1) into defaults to source
2) stops when withBlock returns false
*/
Iteration.eachWhile = function(source, a, b) {
return invokeNormalizedIteration(normalizedEachWhile, source, a, b);
};
normalizedEachWhile = function(source, into, withBlock, options) {
var i, j, k, len, len1, v, w, whenBlock;
if (into === void 0) {
into = source;
}
if (options) {
whenBlock = options.when;
}
if (arrayIterableTest(source)) {
if (whenBlock) {
for (k = i = 0, len = source.length; i < len; k = ++i) {
v = source[k];
if (w = whenBlock(v, k)) {
if (!withBlock(v, k, into, w)) {
break;
}
}
}
} else {
for (k = j = 0, len1 = source.length; j < len1; k = ++j) {
v = source[k];
if (!withBlock(v, k, into)) {
break;
}
}
}
} else if (source != null) {
if (whenBlock) {
for (k in source) {
v = source[k];
if (w = whenBlock(v, k)) {
if (!withBlock(v, k, into, w)) {
break;
}
}
}
} else {
for (k in source) {
v = source[k];
if (!withBlock(v, k, into)) {
break;
}
}
}
}
return into;
};
/*
reduce differences from the common-api:
1) The with-block has a different argument order. Into is passed first instead of last:
with: (into, value, key, whenReturnValue) ->
This allows you to drop-in functions that take two args and reduce them to one like:
Math.max
add = (a, b) -> a + b
The default with-block still returns value (which is now the second argument).
1) if into starts out undefined:
for v = the first value (if whenBlock is present, the first value when whenBlock is true)
into = v
skip: withBlock
2) when withBlock is executed, into is updated:
into = withBlock()
*/
Iteration.reduce = function(source, a, b) {
return invokeNormalizedIteration(normalizedInject, source, a, b);
};
normalizedInject = function(source, into, withBlock, options) {
var intoSet;
if (source == null) {
return into;
}
normalizedEach(source, void 0, (intoSet = into !== void 0) ? function(v, k, __, w) {
return into = withBlock(into, v, k, w);
} : function(v, k, __, w) {
return into = intoSet ? withBlock(into, v, k, w) : (intoSet = true, v);
}, options);
return into;
};
/*
object differences from the common-api:
IN:
options.key: (value, key, into, whenBlockResult) -> value
1) into defaults to a new object ({}) (if into == undefined)
2) when withBlock is executed, into is updated:
if source is array-like:
into[v] = withBlock()
else
into[k] = withBlock()
*/
Iteration.object = function(source, a, b) {
return invokeNormalizedIteration(normalizedObject, source, a, b);
};
normalizedObject = function(source, into, withBlock, options) {
var keyFunction;
keyFunction = options.key || options.withKey || (arrayIterableTest(source) ? returnFirst : returnSecond);
return normalizedEach(source, into != null ? into : {}, function(v, k, into, w) {
return into[keyFunction(v, k, into, w)] = withBlock(v, k, into, w);
}, options);
};
/*
array differences from the common-api:
1) into defaults to a new array ([]) (if into == undefined)
2) when withBlock is executed, into is updated:
into.push withBlock()
*/
Iteration.array = function(source, a, b) {
return invokeNormalizedIteration(normalizedArray, source, a, b);
};
normalizedArray = function(source, into, withBlock, options) {
return normalizedEach(source, into != null ? into : [], function(v, k, into, w) {
return into.push(withBlock(v, k, into, w));
}, options);
};
/*
differs from common api:
1) returns the last value returned by withBlock or undefined if withBlock was never executed
2) stops if
a) whenBlock?: and it returned true (stops after withBlock is evaluated)
b) !whenBlock?: withBlock returns a truish value
*/
Iteration.find = function(source, a, b) {
return invokeNormalizedIteration(normalizedFind, source, a, b);
};
normalizedFind = function(source, into, withBlock, options) {
var found;
normalizedEachWhile(source, found = void 0, options.whenBlock ? function(v, k, into, w) {
found = withBlock(v, k, null, w);
return false;
} : function(v, k, into, w) {
return !(found = withBlock(v, k, null, w));
}, options);
return found;
};
/*
Normalizes input params for the 'iteration' function.
Since this normalizes multile params, and therefor would need to return
an new array or new object otherwise, we pass IN the iteration function
and pass the params directly to it. This keeps the computed params on the
stack and doesn't create new objects.
IN signature 1: (iteration, source, into, withBlock) ->
IN signature 2: (iteration, source, into, options) ->
IN signature 3: (iteration, source, withBlock) ->
IN signature 4: (iteration, source, options) ->
IN signature 5: (iteration, source) ->
IN:
iteration: (source, into, withBlock, options) -> out
The iteration function is invoked last with the computed args.
Its retults are returned.
IN:
source: passed directly through from inputs
into: passed directly through from inputs OR from options.into
withBlock: passed directly through from inputs OR from options.with
options: passed direftly through from inputs OR {}
(guaranteed to be set and a plainObject)
source: the source collection to be iterated over. Passed streight through.
into: passed through to 'iteration'
withBlock: passed through to 'iteration'
options: passed through to 'iteration' AND:
into: set 'into' from the options object
with: set 'withBlock' from the options object
OUT: out
*/
invokeNormalizedIteration = function(iteration, source, a, b) {
var into, options, withBlock;
options = b ? (into = a, b) : a;
if (isPlainObject(options)) {
if (into == null) {
into = options.into;
}
withBlock = options["with"];
} else {
if (isFunction(options)) {
withBlock = options;
}
options = emptyOptions;
}
return iteration(source, into, withBlock || returnFirst, options);
};
return Iteration;
})();
}).call(this);
//# sourceMappingURL=Iteration.js.map