mblarsen / arrgh
Arrgh—a sane PHP array library
Requires
- php: >=5.6
README
The goal of Arrgh is to provide a more uniform library for working with arrays in PHP.
- Arrays as first parameter and they are immutable. The existing API for arrays can be confusing. For some functions the input array is the first parameter on others the last. Moreover some functions returns a new array others mutate the input array (passing it by reference).
- Arrgh is not a re-write but a remapping of parameters to native functions. E.g.
array_map($callable, $array)
becomesarr_map($array, $callable)
. - Comes in three flavors:
- function
arr_map($people, $callable)
- static
Arrgh::map($people, $callable)
- object/chainable
$array->map($callable)
- function
- Adds missing functions like:
tail
,collapse
,sortBy
,mapAssoc
(associative mapping function),get
(a dot-path getter) and many more. (see Additional functions) - Sorting and comparing works both with PHP5 and PHP7, even for your custom sort and compare functions.
- You can use the the names you know or shorter ones using snake_case or camelCase. E.g.
$array->array_map()
,$array->arrayMap()
,$array->map()
.
Installing
composer require mblarsen/arrgh
Examples
Functions takes array as the first parameter in every case—no more looking up in the documents:
arr_map($input, $callable);
arr_join($input, ",");
Chain functions together:
arr($input)->reverse()->slice(0, 5)->sum();
Powerful get function using dot-paths:
// People and their children
$array = [
[ "name" => "Jakob", "age" => 37, "children" => [
[ "name" => "Mona", "sex" => "female" ],
[ "name" => "Lisa", "sex" => "female" ],
] ],
[ "name" => "Topher", "age" => 18, "children" => [
[ "name" => "Joe", "sex" => "male" ],
] ],
[ "name" => "Ginger", "age" => 43 ],
];
// Return all children's names
arr_get($array, "children.name"); // returns [ ["Mona", "Lisa"] , ["Joe"] ]
arr_get($array, "children.name", true) // returns [ "Mona", "Lisa", "Joe" ]
Use buil-in select functions, select by index:
arr_get($array, "children.0.name"); // returns [ ["Mona"], ["Joe"] ]
arr_get($array, "children.1.name"); // returns [ ["Lisa"] ]
arr_get($array, "children.-1.name"); // returns [ ["Lisa"], ["Joe"] ]
... or roll your own select functions, return names of all female children:
$children = arr($array)->get([ "children.!$.name",
function ($item, $index) { return $item['sex'] === 'female'; }
])->toArray();
// returns [ Mona, Lisa ]
(Syntax: An array with the path followed by a callable for each occurance of !$
in the path)
To achieve the same using chain API (which is also pretty concise) it looks like this:
$children = arr($array)
->map(function ($person) { return isset($person["children"]) ? $person["children"] : null; })
->filter()
->collapse()
->map(function ($child) {
if ($child["sex"] === "female") { return $child["name"]; }
return null;
})
->filter()
->toArray();
Array as first argument
Arrgh doesn't rewrite the array algorithms but remaps parameters to native functions. E.g. arr_map
moves the $callable
as the last parameter.
array_map ( callable $callback , array $array1 [, array $... ] )
Becomes:
arr_map ( array $array1 [, array $... ] , callable $callback )
Example usage:
arr_map($products, function ($product) {
return $product->code();
});
All functions that takes one or more arrays now takes them as their first arguments:
array_key_exists($key, $array) => ($array, $key)
in_array($key, $array) => ($array, $key)
array_search($search, $array) => ($array, $search)
implode($glue, $array) => ($array, $glue);
join($glue, $array) => ($array, $glue);
array_map($callable, $array[, arrayN]) => ($array[, arrayN], $callable)
All functions are immutable
Most of the functions that makes alterations to arrays like sorting all pass the input array by reference like:
usort ( array &$array , callable $value_compare_func )
This make functional programming difficult and less fluent in PHP.
In Arrgh all array functions will return an array, unless of course a value is being computed (sum
, pop
, min
, max
, etc).
These functions will now return a result:
array multisort, array push, array splice, array walk, array walk recursive, arsort, asort, krsort, ksort, natcasesort, natsort, rsort, shuffle, sort, uasort, uksort, usort
This means where you had to like this:
// Top 5 most expensive products
array_usort($products, function ($p1, $p2) { ... };
return array_slice($products, 0, 5);
You can now do like this:
// Top 5 most expensive products
return arr_slice(arr_usort($products, function ($p1, $p2) { ... }), 0, 5);
Or you could use chains like this [see more below]:
// Top 5 most expensive products
return arr($products)
->usort($products, function ($p1, $p2) { ... })
->slice(0, 5);
Choose your style
Arrgh comes in three styles for your temperament:
Function style
The Arrgh repertoire of functions is include in the global scope, so that you can use the functions anywhere. Just like the native array functions:
arr_replace($defaults, $params);
The constructor function aargh()
lets you start a chain:
arr($defaults)->replace($params);
Using function style
If you want to make use of function-style you have to define the following before autoloading.
define("ARRGH", true);
require __DIR__ . "/vendor/autoload.php";
You can change the function prefix using:
define("ARRGH_PREFIX", "arr");
Now arr_reverse
becomes:
arr_reverse([1, 2, 3]);
Note: changing the function prefix requires the use of eval()
. If eval()
is disabled Arrgh will throw an exception. Arrgh comes prebuild with arrgh
and arr
prefixes, so for these eval()
is not needed.
Static style
You can use the static functions on Arrgh
:
Arrgh::array_flip($music);
Arrgh::flip($music);
All static methods takes an array as the first input and returns an array. Even sort:
return Arrgh::sort($big_numbers);
You can break out of static-style and start a chain like this:
Arrgh::arr($big_numbers)
->sort()
->reduce(function ($k, $v) { return $k += ln($v); });
A synonym of arrgh
is chain
. A shorthand for both is to prefix any method with underscore:
Arrgh::_sort($big_numbers)
->reduce(function ($k, $v) { return $k += $v });
_sort()
returns chainable Arrgh object.
Chain style
Chains can be created in a couple of ways.
Using the arr()
function:
arr($array)->reverse();
Using the static methods Arrgh::arr()
or Arrgh::chain()
:
Arrgh::chain($array)->reverse();
Using the static method shorthand (_):
Arrgh::_reverse($array);
Or by creating a chainable object:
$videos = new Arrgh($videos);
$media = $videos->merge($music)->shuffle()->slice(0, 3);
When you are working with objects all methods returns an object, not an actual array. To get a native PHP array invoke the toArray()
method:
$media->toArray();
Note: Arrgh implements both ArrayAccessphp and Iteratorphp so you can use an Arrgh object as an array.
In case you want to preserve the array rather than the result of a terminating functions like e.g. pop()
, you can use keepChain()
:
arr([1, 2, 3])->pop(); // will return 3
With use of keepChain()
we'll get the array instead:
arr([1, 2, 3])->keepChain()->pop(); // will return an Arrgh object with the array [1, 2]
If you want to break the chain again. For example to get the sum of the remaining elements you can:
arr([1, 2, 3])->keepChain()->pop()->keepChain(false)->sum(); // returns 3
arr([1, 2, 3])->keepChain()->pop()->breakChain()->sum(); // breakChain() = keepChain(false)
If ->keepChain(false)
had been left out sum()
would also have returned the Arrgh
object.
The same expression can be written using keepOnce()
:
arr([1, 2, 3])->keepOnce()->pop()->sum(); // returns 3
All functions are there
All the functions from the PHP manual Array Functionsphp section are supported.
If you use function style the methods are prefixed with arr_
except for functions starting with array_
(in that case it is replaced):
array_map => arr_map
usort => arr_usort
These functions are not supported:
compact, extract, current, each, key, next, pos, prev, reset
Additional functions
In addition to Array Functionsphp Arrgh provides these functions:
map_assoc(array, function)
: Map function that works on associative arrays. Map functionfunction ($key, $value)
.sort_by(array, key|function)
: Sort a collection of items by a key or a function. Sort functionfunction ($item)
.contains(array, [key])
: Looks through a collection for a certain value and returns true or falls. Can be restricted to a key.collapse(array)
: Collapses an array of arrays into one. E.g.[[1, 2], [3, 4]]
becomes[1, 2, 3, 4]
except(array, key|array)
: Return all collections of items having some keys inkey|array
stripped.only(array, key|array)
: Likeexcept
but will only return items with the keys inkey|array
.get(array, path)
: A powerful getter of multidimensional arrays.isCollection(array)
: Tells if an array is a collection (as opposed ot an associative array)depth(array)
: Tells the depth of arrays of arrays (excluding associative arrays)head(array)
: Synonym for shifttail(array)
: Returns everything but the head. E.g. tail([1, 2, 3]) outputs [2, 3]first(array)
: Synonym for shiftlast(array)
: Synonym for poppartition(array, callable)
: Splits array into two arrays determined by callable functionodd(array)
: Gives all odd-indexed items in an array. 1, 3, 5, etc.even(array)
: Gives all even-indexed items in an array. 0, 2, 4, etc.
Works like array
Arrgh implements ArrayAccessphp and Iteratorphp, so you can use it as an array.
Here is an example using foreach:
$arr = arr([1, 2, 3, 4, 5]);
foreach ($arr as $value) {
echo $value . PHP_EOL;
}
You can push values onto array like native arrays:
echo $arr->sum(); // 15
$arr[] = 6;
echo $arr->sum(); // 21
Array values are returned as Arrgh
objects:
$arr = arr([[3, 2, 1], [5, 4]]);
$content = arr([]);
foreach ($arr as $value) {
$content = $content->merge($value->reverse());
}
$content->toArray(); // returns array [ 1, 2, 3, 4, 5 ];
Note: PHP's native functions can only take arrays as parameters, so that is a limitation. But you are not using them anyway are you?
If you want to make use of function-style you have to define the following before vendor/autoload
require.
define("ARRGH", true);
Now you can use functions like this anywhere in your code:
arr_reverse([1, 2, 3]);
You can change the function prefix using:
define("ARRGH_PREFIX", "arrgh");
Now arr_reverse
becomes:
arrgh_reverse([1, 2, 3]);
Note: changing the function prefix requires the use of eval()
. If eval()
is disabled Arrgh will throw an exception. Arrgh comes prebuild with arr prefixes, so for these eval()
is not needed.
PHP5 vs PHP7
At the time of writing if PHP5 and PHP7 treats equal values returned by comparable-functions differently.
For example. The following unittest will fail in PHP 5.6.x and not in 7:
$original_input = [
[ "name" => "Jakob", "age" => 42 ],
[ "name" => "Topher", "age" => 18 ],
[ "name" => "Ginger", "age" => 42 ],
];
$expected_result = [
[ "name" => "Topher", "age" => 18 ],
[ "name" => "Jakob", "age" => 42 ],
[ "name" => "Ginger", "age" => 42 ],
];
$input = $original_input;
usort($input, function ($a, $b) {
return $a["age"] - $b["age"];
});
// Actual ouput in PHP5 (Jakob and Ginger reversed):
// [
// [ "name" => "Topher", "age" => 18 ],
// [ "name" => "Ginger", "age" => 42 ],
// [ "name" => "Jakob", "age" => 42 ],
// ]
//
// Actual ouput in PHP7 (Jakob and Ginger in the original order):
// [
// [ "name" => "Topher", "age" => 18 ],
// [ "name" => "Jakob", "age" => 42 ],
// [ "name" => "Ginger", "age" => 42 ],
// ]
PHP5 and PHP7 treats the items in an array with a compare result differently. You have to take this into account if you code for both versions. While PHP5 changes the order, PHP7 does not have this side-effect.
Arrgh elevates this problem by providing the correctly signed integer depending on which version is running. So when running your custom compare functions you can do like this:
usort($input, function ($a, $b) {
return Arrgh::getSortDirection($a["age"] - $b["age"]);
});
or using Arrgh:
arr_usort($input, function ($a, $b) {
return Arrgh::getSortDirection($a["age"] - $b["age"]);
});
See example in the unit test ArrghGeneralTest::testPhpVersionFail*
.
As of v0.6 Arrgh handles this internally so you can simply do like this:
arr_usort($input, function ($a, $b) {
return $a["age"] - $b["age"];
});
The callable is wrapped in PHP version aware callable that inspects the result and returns a value according to the PHP version.
Changelog
See CHANGELOG.md
TODO
See TODO.md