Underscore is an utility library that makes working with arrays in PHP
a little bit more pleasant.
Via Composer
$ composer require thinkingmedia/underscoreThe following examples assume that you have included the Underscore utility:
use Underscore\_;Some of the examples might seem a bit contrived, but they're actually really handy. For example, let's say that we have a fictional social network and (for some reason) we want to get the names of all the authenticated user's 2nd degree friends (friends-of-friends) that are over the age of 18. Suddenly, that becomes real easy!
_::create($user->getFriends())->map(function ($f) {
return $f->getFriends();
})->select(function ($f) {
return $f->getAge() > 18;
})->pluck('username');And now (for some even stranger reason) we want to know the total number of third degree friends (friends-of-friends-of-friends) of the 2nd degree friends that are over the age of 18.
_::create($user->getFriends())->map(function ($f) {
return $f->getFriends();
})->select(function ($f) {
return $f->getAge() > 18;
})->reduce(function ($s, $f) {
return $s + count($f->getFriends());
});That's it!
Note: When, in the examples, the return value comment indicates an array the actual return value is a new
_instance! You can get the actual PHP array value by calling thetoArraymethod.
The _ class implements ArrayAccess too, so you can access it like an usual array:
$groups = _::create($user->getFriends())->groupBy(function ($friend) {
$name = $friend->getName();
return $name[0];
});
$groups['A'] = ...; // All friends with the letter 'A' as the first letter in their nameAdditionally, you can traverse the container in a foreach loop as well:
$users = _::create(['alice', 1337, 'bob', 42])->chunk(2);
foreach ($users as $name => $karma) {
// ...
}Call the given callback for each element in the container. Should the callback
return false, the method immediately returns false and ceases enumeration.
If all invocations of the callback return true, all returns true.
_::create([1, 2, 3])->all(function ($n) {
return $n > 0;
}); // trueCall the given callback for each element in the container. Should the callback
return true, the method immediately returns true and enumeration is ceased.
If all invocations of the callback return false, any returns false.
_::create([1, 2, 3])->any(function ($n) {
return $n > 2;
}); // trueChunks the container into a new array of n-sized chunks.
_::create([1, 2, 3, 4])->chunk(2); // [[1, 2], [3, 4]]Combine the container with another array into key/value pairs.
_::create([1, 2, 3])->combine(['foo', 'bar', 'baz']); // [1 => 'foo', 2 => 'bar', 3 => 'baz']Returns a new array that is the container with the given array concatenated
to the end.
_::create([1, 2])->concat([3, 4]); // [1, 2, 3, 4]Convert an array of key/value pairs into the logical dictionary.
_::create([[1, 2], [3, 4]])->dict(); // [1 => 2, 3 => 4]If you have a flat array you can call chunk(2) before dict.
_::create([1, 2, 3, 4])->chunk(2)->dict(); // [1 => 2, 3 => 4]Calls the given callback once for each element in the container, passing that element as the argument.
_::create([1, 2, 3, 4])->each(function ($n) {
printf("%d\n", $n);
}); // outputs: 1\n2\n3\n4\neach also supports two and three parameter versions:
_::create([1, 2, 3, 4])->each(function ($n, $i) {
printf("%d: %d\n", $i, $n);
}); //outputs: 0: 1\n1: 2\n2: 3\n3: 4\n
_::create([1, 2, 3, 4]->each(function ($n, $i, $array) {
// ...
}));Passes each entry in the container to the given callback, returning the first
element for which callback is not false. If no entry matches, returns null.
_::create([1, 2, 3, 4])->find(function ($n) {
return $n > 2;
}); // 3Returns the first n elements in the container.
_::create([1, 2, 3, 4])->first(2); // [1, 2]Returns a new, one-dimensional array that is a recursive flattening of the container.
_::create([1, [2], [3, [4]]])->flatten(); [1, 2, 3, 4]Tip: If you only want to flatten one level of an array,
flatMapmight be useful for you!
Returns a new array with the concatenated results of invoking the callback once for every element in the container.
_::create([1, 2, 3, 4])->flatMap(function ($n) {
return [$n, $n];
}); // [1, 1, 2, 2, 3, 3, 4, 4]
_::create([1, 2, 3, 4])->flatMap(function ($n) {
return [$n, [$n]];
}); // [1, [1], 2, [2], 3, [3], 4, [4]]It might look a bit silly, but this is actually a really useful function when you combine it with other functions! For example, you can create a dictionary for (fictional) users.
_::create([new User('bob', 32), new User('alice', 35)])->flatMap(function ($u) {
return [$n->getName(), $n->getAge()];
})->chunk(2)->dict(); // ['bob' => 32, 'alice' => 35]Which finally allows us, as developers, to create key/value pairs when mapping arrays! Hoorah!
Groups the container by result of the given callback.
_::create([1, 2, 3, 4])->groupBy(function ($n) {
return $n % 2;
}); // [0 => [2, 4], 1 => [1, 3]]
_::create(['foo', 'bar', 'baz'])->groupBy(function ($s) {
return $s[0];
}); // ['f' => ['foo'], 'b' => ['bar', 'baz']]_::create([1, 2, 3, 4])->has(2); // true
_::create([1, 2, 3, 4])->has(0); // falseReturns the index of the given object in the container or null if the element
was not found.
_::create([1, 2, 3, 4])->indexOf(2); // 1
_::create([1, 2, 3, 4])->indexOf(0); // nullCombines all elements of the container by applying a binary operation.
_::create([1, 2, 3])->inject([], function ($m, $n) {
$m[$n] = $n * $n;
return $m;
}); // [1 => 1, 2 => 4, 3 => 9]
_::create(['foo', 'bar', 'baz'])->inject('', function ($m, $s) {
$m .= $s;
}); // foobarbazNote: This is the only exception to the note earlier. The return value here is the return value of the last itertation.
Returns a string of all the container's elements joined with the provided separator string.
_::create([1, 2, 3, 4])->join(''); // 1234
_::create([1, 2, 3, 4])->join(','); // 1,2,3,4Returns the last n elements from the container.
_::create([1, 2, 3, 4, 5, 6])->last(2); // [5, 6]Invokes the given callback for each element in the container. Creates a new array containing the values returned by the block.
If the given callback returns null, that element is skipped in the returned
array.
_::create([1, 2, 3, 4])->map(function ($n) {
return $n * $n;
}); // [1, 4, 9, 16]
_::create([1, 2, 3, 4])->map(function ($n) {
return $n % 2 ? $n * $n : null;
}); // [1, 9]Returns the element for which the given callback returns the largest integer.
_::create('1', 'two', 'three')->max(function ($s) {
return strlen($s);
}); // 'three'Returns the element for which the given callback returns the smallest integer.
_::create('1', 'two', 'three')->min(function ($s) {
return strlen($s);
}); // '1'Test if the given callback returns false for each element in the container.
_::create([1, 2, 3, 4])->none(function ($n) {
return $n < 0;
}); // true
_::create([1, 2, 3, 4])->none(function ($n) {
return $n > 0;
}); // falsePartitions the container into two arrays based on the boolean return value of the given block.
_::create(['A', 'B', 'C', 'AA'])->partition(function ($s) {
return $s[0] == 'A';
}); // [['A', 'AA'], ['B', 'C']]Returns a new array that is the result of retrieving the given property path on each element in the container.
_::create([new User('bob'), new User('alice')])->pluck('username'); // ['bob', 'alice']Calculate the product of the container by assuming that all values can be casted to a double value.
_::create([1, 2, 3])->product(); // 6Reduces the container to a single value.
The usual example of reduce is to sum all values in an array.
_::create([1, 2, 3, 4])->reduce(function ($memo, $n) {
return $memo + $n;
}); // 10Reduce also allows you to set an initial value before reducing the array.
_::create([1, 2, 3, 4])->reduce(function ($s, $n) {
return $s + $n;
}, 10); // 20Returns a new array containing all elements for which the given callback
returns false.
_::create([1, 2, 3, 4])->reject(function ($n) {
return ($n % 2) == 0;
}); // [1, 3]Returns a new array that is the container, reversed.
_::create([1, 2, 3, 4])->reverse(); // [4, 3, 2, 1]Returns a new array rotated about the provided index.
_::create([1, 2, 3, 4, 5, 6])->rotate(2); // [3, 4, 5, 6, 1, 2]
_::create([1, 2, 3, 4, 5, 6])->rotate(-2); // [5, 6, 1, 2, 3, 4]Returns a random element from the container.
_::create([1, 2, 3, 4, 5, 6])->sample(); // Basically a dice roll...Returns a new array containing all elements for which the given block returns
true.
_::create([1, 2, 3, 4])->select(function ($n) {
return ($n % 2) == 0;
}); // [2, 4]Returns a new array that is shuffled.
_::create([1, 2])->shuffle(); // Either [1, 2] or [2, 1]Skips the first n elements and returns the rest of the array.
_::create([1, 2, 3, 4, 5, 6])->skip(2); // [3, 4, 5, 6]Returns a subarray consisting of the given number of elements from the given starting index.
_::create([1, 2, 3, 4])->slice(1, 2); // [2, 3]Snips the end off the array. Returns the container without the last n
elements.
_::create([1, 2, 3, 4, 5, 6])->snip(2); // [1, 2, 3, 4]Returns the container, sorted.
_::create([1, 4, 2, 3])->sort(); // [1, 2, 3, 4]Sorts all objects using the return value of the given callback as the sorting criteria.
$rhombas = new Shape('rhombas');
$ellipse = new Shape('ellipse');
$hexagon = new Shape('hexagon');
_::create([ $rhombas, $ellipse, $hexagon ])->sortBy(function ($s) {
return $s->getName();
}); // [ $ellipse, $hexagon, $rhombas ]Sum all objects by casting the values to a double.
_::create([1, 2, 3, 4])->sum(); // 10Assumes that the container is an array of arrays and transposes the rows and columns.
_::create([[1, 2, 3], [4, 5, 6]])->transpose(); // [[1, 4], [2, 5], [3, 6]]Returns a new array by removing duplicate values in the container.
_::create([1, 2, 3, 1, 2, 4, 1, 2, 5])->uniq(); // [3, 4, 5]Returns a new array where objects in the given array are removed from the container.
_::create([1, 2, 4, 3])->without([4]); // [1, 2, 3]
_::create([1, 2, 3, 4, 5])->without([4, 5]); // [1, 2, 3]Treats container like a stack and removes the last object, returning it.
_::create()->push(1)->push(2)->push(3)->pop(); // 3Treats container like a stack and adds the given object to the end of the container.
_::create()->push(1)->push(2)->push(3); // [1, 2, 3]Removes the container's first object and returns it.
_::create([1, 2, 3])->shift(); // 1Inserts the given object at the front of container, moving all other objects in the container up one index.
_::create([2, 3])->unshift(1); // [1, 2, 3]Returns a new array of the strings in the given string that are separated by the given separator.
_::split('foo bar baz', ' '); // ['foo', 'bar', 'baz']The second parameter is optional and null by default, if you pass null or
an empty string as seperator, you will get an array of the individual characters
in the given string.
_::split('1234'); // ['1', '2', '3', '4']We can do some pretty neat stuff with this!
_::split('1234')->sum(); // 10These functions are strongly related and useful to remember.
_::create([1, 2, 3, 4, 5])->first(2); // [1, 2]
_::create([1, 2, 3, 4, 5])->last(2); // [4, 5]
_::create([1, 2, 3, 4, 5])->skip(2); // [3, 4, 5]
_::create([1, 2, 3, 4, 5])->snip(2); // [1, 2, 3]
_::create([1, 2, 3, 4, 5])->slice(2, 2); // [3, 4]Please see CONTRIBUTING.
This project is heavily inspired by YOLOKit. For all of you who develop in Objective-C, I highly recommend you check it out!
Please see LICENSE.