Pushing round things down square holes.
Belt.Underscore
is an utility library that makes working with arrays in PHP
a little bit more pleasant.
Via Composer
$ composer require belt/underscore
The following examples assume that you have included the Underscore utility:
use Belt\_;
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 thetoArray
method.
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 name
Additionally, 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;
}); // true
Call 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;
}); // true
Chunks 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\n
each
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;
}); // 3
Returns 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,
flatMap
might 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); // false
Returns 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); // null
Combines 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;
}); // foobarbaz
Note: 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,4
Returns 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;
}); // false
Partitions 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(); // 6
Reduces 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;
}); // 10
Reduce 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); // 20
Returns 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(); // 10
Assumes 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(); // 3
Treats 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(); // 1
Inserts 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(); // 10
These 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.