The brass buckles on Underscore's utility belt - a contributors' library for Underscore.
While Underscore provides a bevy of useful tools to support functional programming in JavaScript, it can't (and shouldn't) be everything to everyone. Underscore-contrib is intended as a home for functions that, for various reasons, don't belong in Underscore proper. In particular, it aims to be:
First, you’ll need Underscore. Then you can grab the relevant underscore-contrib libraries and simply add something like the following to your pages:
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="underscore.object.builders.js"></script>
At the moment there are no cross-contrib dependencies (i.e. each library can stand by itself), but that may change in the future.
_.contrib is open sourced under the MIT license.
The _.contrib library currently contains a number of related capabilities, aggregated into the following files.
true
or false
based on some criteriaThe links above are to the annotated source code. Full-blown _.contrib documentation is in the works. Contributors welcomed.
Functions to build arrays. View Annotated Source
Signature: _.cat(... arrays:Array ...)
The _.cat
function provides a way to concatenate zero or more heterogeneous arrays into one.
_.cat(); // 0-args
//=> []
_.cat([]); // 1-arg, empty array
//=> []
_.cat([1,2,3]); // 1-arg
//=> []
_.cat([1,2,3],[4,5,6]); // 2-args
//=> [1,2,3,4,5,6]
_.cat([1,2,3],[4,5,6],[7]); // 3+ args
//=> [1,2,3,4,5,6,7]
Not every argument to _.cat
needs to be an array; other types are accepted.
Signature: _.cat(... elems:Any ...)
_.cat(1,[2],3,4); // mixed args
//=> [1,2,3,4]
The _.cat
function will also work with the arguments
object as if it were an array.
Signature: _.cat(... elems:Arguments ...)
function f(){ return _.cat(arguments, 4,5,6); }
f(1,2,3);
//=> [1,2,3,4,5,6]
Signature: _.cons(head:Any, tail:Array)
The _.cons
function provides a way to "construct" a new array by taking some element and putting it at the front of another array.
_.cons(0, []);
//=> [0]
_.cons(1, [2]);
//=> [1,2]
_.cons([0], [1,2,3]);
//=> [0,1,2,3]
The _.cons
function also can be used to create pairs if the second argument is not an array.
Signature: _.cons(head:Any, tail:Any)
_.cons(1, 2);
//=> [1,2]
_.cons([1], 2);
//=> [[1],2]
Finally, _.cons
will operate with the arguments
object.
Signature: _.cons(head:Any, tail:Arguments)
function f() { return _.cons(0, arguments) }
f(1,2,3);
//=> [0,1,2,3]
The _.partition
function, by default, accepts an array and a number and splits and returns a new array representing the original array split into some number of arrays of the given size:
_.partition([0,1,2,3], 2);
//=> , [[0,1],[2,3]]
If the original array cannot completely fulfill the partition scheme then the array returned will drop the undersized final partition:
_.partition([0,1,2,3,4], 2);
//=> , [[0,1],[2,3]]
You can pass an optional third argument to _.partition
to pad out the final array partition should it fall short. If the value given as the third argument is not an array then it is repeated the needed number of times:
_.partition([0,1,2,3,4], 3, '_');
//=> , [[0,1,2],[3,'_','_']]
If you given an array as the optional third argument then that array is used to pad in-place as needed:
_.partition([0,1,2,3,4], 3, ['a', 'b']);
//=> , [[0,1,2],[3,'a','b']]
The _.partitionAll
function is similar to _.partition
except for the following. First, _.partionAll
will never drop short partitions from the end:
_.partitionAll([0,1,2,3,4], 3);
//=> , [[0,1,2],[3]]
Also, _.paritionAll
takes an optional third argument signifying that paritions should be built from skipped regions:
_.partitionAll(_.range(1), 2, 4);
//=> [[0,1],[4,5],[8,9]]
There are times when a mapping operation produces results contained in arrays, but the final result should be flattened one level. For these circumstances you can use _.mapcat
to produce results:
var array = [1,2,null,4,undefined,6];
var errors = _.mapcat(array, function(e,i) {
if (e == null) {
return ["Element @" + i + " is bad"];
}
else {
return [];
}
});
Inspecting the contents of errors
shows:
["Element @2 is bad", "Element @4 is bad"]
The _.mapcat
function is equivalent to _.cat.apply(array, _.map(array,fun))
.
The _.interpose
function takes an array and an element and returns a new array with the given element inserted betwixt every element in the original array:
_.interpose([1,2,3], 0);
//=> [1,0,2,0,3]
If there are no betweens (i.e. empty and single-element arrays), then the original array is returned:
_.interpose([1], 0);
//=> [1]
_.interpose([], 0);
//=> []
The _.weave
function works similarly to _.interpose
(shown above) except that it accepts an array used as the interposition values. In other words, _.weave
takes two arrays and returns a new array with the original elements woven together. An example would help:
_.weave(['a','b','c'], [1,2,3]);
//=> ['a',1,'b',2,'c',3]
The array returned from _.weave
will be as long as the longest array given with the woven entries stopping according to the shortest array:
_.weave(['a','b','c'], [1]);
//=> ['a',1,'b','c']
The _.interleave
function is an alias for _.weave
.
Signature: _.repeat(t:Integer, value:Any)
The _.repeat
function takes an integer value used to build an array of that size containing the value given:
_.repeat(5, 'a');
//=> ['a','a','a','a','a']
The _.cycle
function takes an integer value used to build an array of that size containing the number of iterations through the given array, strung end-to-end as many times as needed. An example is probably more instructive:
_.cycle(5, [1,2,3]);
//=> [1,2,3,1,2]
The _.splitAt
function takes an array and a numeric index and returns a new array with two embedded arrays representing a split of the original array at the index provided:
_.splitAt([1,2,3,4,5], 2);
//=> [[1,2],[3,4,5]]
_.splitAt([1,2,3,4,5], 0);
//=> [[],[1,2,3,4,5]]
The operation of _.splitAt
is safe if the index provided is outside the range of legal indices:
_.splitAt([1,2,3,4,5], 20000);
//=> [[1,2,3,4,5],[]]
_.splitAt([1,2,3,4,5], -1000);
//=> [[],[1,2,3,4,5]]
_.splitAt([], 0);
//=> [[],[]]
The _.takeSkipping
function takes an array and a number and returns a new array containing every nth element in the original array:
_.takeSkipping(_.range(10), 2);
//=> [0,2,4,6,8]
The _.takeSkipping
function is safe against numbers larger or smaller than the array size:
_.takeSkipping(_.range(10), 100000);
//=> [0]
_.takeSkipping(_.range(10), -100);
//=> []
The _.reductions
function is similar to Underscore's builtin _.reduce
function except that it returns an array of every intermediate value in the folding operation:
_.reductions([1,2,3,4,5], function(agg, n) {
return agg + n;
}, 0);
//=> [1,3,6,10,15]
The last element in the array returned from _.reductions
is the answer that you would get if you had just chosen to use _.reduce
.
The _.keepIndexed
function takes an array and a function and returns a new array filled with the non-null return results of the given function on the elements or keys in the given array:
_.keepIndexed([1,2,3], function(k) {
return i === 1 || i === 2;
});
//=> [false, true, true]
If you return either null
or undefined
then the result is dropped from the resulting array:
_.keepIndexed(['a','b','c'], function(k, v) {
if (k === 1) return v;
});
//=> ['b']
The _.iterateUntil
function takes a function used as a result generator, a function used as a stop-check and a seed value and returns an array of each generated result. The operation of _.iterateUntil
is such that the result generator is passed the seed to start and each subsequent result which will continue until a result fails the check function (i.e. returns falsey). An example is best:
var dec = function(n) { return n - 1; };
var isPos = function(n) { return n > 0; };
The dec
result generator takes a number and decrements it by one. The isPos
predicate takes a number and returns true
if it's positive. Using these two functions you can build an array of every number from 6
to 0
, inclusive:
_.iterateUntil(dec, isPos, 6);
//=> [5,4,3,2,1]
That is, the array only contains every number from 5
down to 1
because when the result of dec
got to 0
the isPos
check failed (i.e. went falsey) thus terminating the execution.
Functions to take things from arrays. View Annotated Source
The _.second
function is a convenience for the equivalent array[1]
:
_.second(['a','b']);
//=> 'b'
_.map([['a','b'], _.range(10,20)], _.second);
//=> ['b',11]
You can also pass an optional number to the _.second
function to take a number of elements from an array starting with the second element and ending at the given index:
_.second(_.range(10), 5)
//=> [1, 2, 3, 4]
The _.third
function is a convenience for the equivalent array[2]
:
_.third(['a','b','c']);
//=> 'c'
_.map([['a','b','c'], _.range(10,20)], _.third);
//=> ['c',12]
You can also pass an optional number to the _.third
function to take a number of elements from an array starting with the third element and ending at the given index:
_.third(_.range(10), 5)
//=> [2, 3, 4]
The _.nth
function is a convenience for the equivalent array[n]
:
_.nth(['a','b','c'], 2);
//=> 'c'
If given an index out of bounds then _.nth
will return undefined
:
_.nth(['a','b','c'], 2000);
//=> undefined
The _.nth
function can also be used in conjunction with _.map
and _.compact
like so:
var b = [['a'],['b'],[]];
_.compact(_.map(b, function(e) { return _.nth(e,0) }));
//=> ['a','b']
If wrapping a function around _.nth
is too tedious or you'd like to partially apply the index then Underscore-contrib offers any of _.flip2
, _.fix
or rcurry2
to solve this.
The _.takeWhile
function takes an array and a function and returns a new array containing the first n elements in the original array for which the given function returns a truthy value:
var isNeg = function(n) { return n < 0; };
_.takeWhile([-2,-1,0,1,2], isNeg);
//=> [-2,-1]
The _.dropWhile
function works similarly except that it drops elements from the original array for which the given function returns a truthy value:
_.dropWhile([-2,-1,0,1,2], isNeg);
//=> [0,1,2]