PHP underscore inspired &/or cloned from awesome _.js
. A set of utilities and data manipulation helpers providing convenience functionalites to deal with array, list, hash, functions and so on in a neat elegant and OOP way. Guaranteed to save you tons of boiler plate codes when churning complex data collection.
- Zero dependency (no vendor bloat).
Requires PHP5.6 or later.
composer require adhocore/underscore
Although all of them are available with helper function underscore($data)
or new Ahc\Underscore($data)
,
the methods are grouped and organized in different heriarchy and classes according as their scope.
This keeps it maintainable and saves from having a God class.
- Underscore
- UnderscoreFunction
- UnderscoreArray
- UnderscoreCollection
- UnderscoreBase
- HigherOrderMessage
- ArrayAccess
- Arrayizes
Generates a function that always returns a constant value.
$fn = underscore()->constant([1, 2]);
$fn(); // [1, 2]
No operation!
underscore()->noop(); // void/null
Return a random integer between min and max (inclusive).
$rand = underscore()->random(1, 10);
Run callable n times and create new collection.
$fn = function ($i) { return $i * 2; };
underscore()->times(5, $fn)->get();
// [0, 2, 4, 6, 8]
Generate unique ID (unique for current go/session).
$u = underscore()->uniqueId(); // '1'
$u1 = underscore()->uniqueId(); // '2'
$u3 = underscore()->uniqueId('id:'); // 'id:3'
Returns a function that is the composition of a list of functions, each consuming the return value of the function that follows.
$c = underscore()->compose('strlen', 'strtolower', 'strtoupper');
$c('aBc.xYz'); // ABC.XYZ => abc.xyz => 7
Cache the result of callback for given arguments and reuse that in subsequent call.
$cb = underscore()->delay(function () { echo 'OK'; }, 100);
// waits 100ms
$cb(); // 'OK'
Returns a callable which when invoked caches the result for given arguments and reuses that result in subsequent calls.
$sum = underscore()->memoize(function ($a, $b) { return $a + $b; });
$sum(4, 5); // 9
// Uses memo:
$sum(4, 5); // 9
Returns a callable that wraps given callable which can be only invoked at most once per given $wait threshold.
$fn = underscore()->throttle($callback, 100);
while (...) {
$fn(); // it will be constantly called but not executed more than one in 100ms
if (...) break;
}
Get only the truthy items.
underscore($array)->compact()->get();
// [1 => 'a', 4 => 2, 5 => [1]
Get the items whose value is not in given data.
underscore([1, 2, 1, 'a' => 3, 'b' => [4]])->difference([1, [4]])->get();
// [1 => 2, 'a' => 3]
Find the first index that passes given truth test.
$u = underscore([[1, 2], 'a' => 3, 'x' => 4, 'y' => 2, 'b' => 'B']);
$isEven = function ($i) { return is_numeric($i) && $i % 2 === 0; };
$u->findIndex(); // 0
$u->findIndex($isEven); // 'x'
Find the last index that passes given truth test.
$u = underscore([[1, 2], 'a' => 3, 'x' => 4, 'y' => 2, 'b' => 'B']);
$isEven = function ($i) { return is_numeric($i) && $i % 2 === 0; };
$u->findLastIndex(); // 'b'
$u->findLastIndex($isEven); // 'y'
Get the first n items.
underscore([1, 2, 3])->first(); // 1
underscore([1, 2, 3])->first(2); // [1, 2]
Gets the flattened version of multidimensional items.
$u = underscore([0, 'a', '', [[1, [2]]], 'b', [[[3]], 4, 'c', underscore([5, 'd'])]]);
$u->flatten()->get(); // [0, 'a', '', 1, 2, 'b', 3, 4, 'c', 5, 'd']
Find the first index of given value if available null otherwise.
$u = underscore([[1, 2], 'a' => 2, 'x' => 4]);
$array->indexOf(2); // 'a'
Gets the items whose value is common with given data.
$u = underscore([1, 2, 'a' => 3]);
$u->intersection([2, 'a' => 3, 3])->get(); // [1 => 2, 'a' => 3]
Get the last n items.
underscore([1, 2, 3])->last(); // 3
underscore([1, 2, 3])->last(2); // [2, 3]
Find the last index of given value if available null otherwise.
$u = underscore([[1, 2], 'a' => 2, 'x' => 4, 'y' => 2]);
$array->lastIndexOf(2); // 'y'
Hydrate the items into given class or stdClass.
underscore(['a', 'b' => 2])->object(); // stdClass(0: 'a', 'b': 2)
Creates a new range from start to stop with given step.
underscore()->range(4, 9)->get(); // [4, 5, 6, 7, 8, 9]
Gets the smallest index at which an object should be inserted so as to maintain order.
underscore([1, 3, 5, 8, 11])->sortedIndex(9, null); // 4
Get the union/merger of items with given data.
$u = underscore([1, 2, 'a' => 3]);
$u->union([3, 'a' => 4, 'b' => [5]])->get(); // [1, 2, 'a' => 4, 3, 'b' => [5]]
Gets the unique items using the id resulted from callback.
$u = underscore([1, 2, 'a' => 3]);
$u->union([3, 'a' => 4, 'b' => [5]])->get();
// [1, 2, 'a' => 4, 3, 'b' => [5]]
Group the values from data and items having same indexes together.
$u = underscore([1, 2, 'a' => 3, 'b' => 'B']);
$u->zip([2, 4, 'a' => 5])->get();
// [[1, 2], [2, 4], 'a' => [3, 5], 'b' => ['B', null]]
Check if the collection contains given item.
$u = underscore(['a' => 1, 'b' => 2, 'c' => 3, 5]);
$u->contains(1); // true
$u->contains('x'); // false
Count items in each group indexed by the result of callback.
$u = underscore([
['a' => 0, 'b' => 1, 'c' => 1],
['a' => true, 'b' => false, 'c' => 'c'],
['a' => 2, 'b' => 1, 'c' => 2],
['a' => 1, 'b' => null, 'c' => 0],
]);
// by key 'a'
$u->countBy('a')->get();
// [0 => 1, 1 => 2, 2 => 1]
Apply given callback to each of the items in collection.
$answers = [];
underscore([1, 2, 3])->each(function ($num) use (&$answers) {
$answers[] = $num * 5;
});
$answers; // [5, 10, 15]
Tests if all the items pass given truth test.
$gt0 = underscore([1, 2, 3, 4])->every(function ($num) { return $num > 0; });
$gt0; // true
Find and return all the items that passes given truth test.
$gt2 = underscore([1, 2, 4, 0, 3])->filter(function ($num) { return $num > 2; });
$gt2->values(); // [4, 3]
Find the first item (or index) that passes given truth test.
$num = underscore([1, 2, 4, 3])->find(function ($num) { return $num > 2; });
$num; // 4
$idx = underscore([1, 2, 4, 3])->find(function ($num) { return $num > 2; }, false);
$idx; // 2
Get the first item that contains all the given props (matching both index and value).
$u = underscore([['a' => 1, 'b' => 2], ['a' => 2, 'b' => 2], ['a' => 1, 'b' => 3]]);
$u->findWhere(['b' => 3]); // ['a' => 1, 'b' => 3]
Group items by using the result of callback as index. The items in group will have original index intact.
$u = underscore([
['a' => 0, 'b' => 1, 'c' => 1],
['a' => true, 'b' => false, 'c' => 'c'],
['a' => 2, 'b' => 1, 'c' => 2],
['a' => 1, 'b' => null, 'c' => 0],
]);
// by key 'a'
$u->groupBy('a')->get();
// [
// 0 => [0 => ['a' => 0, 'b' => 1, 'c' => 1]],
// 1 => [1 => ['a' => true, 'b' => false, 'c' => 'c'], 3 => ['a' => 1, 'b' => null, 'c' => 0]],
// 2 => [2 => ['a' => 2, 'b' => 1, 'c' => 2]],
// ]
Reindex items by using the result of callback as new index.
$u = underscore([
['a' => 0, 'b' => 1, 'c' => 1],
['a' => true, 'b' => false, 'c' => 'c'],
['a' => 2, 'b' => 1, 'c' => 2],
['a' => 1, 'b' => null, 'c' => 0],
]);
// by key 'a'
$u->indexBy('a')->get();
// [
// 0 => ['a' => 0, 'b' => 1, 'c' => 1],
// 1 => ['a' => 1, 'b' => null, 'c' => 0],
// 2 => ['a' => 2, 'b' => 1, 'c' => 2],
// ]
Invoke a callback using all of the items as arguments.
$sum = underscore([1, 2, 4])->invoke(function () { return array_sum(func_get_args()); });
$sum; // 7
Update the value of each items with the result of given callback.
$map = underscore([1, 2, 3])->map(function ($num) { return $num * 2; });
$map->get(); // [2, 4, 6]
Find the maximum value using given callback or just items.
underscore([1, 5, 4])->max(); // 5
$u = underscore([['a' => 1, 'b' => 2], ['a' => 2, 'b' => 3], ['a' => 0, 'b' => 1]]);
$u->max(function ($i) { return $i['a'] + $i['b']; }); // 5
Find the minimum value using given callback or just items.
underscore([1, 5, 4])->min(); // 1
$u = underscore([['a' => 1, 'b' => 2], ['a' => 2, 'b' => 3], ['a' => 0, 'b' => 1]]);
$u->min(function ($i) { return $i['a'] + $i['b']; }); // 1
Separate the items into two groups: one passing given truth test and other failing.
$u = underscore(range(1, 10));
$oddEvn = $u->partition(function ($i) { return $i % 2; });
$oddEvn->get(0); // [1, 3, 5, 7, 9]
$oddEvn->get(1); // [2, 4, 6, 8, 10]
Pluck given property from each of the items.
$u = underscore([['name' => 'moe', 'age' => 30], ['name' => 'curly']]);
$u->pluck('name')->get(); // ['moe', 'curly']
Iteratively reduce the array to a single value using a callback function.
$sum = underscore([1, 2, 3])->reduce(function ($sum, $num) {
return $num + $sum;
}, 0);
$sum; // 6
Same as reduce but applies the callback from right most item first.
$concat = underscore([1, 2, 3, 4])->reduceRight(function ($concat, $num) {
return $concat . $num;
}, '');
echo $concat; // '4321'
Find and return all the items that fails given truth test.
$evens = underscore([1, 2, 3, 4, 5, 7, 6])->reject(function ($num) {
return $num % 2 !== 0;
});
$evens->values(); // [2, 4, 6]
Get upto n items in random order.
$u = underscore([1, 2, 3, 4]);
$u->sample(1)->count(); // 1
$u->sample(2)->count(); // 2
Randomize the items keeping the indexes intact.
underscore([1, 2, 3, 4])->shuffle()->get();
Tests if some (at least one) of the items pass given truth test.
$some = underscore([1, 2, 0, 4, -1])->some(function ($num) {
return $num > 0;
});
$some; // true
Sort items by given callback and maintain indexes.
$u = underscore(range(1, 15))->shuffle(); // randomize
$u->sortBy(null)->get(); // [1, 2, ... 15]
$u = underscore([['a' => 1, 'b' => 2], ['a' => 2, 'b' => 3], ['a' => 0, 'b' => 1]]);
$u->sortBy('a')->get();
// [2 => ['a' => 0, 'b' => 1], 0 => ['a' => 1, 'b' => 2], 1 => ['a' => 2, 'b' => 3]]
$u->sortBy(function ($i) { return $i['a'] + $i['b']; })->get();
// [2 => ['a' => 0, 'b' => 1], 0 => ['a' => 1, 'b' => 2], 1 => ['a' => 2, 'b' => 3]],
Filter only the items that contain all the given props (matching both index and value).
$u = underscore([['a' => 1, 'b' => 2], ['a' => 2, 'b' => 2], ['a' => 1, 'b' => 3, 'c']]);
$u->where(['a' => 1, 'b' => 2])->get(); // [['a' => 1, 'b' => 2, 'c']]
A static shortcut to constructor.
$u = Ahc\Underscore\Underscore::_([1, 3, 7]);
Stringify the underscore instance as json string.
echo (string) underscore([1, 2, 3]); // [1, 2, 3]
echo (string) underscore(['a', 2, 'c' => 3]); // {0: "a", 1: 2, "c":3}
Get data as array.
underscore()->asArray('one'); // ['one']
underscore()->asArray([1, 2]); // [1, 2]
underscore()->asArray(underscore(['a', 1, 'c', 3])); // ['a', 1, 'c', 3]
underscore()->asArray(new class {
public function toArray()
{
return ['a', 'b', 'c'];
}
}); // ['a', 'b', 'c']
underscore()->asArray(new class implements \JsonSerializable {
public function jsonSerialize()
{
return ['a' => 1, 'b' => 2, 'c' => 3];
}
}); // ['a' => 1, 'b' => 2, 'c' => 3]
Creates a shallow copy of itself.
$u = underscore(['will', 'be', 'cloned']);
$u->clon() == $u; // true
$u->clon() === $u; // false
Gets the count of items.
underscore([1, 2, [3, 4]])->count(); // 3
underscore()->count(); // 0
Flatten a multi dimension array to 1 dimension.
underscore()->flat([1, 2, [3, 4, [5, 6]]]); // [1, 2, 3, 4, 5, 6]
Get the underlying array data by index.
$u = underscore([1, 2, 3]);
$u->get(); // [1, 2, 3]
$u->get(1); // 2
$u->get(3); // 3
Get data.
// same as `get()` without args:
underscore([1, 2, 3])->getData(); // [1, 2, 3]
Gets the iterator for looping.
$it = underscore([1, 2, 3])->getIterator();
while ($it->valid()) {
echo $it->current();
}
Swap index and value of all the items. The values should be stringifiable.
$u = underscore(['a' => 1, 'b' => 2, 'c' => 3]);
$u->invert()->get(); // [1 => 'a', 2 => 'b', 3 => 'c']
Gets the data for json serialization.
$u = underscore(['a' => 1, 'b' => 2, 'c' => 3]);
json_encode($u); // {"a":1,"b":2,"c":3}
Get all the keys.
$u = underscore(['a' => 1, 'b' => 2, 'c' => 3, 5]);
$u->keys()->get(); // ['a', 'b', 'c', 0]
Adds a custom handler/method to instance. The handler is bound to this instance.
Ahc\Underscore\Underscore::mixin('square', function () {
return $this->map(function ($v) { return $v * $v; });
});
underscore([1, 2, 3])->square()->get(); // [1, 4, 9]
The current time in millisec.
underscore()->now(); // 1529996371081
Omit the items having one of the blacklisted indexes.
$u = underscore(['a' => 3, 7, 'b' => 'B', 1 => ['c', 5]]);
$u->omit('a', 0)->get(); // ['b' => 'B', 1 => ['c', 5]]
Pair all items to use an array of index and value.
$u = ['a' => 3, 7, 'b' => 'B'];
$u->pair(); // ['a' => ['a', 3], 0 => [0, 7], 'b' => ['b', 'B']
Pick only the items having one of the whitelisted indexes.
$u = underscore(['a' => 3, 7, 'b' => 'B', 1 => ['c', 5]]);
$u->pick(0, 1)->get(); // [7, 1 => ['c', 5]]
Invokes callback fn with clone and returns original self.
$u = underscore([1, 2]);
$tap = $u->tap(function ($_) { return $_->values(); });
$tap === $u; // true
Convert the data items to array.
$u = underscore([1, 3, 5, 7]);
$u->toArray(); // [1, 3, 5, 7]
Get string value (JSON representation) of this instance.
underscore(['a', 2, 'c' => 3])->valueOf(); // {0: "a", 1: 2, "c":3}
Get all the values.
$u = underscore(['a' => 1, 'b' => 2, 'c' => 3, 5]);
$u->values()->get(); // [1, 2, 3, 5]
Alias of map().
Alias of find().
Alias of last().
Alias of reduce().
Alias of reduceRight().
Alias of first().
Alias of contains().
Alias of reduce().
Alias of filter().
Alias of count().
Alias of last().
Alias of first().
Alias of unique().
Alias of difference().
A syntatic sugar to use elegant shorthand oneliner for complex logic often wrapped in closures. See example below:
// Higher Order Messaging
class HOM
{
protected $n;
public $square;
public function __construct($n)
{
$this->n = $n;
$this->square = $n * $n;
}
public function even()
{
return $this->n % 2 === 0;
}
}
$u = [new HOM(1), new HOM(2), new HOM(3), new HOM(4)];
// Filter `even()` items
$evens = $u->filter->even(); // 'even()' method of each items!
// Map each evens to their squares
$squares = $evens->map->square; // 'square' prop of each items!
// Gives an Underscore instance
// Get the data
$squares->get();
// [1 => 4, 3 => 16]
Without higher order messaging that would look like:
$evens = $u->filter(function ($it) {
return $it->even();
});
$squares = $evens->map(function ($it) {
return $it->square;
});
Underscore instances can be treated as array:
$u = underscore([1, 2, 'a' => 3]);
isset($u['a']); // true
isset($u['b']); // false
echo $u[1]; // 2
$u['b'] = 'B';
isset($u['b']); // true
unset($u[1]);
You can use this trait to arrayize all complex data.
use Ahc\Underscore\Arrayizes;
class Any
{
use Arrayizes;
public function name()
{
$this->asArray($data);
}
}
MIT | © 2017-2018 | Jitendra Adhikari