Underscore.php is a PHP port of Underscore.js.
In addition to porting Underscore's functionality, Underscore.php includes matching unit tests. Thanks to Jeremy Ashkenas and all contributors to Underscore.js.
Object-Oriented and Static Styles
each, map, reduce, reduceRight, find, filter, reject, all, any, includ, invoke, pluck, max, min, groupBy, sortBy, sortedIndex, shuffle, toArray, size
first, initial, rest, last, compact, flatten, without, uniq, union, intersection, difference, zip, indexOf, lastIndexOf, range
bind, bindAll memoize, delay, defer, throttle, debounce, once, after, wrap, compose
keys, values, functions, extend, defaults, clon, tap, has, isEqual, isEmpty, isElement, isObject, isArray, isArguments, isFunction, isString, isNumber, isBoolean, isDate, isRegExp, isNaN, isNull, isUndefined
noConflict, identity, times, mixin, uniqueId, escape, template
Some functions were not ported from Underscore.js to Underscore.php for technical reasons or because they weren't applicable to PHP. They have been marked with
strikethrough
In many PHP installations, _()
already exists as an alias to gettext()
. The previous declaration of _
in PHP forces Underscore.php to use another name. For consistency and memorability, __
has been chosen for both the function and class name.
Underscore.php works in both object-oriented and static styles. The following lines are identical ways to double a list of numbers.
__::map([1, 2, 3], function($n) { return $n * 2; });
__([1, 2, 3])->map(function($n) { return $n * 2; });
###each
__::each(collection, iterator)
Iterates over the collection and yield each in turn to the iterator function. Arguments passed to iterator are (value, key, collection). Unlike Underscore.js, context is passed using PHP's use
statement. Underscore.php does not contain the forEach alias because 'foreach' is a reserved keyword in PHP.
__::each(array(1, 2, 3), function($num) { echo $num . ','; });
// 1,2,3,
$multiplier = 2;
__::each([1, 2, 3], function($num, $index) use ($multiplier) {
echo $index . '=' . ($num * $multiplier) . ',';
});
// 0=2,1=4,2=6,
Alias: collect
__::map(collection, iterator)
Returns an array of values by mapping each in collection through the iterator. Arguments passed to iterator are (value, key, collection). Unlike Underscore.js, context is passed using PHP's use
statement.
__::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(collection, iterator, memo)
Reduce the collection into a single value. Memo is the initial state of the reduction, updated by the return value of the iterator. Unlike Underscore.js, context is passed using PHP's use
statement.
__::reduce([1, 2, 3], function($memo, $num) { return $memo + $num; }, 0);
// 6
Alias: foldl
__::reduceRight(collection, iterator, memo)
Right-associative version of reduce.
$list = [[0, 1], [2, 3], [4, 5]];
$flat = __::reduceRight($list, function($a, $b) { return array_merge($a, $b); }, []);
// [4, 5, 2, 3, 0, 1]
Alias: detect
__::find(collection, iterator)
Return the value of the first item in the collection that passes the truth test (iterator).
__::find([1, 2, 3, 4], function($num) { return $num % 2 === 0; });
// 2
Alias: select
__::filter(collection, iterator)
Return the values in the collection that pass the truth test (iterator).
__::filter([1, 2, 3, 4], function($num) { return $num % 2 === 0; });
// [2, 4]
__::reject(collection, iterator)
Return an array where the items failing the truth test (iterator) are removed.
__::reject([1, 2, 3, 4], function($num) { return $num % 2 === 0; });
// [1, 3]
__::all(collection, iterator)
Returns true if all values in the collection pass the truth test (iterator).
__::all([1, 2, 3, 4], function($num) { return $num % 2 === 0; });
// false
__::all([1, 2, 3, 4], function($num) { return $num < 5; });
// true
__::any(collection, iterator)
Returns true if any values in the collection pass the truth test (iterator).
__::any([1, 2, 3, 4], function($num) { return $num % 2 === 0; });
// true
__::any([1, 2, 3, 4], function($num) { return $num === 5; });
// false
Alias: contians
__::includ(collection, value)
Returns true if value is found in the collection using === to test equality. This function is called 'include' in Underscore.js, but was renamed to 'includ' in Underscore.php because 'include' is a reserved keyword in PHP.
__::includ([1, 2, 3], 3);
// true
__::invoke(collection, functionName)
Returns a copy of the collection after running functionName across all elements.
__::invoke([' foo', ' bar '], 'trim');
// ['foo', 'bar']
__::pluck(collection, propertyName)
Extract an array of property values
$stooges = [
['name'=>'moe', 'age'=>40],
['name'=>'larry', 'age'=>50],
['name'=>'curly', 'age'=>60]
];
__::pluck($stooges, 'name');
// ['moe', 'larry', 'curly']
__::max(collection, [iterator])
Returns the maximum value from the collection. If passed an iterator, max will return max value returned by the iterator. Unlike Underscore.js, context is passed using PHP's use
statement.
$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(collection, [iterator])
Returns the minimum value from the collection. If passed an iterator, min will return min value returned by the iterator. Unlike Underscore.js, context is passed using PHP's use
statement.
$stooges = array(
array('name'=>'moe', 'age'=>40),
array('name'=>'larry', 'age'=>50),
array('name'=>'curly', 'age'=>60)
);
__::min($stooges, function($stooge) { return $stooge['age']; });
// array('name'=>'moe', 'age'=>40)```
### groupBy
```php
__::groupBy(collection, iterator)
Group values by their return value when passed through the iterator. If iterator is a string, the result will be grouped by that property.
__::groupBy(array(1, 2, 3, 4, 5), function($n) { return $n % 2; });
// array(0=>array(2, 4), 1=>array(1, 3, 5))
$values = array(
['name'=>'Apple', 'grp'=>'a'),
['name'=>'Bacon', 'grp'=>'b'),
[]'name'=>'Avocado', 'grp'=>'a')
);
__::groupBy($values, 'grp');
//array(
// 'a'=>array(
// array('name'=>'Apple', 'grp'=>'a'),
// array('name'=>'Avocado', 'grp'=>'a')
// ),
// 'b'=>array(
// array('name'=>'Bacon', 'grp'=>'b')
// )
//);
__::sortBy(collection, iterator)
Returns an array sorted in ascending order based on the iterator results. If passed an iterator, min will return min value returned by the iterator. Unlike Underscore.js, context is passed using PHP's use
statement.
__::sortBy([1, 2, 3], function($n) { return -$n;});
// [3, 2, 1]
__::sortedIndex(collection, value, [iterator])
Returns the index at which the value should be inserted into the sorted collection.
__::sortedIndex(array(10, 20, 30, 40), 35); // 3
__::shuffle(collection)
Returns a shuffled copy of the collection.
__::shuffle(array(10, 20, 30, 40)); // 30, 20, 40, 10
__::toArray(collection)
Converts the collection into an array.
$stooge = new StdClass;
$stooge->name = 'moe';
$stooge->age = 40;
__::toArray($stooge);
// array('name'=>'moe', 'age'=>40)
__::size(collection)
Returns the number of values in the collection.
$stooge = new StdClass;
$stooge->name = 'moe';
$stooge->age = 40;
__::size($stooge); // 2
Alias: head
__::first(array, [n])
Get the first element of an array. Passing n returns the first n elements.
__::first(array(5, 4, 3, 2, 1)); // 5
__::first(array(5, 4, 3, 2, 1), 3); // array(5, 4, 3)
__::initial(array, [n])
Get everything but the last array element. Passing n excludes the last n elements.
__::initial(array(5, 4, 3, 2, 1)); // array(5, 4, 3, 2)
__::initial(array(5, 4, 3, 2, 1), 3); // array(5, 4)
Alias: tail
__::rest(array, [index])
Get the rest of the array elements. Passing an index returns from that index onward.
__::rest(array(5, 4, 3, 2, 1)); // array(4, 3, 2, 1)
__::last(array, [n])
Get the last element of an array. Passing n returns the last n elements.
__::last(array(5, 4, 3, 2, 1)); // 1
__::last(array(5, 4, 3, 2, 1), 2); // array(2, 1)
__::compact(array)
Returns a copy of the array with falsy values removed
__::compact(array(false, true, 'a', 0, 1, '')); // array(true, 'a', 1)
__::flatten(array, [shallow])
Flattens a multidimensional array. If you pass shallow, the array will only be flattened a single level.
__::flatten(array(1, array(2), array(3, array(array(array(4))))));
// array(1, 2, 3, 4)
__::flatten(array(1, array(2), array(3, array(array(array(4))))), true);
// array(1, 2, 3, array(array(4)))
__::without(array, [*values])
Returns a copy of the array with all instances of values removed. === is used for equality testing. Keys are maintained.
__::without(array(5, 4, 3, 2, 1), 3, 2); // array(5, 4, 4=>1)
Alias: unique
__::uniq(array, [isSorted [iterator]])
Returns a copy of the array containing no duplicate values. Unlike Underscore.js, passing isSorted does not currently affect the performance of uniq
. You can optionally compute uniqueness by passing an iterator function.
__::uniq(array(2, 2, 4, 4, 4, 1, 1, 1)); // array(2, 4, 1)
__::union(*arrays)
Returns an array containing the unique items in one or more of the arrays.
$arr1 = array(1, 2, 3);
$arr2 = array(101, 2, 1, 10);
$arr3 = array(2, 1);
__::union($arr1, $arr2, $arr3); // array(1, 2, 3, 101, 10)
__::intersection(*arrays)
Returns an array containing the intersection of all the arrays. Each value in the resulting array exists in all arrays.
$arr1 = array(0, 1, 2, 3);
$arr2 = array(1, 2, 3, 4);
$arr3 = array(2, 3, 4, 5);
__::intersection($arr1, $arr2, $arr3);
// array(2, 3)
__::difference(array, *others)
Returns an array containing the items existing in one array, but not the other.
__::difference(array(1, 2, 3, 4, 5), array(5, 2, 10));
// array(1, 3, 4)
__::zip(*arrays)
Merges arrays
$names = array('moe', 'larry', 'curly');
$ages = array(30, 40, 50);
$leaders = array(true, false, false);
__::zip($names, $ages, $leaders);
// array(
// array('moe', 30, true),
// array('larry', 40, false),
// array('curly', 50, false)
// )
__::indexOf(array, value)
Returns the index of the first match. Returns -1 if no match is found. Unlike Underscore.js, Underscore.php does not take a second isSorted parameter.
__::indexOf(array(1, 2, 3, 2, 2), 2);
// 1
__::lastIndexOf(array, value)
Returns the index of the last match. Returns -1 if no match is found.
__::lastIndexOf(array(1, 2, 3, 2, 2), 2);
// 4
__::range([start], stop, [step])
Returns an array of integers from start to stop (exclusive) by step. Defaults: start=0, step=1.
__::range(10); // array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
__::range(1, 5); // array(1, 2, 3, 4)
__::range(0, 30, 5); // array(0, 5, 10, 15, 20, 25)
__::range(0, -5, -1); // array(0, -1, -2, -3, -4)
__::range(0); // array()
__::memoize(function, [hashFunction])
Memoizes a function by caching the computed result. Useful for computationally expensive functions. Optionally, pass a hashFunction to calculate the key for the cached value.
$fibonacci = function($n) use (&$fibonacci) {
return $n < 2 ? $n : $fibonacci($n - 1) + $fibonacci($n - 2);
};
$fastFibonacci = __::memoize($fibonacci);
__::throttle(function, wait)
Throttles a function so that it can only be called once every wait milliseconds.
$func = function() { return 'x'; }
__::throttle($func);
__::once(function)
Creates a version of the function that can only be called once.
$num = 0;
$increment = __::once(function() use (&$num) { return $num++; });
$increment();
$increment();
echo $num;
// 1
__::after(count, function)
Creates a version of the function that will only run after being called count times.
$func = __::after(3, function() { return 'x'; });
$func(); //
$func(); //
$func();
// 'x'
__::wrap(function, wrapper)
Wraps the function inside the wrapper function, passing it as the first argument. Lets the wrapper execute code before and/or after the function runs.
$hello = function($name) { return 'hello: ' . $name; };
$hi = __::wrap($hello, function($func) {
return 'before, ' . $func('moe') . ', after';
});
$hi();
// 'before, hello: moe, after'
__::compose(*functions)
Returns the composition of the functions, where the return value is passed to the following function.
$greet = function($name) { return 'hi: ' . $name; };
$exclaim = function($statement) { return $statement . '!'; };
$welcome = __::compose($exclaim, $greet);
$welcome('moe');
// 'hi: moe!'
__::keys(object)
Get the keys
__::keys((object) array('name'=>'moe', 'age'=>40));
// array('name', 'age')
__::values(object)
Get the values
__::values((object) array('name'=>'moe', 'age'=>40));
// array('moe', 40)
Alias: methods
__::functions(object)
Get the names of functions available to the object
class Stooge {
public function getName() { return 'moe'; }
public function getAge() { return 40; }
}
$stooge = new Stooge;
__::functions($stooge);
// array('getName', 'getAge')
__::extend(destination, *sources)
Copy all properties from the source objects into the destination object. Copying happens in order, so rightmost sources have override power.
__::extend((object) array('name'=>'moe'), (object) array('age'=>50));
// (object) array('name'=>'moe', 'age'=>50)```
### defaults
```php
__::defaults(object, *defaults)
Returns the object with any missing values filled in using the defaults. Once a default is applied for a given property, it will not be overridden by following defaults.
$food = (object) array('dairy'=>'cheese');
$defaults = (object) array('meat'=>'bacon');
__::defaults($food, $defaults);
// (object) array('dairy'=>'cheese', 'meat'=>'bacon');
__::clon(object)
Returns a shallow copy of the object. This function is called 'clone' in Underscore.js, but was renamed to 'clon' in Underscore.php because 'clone' is a reserved keyword in PHP.
$stooge = (object) array('name'=>'moe');
__::clon($stooge); // (object) array('name'=>'moe');
__::tap(object, interceptor)
Invokes the interceptor on the object, then returns the object. Useful for performing intermediary operations on the object.
$interceptor = function($obj) { return $obj * 2; };
__::chain(array(1, 2, 3))->max()
->tap($interceptor)
->value();
// 6
__::has(object, key)
Does the object have this key?
__::has((object) array('a'=>1, 'b'=>2, 'c'=>3), 'b');
// true
__::isEqual(object, other)
Are these items equal? Uses === equality testing. Objects tested using values.
$stooge = (object) array('name'=>'moe');
$clon = __::clon($stooge);
$stooge === $clon; // false
__::isEqual($stooge, $clon);
// true
__::isEmpty(object)
Returns true if the object contains no values.
$stooge = (object) array('name'=>'moe');
__::isEmpty($stooge);
// false
__::isEmpty(new StdClass);
// true
__::isEmpty((object) array());
// true
__::isObject(object)
Returns true if passed an object.
__::isObject((object) array(1, 2));
// true
__::isObject(new StdClass);
// true
__::isArray(object)
Returns true if passed an array.
__::isArray(array(1, 2));
// true
__::isArray((object) array(1, 2));
// false
__::isFunction(object)
Returns true if passed a function.
__::isFunction(function() {});
// true
__::isFunction('trim');
// false
__::isString(object)
Returns true if passed a string.
__::isString('moe');
// true
__::isString('');
// true
__::isNumber(object)
Returns true if passed a number.
__::isNumber(1); // true
__::isNumber(2.5); // true
__::isNumber('5'); // false
__::isBoolean(object)
Returns true if passed a boolean.
__::isBoolean(null); // false
__::isBoolean(true); // true
__::isBoolean(0); // false
__::isDate(object)
Returns true if passed a DateTime object
__::isDate(null); // false
__::isDate('2011-06-09 01:02:03'); // false
__::isDate(new DateTime); // true
__::isNaN(object)
Returns true if value is NaN
__::isNaN(null); // false
__::isNaN(acos(8)); // true
__::isNull(object)
Returns true if value is null
__::isNull(null); // true
__::isNull(false); // false
__::identity(value)
Returns the same value passed as the argument
$moe = array('name'=>'moe');
$moe === __::identity($moe); // true
__::times(n, iterator)
Invokes the iterator function n times.
__::times(3, function() { echo 'a'; }); // 'aaa'
__::mixin(array)
Extend Underscore.php with your own functions.
__::mixin(array(
'capitalize'=> function($string) { return ucwords($string); },
'yell' => function($string) { return strtoupper($string); }
));
__::capitalize('moe'); // 'Moe'
__::yell('moe'); // 'MOE'
__::uniqueId([prefix])
Generate a globally unique id.
__::uniqueId(); // 0
__::uniqueId('stooge_'); // 'stooge_1'
__::uniqueId(); // 2
__::escape(html)
Escapes the string.
__::escape('Curly, Larry & Moe'); // 'Curly, Larry & Moe'
__::template(templateString, [context])
Compile templates into functions that can be evaluated for rendering. Templates can interpolate variables and execute arbitrary PHP code.
$compiled = __::template('hello: <%= $name %>');
$compiled(array('name'=>'moe'));
// 'hello: moe'
$list = '<% __::each($people, function($name) { %> <li><%= $name %></li> <% }); %>';
__::template($list, array('people'=>array('moe', 'curly', 'larry')));
// '<li>moe</li><li>curly</li><li>larry</li>'
Note: if your template strings include variables, wrap your template strings in single quotes, not double quotes. Wrapping in double quotes will cause your variables to be interpolated prior to entering the template function.
// Correct
$compiled = __::template('hello: <%= $name %>');
// Incorrect
$compiled = __::template("hello: <%= $name %>");
You can set custom delimiters (for instance, Mustache style) by calling __::templateSettings()
and passing interpolate and/or evaluate values:
// Mustache style
__::templateSettings(array(
'interpolate' => '/\{\{(.+?)\}\}/'
));
$mustache = __::template('Hello {{$planet}}!');
$mustache(array('planet'=>'World')); // "Hello World!"
Returns a wrapped object. Methods will return the object until you call value()
__::chain(item);
// filter and reverse the numbers
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$result = __::chain($numbers)->select(function($n) { return $n < 5; })
->reject(function($n) { return $n === 3; })
->sortBy(function($n) { return -$n; })
->value();
// [4, 2, 1]
__(obj)->value()
Extracts the value of a wrapped object.
__(array(1, 2, 3))->value();
// array(1, 2, 3)