Skip to content

mblarsen/arrgh

Repository files navigation

Arrgh—a sane PHP array library

Build Status Coverage Status Total Downloads Scrutinizer Code Quality Codacy Badge
Get help on Codementor Join the chat at https://gitter.im/mblarsen/arrgh

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) becomes arr_map($array, $callable).
  • Comes in three flavors:
    1. function arr_map($people, $callable)
    2. static Arrgh::map($people, $callable)
    3. object/chainable $array->map($callable)
  • 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 function function ($key, $value).
  • sort_by(array, key|function): Sort a collection of items by a key or a function. Sort function function ($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 in key|array stripped.
  • only(array, key|array): Like except but will only return items with the keys in key|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 shift
  • tail(array): Returns everything but the head. E.g. tail([1, 2, 3]) outputs [2, 3]
  • first(array): Synonym for shift
  • last(array): Synonym for pop
  • partition(array, callable): Splits array into two arrays determined by callable function
  • odd(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

About

A sane PHP array library.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages