Underscore.cfc is a port of Underscore.js
for Coldfusion. It is a utility-belt library that provides a lot of the
functional programming support that you would expect in Prototype.js
(or Ruby).
Underscore.cfc provides dozens of functions that
support both the usual functional suspects: map, select, invoke - as
well as more specialized helpers: function binding, sorting, deep
equality testing, and so on. It delegates to built-in functions where
applicable.
Underscore.cfc is compatible with Adobe Coldfusion 10 and Railo 4.
The project is hosted on GitHub. Contributions are welcome.
each _.each(collection, iterator, [context]) : void
Iterates
over a collection of elements, yielding each in turn to an iterator
function. The iterator is bound to the context object (component or
struct), if one is passed. Each invocation of iterator is called with
three arguments: (element, index, collection, this). If collection is an
object/struct, iterator's arguments will be (value, key, collection,
this).
_.each([1, 2, 3], function(num){ writeDump(num); });
=> dumps each number in turn...
_.each({one : 1, two : 2, three : 3}, function(num, key){ writeDump(num); });
=> dumps each number in turn...
map _.map(collection, iterator, [context]) : array
Produces
a new array of values by mapping each value in collection through a
transformation function (iterator). If collection is an object/struct,
iterator's arguments will be (value, key, collection, this).
_.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
_.map({one : 1, two : 2, three : 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
reduce _.reduce(collection, iterator, memo, [context]) : any
Also
known as inject and foldl, reduce boils down a collection of values
into a single value. Memo is the initial state of the reduction, and
each successive step of it should be returned by iterator.
sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
=> 6
reduceRight _.reduceRight(collection, [iterator], memo, [context])
The right-associative version of reduce.
list = [[0, 1], [2, 3], [4, 5]];
flat = _.reduceRight(list, function(a, b) { return _.concat(a, b); }, []);
=> [4, 5, 2, 3, 0, 1]
find _.find(collection, iterator, [context]) : any
Looks
through each value in the collection, returning the first one that
passes a truth test (iterator). The function returns as soon as it finds
an acceptable element, and doesn't traverse the entire collection.
even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> 2
filter _.filter(collection, iterator, [context]) : array
Looks through each value in the collection, returning an array of all the values that pass a truth test (iterator).
evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
where _.where(list, properties) : array
Looks
through each value in the list, returning an array of all the values
that contain all of the key-value pairs listed in properties.
_.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
{title: "The Tempest", author: "Shakespeare", year: 1611}]
findWhere _.findWhere(collection, properties) : any
Looks through the collection and returns the first value that matches all of the key-value pairs listed in properties.
_.findWhere(publicServicePulitzers, {newsroom: "The New York Times"});
=> {year: 1918, newsroom: "The New York Times",
reason: "For its public service in publishing in full so many official reports,
documents and speeches by European statesmen relating to the progress and
conduct of the war."}
reject _.reject(collection, iterator, [context]) : array
Returns the values in collection without the elements that the truth test (iterator) passes. The opposite of filter.
odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [1, 3, 5]
all _.all(collection, iterator, [context]) : boolean
Returns true if all of the values in the collection pass the iterator truth test.
_.all([true, 1, 'no'], _.identity);
=> false
any _.any(collection, [iterator], [context]) : boolean
Returns
true if any of the values in the collection pass the iterator truth
test. Short-circuits and stops traversing the collection if a true
element is found.
_.any([0, 'yes', false]);
=> true
include _.include(collection, value) : boolean
Returns true if the value is present in the collection.
_.include([1, 2, 3], 3);
=> true
invoke _.invoke(collection, methodName, [arguments]) : array
Calls
the method named by methodName on each value in the collection. The
arguments struct passed to invoke will be forwarded on to the method
invocation.
_.invoke([{fun: function(){ return 1; }}], 'fun');
=> [1]
pluck _.pluck(collection, propertyName) : array
A convenient version of what is perhaps the most common use-case for map: extracting a collection of property values.
stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
_.pluck(stooges, 'name');
=> ["moe", "larry", "curly"]
max _.max(collection, [iterator], [context]) : any
Returns
the maximum value in collection. If iterator is passed, it will be used
on each value to generate the criterion by which the value is ranked.
stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
_.max(stooges, function(stooge){ return stooge.age; });
=> {name : 'curly', age : 60};
min _.min(collection, [iterator], [context]) : any
Returns
the minimum value in collection. If iterator is passed, it will be used
on each value to generate the criterion by which the value is ranked.
numbers = [10, 5, 100, 2, 1000];
_.min(numbers);
=> 2
sortBy _.sortBy(collection, [iterator], [context]) : array
Returns
a sorted copy of collection, ranked in ascending order by the results
of running each value through iterator. Iterator may also be the string
name of the object key to sort by. Delegates to arraySort().
_.sortBy([6, 2, 4, 3, 5, 1], function(num){ return num; });
=> [1, 2, 3, 4, 5, 6]
groupBy _.groupBy(collection, iterator) : struct
Splits
a collection into sets, grouped by the result of running each value
through iterator. If iterator is a string instead of a function, groups
by the property named by iterator on each of the values.
_.groupBy([1.3, 2.1, 2.4], function(num){ return fix(num); });
=> {1: [1.3], 2: [2.1, 2.4]}
_.groupBy(['one', 'two', 'three'], function(num) { return len(num); });
=> {3: ["one", "two"], 5: ["three"]}
countBy _.countBy(collection, iterator) : struct
Sorts
a collection into groups and returns a count for the number of objects
in each group. Similar to groupBy, but instead of returning a list of
values, returns a count for the number of values in that group.
_.countBy([1, 2, 3, 4, 5], function(num) { return num % 2 == 0 ? 'even' : 'odd'; });
=> {odd: 3, even: 2}
sortedIndex _.sortedIndex(collection, value, [iterator]) : numeric
Uses
a binary search to determine the index at which the value should be
inserted into the collection in order to maintain the collection's
sorted order. If an iterator is passed, it will be used to compute the
sort ranking of each value.
_.sortedIndex([10, 20, 30, 40, 50], 35);
=> 4
shuffle _.shuffle(array) : array
Returns a shuffled copy of the array, using a version of the Fisher-Yates shuffle.
_.shuffle([1, 2, 3, 4, 5, 6]);
=> [4, 1, 6, 3, 5, 2]
toArray _.toArray(collection) : array
Converts the collection (object, struct, query, xml, or cf-list), into an array.
_.toArray({a:10,b:20});
=> [10, 20]
toQuery _.toQuery(array, [columnNames], [columnTypes]) : query
Converts
an array of structs to a Coldfusion query. Columns are created
dynamically unless a comma-delimited list of column names are provided.
Column types are "varchar" unless a comma-delimited list of column types
is provided. Delegates to native QueryNew().
_.toQuery([{someColumn: "row 1"}]);
=> (result is a query with one column titled "someColumn" and one row containing "row 1
toXml _.toXml(collection, [*elementNames]) : xml
Converts
a collection to an XML object. Element names default to variable types.
If provided, element names will be assigned to unnamed elements (any
element without a key) in the order they are listed.
_.toXml([1, 2]);
=> <array><element>1</element><element>2</element></array>
_.toXml([3], 'myArray', 'number');
=> <myArray><number>3</number><myArray>
size _.size(collection) : numeric
Return the number of values in the collection. Note: A simple value will be considered a single list item.
_.size({one : 1, two : 2, three : 3});
=> 3
_.size(99);
=> 1
_.size("101");
=> 1
_.size();
=> 0
first _.first(array, [n]) : any
Returns the first element of an array. Passing n will return the first n elements of the array.
_.first([5, 4, 3, 2, 1]);
=> 5
initial _.initial(array, [n]) : array
Returns
everything but the last entry of the array. Especially useful on the
arguments object. Pass n to exclude the last n elements from the result.
Note: CF arrays start at an index of 1
_.initial([5, 4, 3, 2, 1]);
=> [5, 4, 3, 2]
last _.last(array, [n]) : any
Returns the last element of an array. Passing n will return the last n elements of the array.
_.last([5, 4, 3, 2, 1]);
=> 1
rest _.rest(array, [index]) : array
Returns the rest of the elements in an array. Pass an index to return the values of the array from that index onward.
_.rest([5, 4, 3, 2, 1]);
=> [4, 3, 2, 1]
compact _.compact(array) : array
Returns a copy of the array with all falsy values removed. In Coldfusion, false, 0, and "" are all falsy.
_.compact([0, 1, false, 2, '', 3]);
=> [1, 2, 3]
flatten _.flatten(array, [shallow]) : array
Flattens a nested array (the nesting can be to any depth). If you pass shallow, the array will only be flattened a single level.
_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];
_.flatten([1, [2], [3, [[4]]]], true);
=> [1, 2, 3, [[4]]];
without _.without(array, [values]) : array
Returns a copy of the array with all instances of the values removed.
_.without([1, 2, 1, 0, 3, 1, 4], [0, 1]);
=> [2, 3, 4]
union _.union(*arrays) : array
Computes
the union of the passed-in arrays: the collection of unique items, in
order, that are present in one or more of the arrays.
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
intersection _.intersection(*arrays) : array
Computes
the collection of values that are the intersection of all the arrays.
Each value in the result is present in each of the arrays.
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
difference _.difference(array, others) : array
Similar to without, but returns the values from array that are not present in the other arrays.
_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]
uniq _.uniq(array, [isSorted], [iterator]) : array
Produces
a duplicate-free version of the array. If you know in advance that the
array is sorted, passing true for isSorted will run a much faster
algorithm. If you want to compute unique items based on a
transformation, pass an iterator function.
_.uniq([1, 2, 1, 3, 1, 4]);
=> [1, 2, 3, 4]
zip _.zip(*arrays) : array
Merges
together the values of each of the arrays with the values at the
corresponding position. Useful when you have separate data sources that
are coordinated through matching array indexes.
_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
object _.object(array, [values])
Converts an array into a struct. Pass either a single array of [key, value] pairs, or an array of keys, and an array of values.
_.object([['MYKEY', 'myVal']]);
=> {MYKEY: 'myVal'}
_.object(['KEY'], ['myVal']);
=> {KEY: 'value'}
indexOf _.indexOf(array, value, [isSorted/fromIndex]) : numeric
Returns
the index at which value can be found in the array, or 0 if value is
not present in the array. Uses the native ArrayFind() function. If
you're working with a large array, and you know that the array is
already sorted, pass true for isSorted to use a faster binary search.
_.indexOf([1, 2, 3], 2);
=> 2
lastIndexOf _.lastIndexOf(array, value) : numeric
Returns the index of the last occurrence of value in the array, or 0 if value is not present.
_.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
=> 5
range _.range([start], stop, [step]) : array
A
function to create flexibly-numbered arrays of integers, handy for each
and map loops. start, if omitted, defaults to 0; step defaults to 1.
Returns an array of integers from start to stop, incremented (or
decremented) by step, exclusive.
_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []
concat _.concat(*arrays) : array
Concatenates any number of arrays together an returns the result. Delegates to ArrayAppend().
_.concat([1, 2, 3], [4, 5, 6]);
=> [1, 2, 3, 4, 5, 6];
reverse _.reverse(array) : array
Returns a copy of the array in reverse order.
_.reverse([1, 2, 3]);
=> [3, 2, 1]
takeWhile _.takeWhile(array) : array
Appends values to a new array as long as the iterator is true.
_.takeWhile([1, 2, 3, 4, 1, 2], function(val) { return val < 3; });
=> [1, 2]
splice _.splice(array, index, howMany, [*items]) : array
Returns
a copy of the array with howMany elements removed. Optionally inserts
items at the index. Note: differs from Javascript splice() in that it
does not return the removed elements.
_.splice([10, 90, 30], 2, 2);
=> [10]
_.splice([10, 90, 30], 2, 1, 20);
=> [10, 20, 30]
push _.push(array, *values) : array
Returns a new array with values appended to the end of it. Does not modify the original array.
_.push([1, 2], 3, 4);
=> [1, 2, 3, 4]
unshift _.unshift(array, *values) : array
Returns a new array with values prepended to the array. Does not modify the original array.
_.unshift(["end"], "start", "middle");
=> ["start", "middle", "end"]
join _.join(array, [separator]) : string
Returns a string with all array elements joined together. Default separator is a single space.
_.join([1, 2], " and ");
=> "1 and 2
split _.split(string, [delimiters], [includeEmptyFields], [multiCharacterDelimiter]) : array
Returns
an array of strings from the string, separating the string at each of
the delimeters passed to it (default is comma). Note: This is simply an
alias for listToArray().
_.split("hello", '');
=> ['h', 'e', 'l', 'l', 'o']
slice _.slice(array, [from], [to]) : array
Returns a subsection of the array. Negative values for to and from offset from the end of the array.
_.slice([1, 2, 3, 4]);
=> [2, 3, 4]
_.slice([1, 2, 3, 4], 3);
=> [3, 4]
_.slice([1, 2, 3, 4], 2, -1);
=> [2, 3]
_.slice([1, 2, 3, 4], -3, -1);
=> [2, 3]
bind _.bind(function, object, [*arguments]) : function
Bind
a function to a structure, meaning that whenever the function is
called, the value of "this" will be the structure. Optionally, bind
arguments to the function to pre-fill them, also known as partial
application.
func = function(args, this){ return args.greeting & ': ' & this.name; };
func = _.bind(func, {name : 'moe'}, {greeting: 'hi'});
func();
=> 'hi: moe'
bindAll _.bindAll(object, [*methodNames]) : any
Bind all of an object's methods to that object. Useful for ensuring that all callbacks defined on an object belong to it.
greeter = {hello: 'Hello, ', greet: function(this){ return this.hello & 'World!'; }};
_.bindAll(greeter);
greeter.greet();
=> 'Hello, World!'
memoize _.memoize(function, [hashFunction]) : function
Memoizes
a given function by caching the computed result. Useful for speeding up
slow-running computations. If passed an optional hashFunction, it will
be used to compute the hash key for storing the result, based on the
arguments to the original function. The default hashFunction just uses
the first argument to the memoized function as the key.
fibonacci = _.memoize(function(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); });
delay _.delay(function, wait, arguments) : any
Delays a function for the given number of milliseconds, and then calls it with the arguments supplied in the args struct.
_.delay(function (msg) {return msg;}, 1000, {msg = "hi"});
=> "hi" // appears after one second
once _.once(function) : function
Returns a function that will be executed at most one time, no matter how often you call it. Useful for lazy initialization.
i = 0;
once = _.once(function () { i = i+1; return i; });
once();
=> 1
once();
=> 1
debounce _.debounce(function, wait, immediate) : function
Returns
a function that, as long as it continues to be invoked, will not be
triggered. The function will be called after it stops being called for N
milliseconds. If immediate is passed, trigger the function on the
leading edge, instead of the trailing. (Immediate requires a Wait
cooldown period beteween calls.)
keepCalm = _.debounce(function(){}, 300, true);
for (var i = 0; i<10; i++){ keepCalm(); }
=>//function argument is called only once
after _.after(count, function) : any
Returns
a function that will only be executed after being called N times. When
count <= 0, the result of calling the function immediately is
returned.
func = function () { writeOutput("hi"); };
callFuncAfterTwo = _.after(2, func);
callFuncAfterTwo();
=> // nothing
callFuncAfterTwo();
=> 'hi'
wrap _.wrap(function, wrapper) : function
Returns
the first function passed as an argument to the second, allowing you to
adjust arguments, run code before and after, and conditionally execute
the original function.
hello = function(name) { return "hello: " & name; };
hello = _.wrap(hello, function(func) {
return "before, " & func("moe") & ", after";
});
hello();
=> 'before, hello: moe, after'
compose _.compose(*functions) : function
Returns
a function that is the composition of a list of functions, each
function consumes the return value of the function that follows. In math
terms, composing the functions f(), g(), and h() produces f(g(h())).
greet = function(name){ return "hi: " & name; };
exclaim = function(statement){ return statement & "!"; };
welcome = _.compose(exclaim, greet);
welcome('moe');
=> 'hi: moe!';
keys _.keys(object) : array
Retrieve all the names of the object's properties.
_.keys({one : 1, two : 2, three : 3});
=> ["one", "two", "three"]
values _.values(object) : array
Returns
true if any of the values in the object pass the iterator truth test.
Short-circuits and stops traversing the object if a true element is
found.
_.values({one : 1, two : 2, three : 3});
=> [1, 2, 3]
pairs _.pairs(object) : array
Convert
an object into a list of [key, value] pairs. Notes: Resulting item
order is not guaranteed. Key capitalization will depend on your CF
engine.
_.pairs({ONE: 1, TWO: 2, THREE: 3});
=> [["ONE", 1], ["TWO", 2], ["THREE", 3]]
invert _.invert(object) : object
Returns
a copy of the object where the keys have become the values and the
values the keys. For this to work, all of your object's values should be
unique and string serializable. Note: Capitalization will depend on
your CF engine.
_.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
=> {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};
functions _.functions(object) : array
Returns
a sorted array of the names of every method in an object -- that is to
say, the name of every function property of the object.
_.functions(_);
=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose" ...
extend _.extend(destination, *sources) : any
Copy
all of the properties in the source objects over to the destination
object, and return the destination object. It's in-order, so the last
source will override properties of the same name in previous arguments.
_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
pick _.pick(object, *keys) : struct
Return a copy of the object, filtered to only have values for the whitelisted keys (or array of valid keys).
_.pick({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');
=> {name : 'moe', age : 50}
omit _.omit(object, *keys) : struct
Return a copy of the object, filtered to omit the blacklisted keys (or array of keys).
_.omit({NAME: 'moe', AGE: 50, USERID: 'moe1'}, 'userid');
=> {NAME: 'moe', AGE: 50}
defaults _.defaults(object, *defaults) : any
Fill
in missing properties in object with default values from the defaults
objects, and return the object. As soon as the property is filled,
further defaults will have no effect.
iceCream = {flavor : "chocolate"};
_.defaults(iceCream, {flavor : "vanilla", sprinkles : "lots"});
=> {flavor : "chocolate", sprinkles : "lots"}
clone _.clone(object) : any
Create a shallow-copied clone of the object. Any nested structs or objects will be copied by reference, not duplicated.
_.clone({name : 'moe'});
=> {name : 'moe'}
has _.has(object, key) : boolean
Does the object contain the given key? Delegates to _.include() for arrays or native structKeyExists() for objects.
_.has({a: 1, b: 2, c: 3}, "b");
=> true
isEqual _.isEqual(object, other)
Performs a deep comparison between the two objects, to determine if they should be considered equal.
moe = {name : 'moe', luckyNumbers : [13, 27, 34]};
clone = {name : 'moe', luckyNumbers : [13, 27, 34]};
_.isEqual(moe, clone);
=> true
isEmpty _.isEmpty(object) : boolean
Returns true if object contains no values. Delegates to ArrayLen for arrays, structIsEmpty() otherwise.
_.isEmpty([1, 2, 3]);
=> false
_.isEmpty({});
=> true
isArray _.isArray(object) : boolean
Returns true if object is an Array. Delegates to native isArray();
_.isArray({one: 1});
=> false
_.isArray([1,2,3]);
=> true
isObject _.isObject(object) : boolean
Returns true if value is an Object. Delegates to native isObject()
_.isObject(new Component());
=> true
_.isObject({});
=> false
isFunction _.isFunction(object) : boolean
Returns true if object is a Function. Delegates to native isClosure() || isCustomFunction()
_.isFunction(function(){return 1;});
=> true
isString _.isString(object) : boolean
Returns true if object is a String. Uses Java String type comparison.
_.isString("moe");
=> true
_.isString(1);
=> true//Coldfusion converts numbers to strings
isNumber _.isNumber(object) : boolean
Returns true if object is of a Java numeric type.
_.isNumber(1);
=> false//Coldfusion converts numbers to strings
_.isNumber(JavaCast("int", 1));
=> true
isBoolean _.isBoolean(object) : boolean
Returns true if object is a boolean. Delegates to native isBoolean()
_.isBoolean(false);
=> true
isDate _.isDate(object) : boolean
Returns true if object is a date. Delegates to native isDate()
_.isDate(now());
=> true
times _.times(n, iterator) : void
Invokes the given iterator function n times.
_.times(3, function(){ genie.grantWish(); });
random _.random(min, max) : numeric
Returns
a random integer between min and max, inclusive. If you only pass one
argument, it will return a number between 0 and that number. Delegates
to RandRange().
_.random(0, 100);
=> 42
mixin _.mixin(object) : void
Allows
you to extend Underscore with your own utility functions. Pass a struct
of {name: function} definitions to have your functions added to the
Underscore object, as well as the OOP wrapper.
_.mixin({
upper: function(string) { return uCase(string); }
});
_.upper("fabio");
=> "Fabio"
uniqueId _.uniqueId([prefix]) : string
Generates an identifier that is unique for this instance of Underscore
_.uniqueId('c');
=> 'c1
escape _.escape(input) : string
Escapes a string for insertion into HTML, replacing &, <, >, and " characters. Delegates to HTMLeditFormat().
_.escape('Curly, Larry & Moe');
=> "Curly, Larry & Moe
result _.result(object, property) : any
If the value of the named property is a function then invoke it; otherwise, return it.
object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=> "crumpets"
_.result(object, 'stuff');
=> "nonsense"
2.4 -- Feb 3rd, 2014
2.3 -- December 12th, 2012
2.2 -- August 10th, 2012
2.1 -- July 9th, 2012
2.0 -- July 4th, 2012
1.0 -- June 30, 2012