Upgrade to 4.0.0

This commit is contained in:
Bastian Allgeier
2023-11-28 09:33:56 +01:00
parent f96b96af76
commit 3b0b6546ca
480 changed files with 21371 additions and 13327 deletions

View File

@@ -37,7 +37,7 @@ class A
*
* @param mixed ...$args Parameters to pass to the closures
*/
public static function apply(array $array, ...$args): array
public static function apply(array $array, mixed ...$args): array
{
array_walk_recursive($array, function (&$item) use ($args) {
if ($item instanceof Closure) {
@@ -49,16 +49,52 @@ class A
}
/**
* Counts the number of elements in an array
* Returns the average value of an array
*
* @param array $array
* @return int
* @param array $array The source array
* @param int $decimals The number of decimals to return
* @return float|null The average value
*/
public static function average(array $array, int $decimals = 0): float|null
{
if (empty($array) === true) {
return null;
}
return round((array_sum($array) / sizeof($array)), $decimals);
}
/**
* Counts the number of elements in an array
*/
public static function count(array $array): int
{
return count($array);
}
/**
* Merges arrays recursively
*
* <code>
* $defaults = [
* 'username' => 'admin',
* 'password' => 'admin',
* ];
*
* $options = A::extend($defaults, ['password' => 'super-secret']);
* // returns: [
* // 'username' => 'admin',
* // 'password' => 'super-secret'
* // ];
* </code>
*
* @psalm-suppress NamedArgumentNotAllowed
*/
public static function extend(array ...$arrays): array
{
return array_merge_recursive(...$arrays);
}
/**
* Checks if every element in the array passes the test
*
@@ -75,9 +111,7 @@ class A
* </code>
*
* @since 3.9.8
* @param array $array
* @param callable(mixed $value, int|string $key, array $array):bool $test
* @return bool
*/
public static function every(array $array, callable $test): bool
{
@@ -90,6 +124,57 @@ class A
return true;
}
/**
* Fills an array up with additional elements to certain amount.
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $result = A::fill($array, 5, 'elephant');
*
* // result: [
* // 'cat',
* // 'dog',
* // 'bird',
* // 'elephant',
* // 'elephant',
* // ];
* </code>
*
* @param array $array The source array
* @param int $limit The number of elements the array should
* contain after filling it up.
* @param mixed $fill The element, which should be used to
* fill the array. If it's a callable, it
* will be called with the current index
* @return array The filled-up result array
*/
public static function fill(
array $array,
int $limit,
mixed $fill = 'placeholder'
): array {
for ($x = count($array); $x < $limit; $x++) {
$array[] = is_callable($fill) ? $fill($x) : $fill;
}
return $array;
}
/**
* Filter the array using the given callback
* using both value and key
* @since 3.6.5
*/
public static function filter(array $array, callable $callback): array
{
return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
}
/**
* Finds the first element matching the given callback
*
@@ -115,9 +200,7 @@ class A
* </code>
*
* @since 3.9.8
* @param array $array
* @param callable(mixed $value, int|string $key, array $array):bool $callback
* @return mixed
*/
public static function find(array $array, callable $callback): mixed
{
@@ -130,6 +213,28 @@ class A
return null;
}
/**
* Returns the first element of an array
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $first = A::first($array);
* // first: 'miao'
* </code>
*
* @param array $array The source array
* @return mixed The first element
*/
public static function first(array $array): mixed
{
return array_shift($array);
}
/**
* Gets an element of an array by key
*
@@ -159,7 +264,7 @@ class A
public static function get(
$array,
string|int|array|null $key,
$default = null
mixed $default = null
) {
if (is_array($array) === false) {
return $array;
@@ -237,22 +342,45 @@ class A
/**
* Checks if array has a value
*
* @param array $array
* @param mixed $value
* @param bool $strict
* @return bool
*/
public static function has(array $array, $value, bool $strict = false): bool
{
public static function has(
array $array,
mixed $value,
bool $strict = false
): bool {
return in_array($value, $array, $strict);
}
/**
* Checks whether an array is associative or not
*
* <code>
* $array = ['a', 'b', 'c'];
*
* A::isAssociative($array);
* // returns: false
*
* $array = ['a' => 'a', 'b' => 'b', 'c' => 'c'];
*
* A::isAssociative($array);
* // returns: true
* </code>
*
* @param array $array The array to analyze
* @return bool true: The array is associative false: It's not
*/
public static function isAssociative(array $array): bool
{
return ctype_digit(implode('', array_keys($array))) === false;
}
/**
* Joins the elements of an array to a string
*/
public static function join(array|string $value, string $separator = ', '): string
{
public static function join(
array|string $value,
string $separator = ', '
): string {
if (is_string($value) === true) {
return $value;
}
@@ -271,10 +399,6 @@ class A
*
* // Now you can access the array by the id
* </code>
*
* @param array $array
* @param string|callable $keyBy
* @return array
*/
public static function keyBy(array $array, string|callable $keyBy): array
{
@@ -290,6 +414,38 @@ class A
return array_combine($keys, $array);
}
/**
* Returns the last element of an array
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $last = A::last($array);
* // last: 'tweet'
* </code>
*
* @param array $array The source array
* @return mixed The last element
*/
public static function last(array $array): mixed
{
return array_pop($array);
}
/**
* A simple wrapper around array_map
* with a sane argument order
* @since 3.6.0
*/
public static function map(array $array, callable $map): array
{
return array_map($map, $array);
}
public const MERGE_OVERWRITE = 0;
public const MERGE_APPEND = 1;
public const MERGE_REPLACE = 2;
@@ -412,246 +568,13 @@ class A
/**
* Reduce an array to a single value
*
* @param array $array
* @param callable $callback
* @param mixed $initial
* @return mixed
*/
public static function reduce(array $array, callable $callback, $initial = null): mixed
{
return array_reduce($array, $callback, $initial);
}
/**
* Shuffles an array and keeps the keys
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $shuffled = A::shuffle($array);
* // output: [
* // 'dog' => 'wuff',
* // 'cat' => 'miao',
* // 'bird' => 'tweet'
* // ];
* </code>
*
* @param array $array The source array
* @return array The shuffled result array
*/
public static function shuffle(array $array): array
{
$keys = array_keys($array);
$new = [];
shuffle($keys);
// resort the array
foreach ($keys as $key) {
$new[$key] = $array[$key];
}
return $new;
}
/**
* Returns a slice of an array
*
* @param array $array
* @param int $offset
* @param int|null $length
* @param bool $preserveKeys
* @return array
*/
public static function slice(
public static function reduce(
array $array,
int $offset,
int $length = null,
bool $preserveKeys = false
): array {
return array_slice($array, $offset, $length, $preserveKeys);
}
/**
* Checks if at least one element in the array passes the test
*
* <code>
* $array = [1, 30, 39, 29, 10, 'foo' => 12, 13];
*
* $isAboveThreshold = fn($value) => $value > 30;
* echo A::some($array, $isAboveThreshold) ? 'true' : 'false';
* // output: 'true'
*
* $isStringKey = fn($value, $key) => is_string($key);
* echo A::some($array, $isStringKey) ? 'true' : 'false';
* // output: 'true'
* </code>
*
* @since 3.9.8
* @param array $array
* @param callable(mixed $value, int|string $key, array $array):bool $test
* @return bool
*/
public static function some(array $array, callable $test): bool
{
foreach ($array as $key => $value) {
if ($test($value, $key, $array)) {
return true;
}
}
return false;
}
/**
* Sums an array
*
* @param array $array
* @return int|float
*/
public static function sum(array $array): int|float
{
return array_sum($array);
}
/**
* Returns the first element of an array
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $first = A::first($array);
* // first: 'miao'
* </code>
*
* @param array $array The source array
* @return mixed The first element
*/
public static function first(array $array)
{
return array_shift($array);
}
/**
* Returns the last element of an array
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $last = A::last($array);
* // last: 'tweet'
* </code>
*
* @param array $array The source array
* @return mixed The last element
*/
public static function last(array $array)
{
return array_pop($array);
}
/**
* Returns a number of random elements from an array,
* either in original or shuffled order
*/
public static function random(array $array, int $count = 1, bool $shuffle = false): array
{
if ($shuffle) {
return array_slice(self::shuffle($array), 0, $count);
}
if ($count === 1) {
$key = array_rand($array);
return [$key => $array[$key]];
}
return self::get($array, array_rand($array, $count));
}
/**
* Fills an array up with additional elements to certain amount.
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $result = A::fill($array, 5, 'elephant');
*
* // result: [
* // 'cat',
* // 'dog',
* // 'bird',
* // 'elephant',
* // 'elephant',
* // ];
* </code>
*
* @param array $array The source array
* @param int $limit The number of elements the array should
* contain after filling it up.
* @param mixed $fill The element, which should be used to
* fill the array. If it's a callable, it
* will be called with the current index
* @return array The filled-up result array
*/
public static function fill(array $array, int $limit, $fill = 'placeholder'): array
{
for ($x = count($array); $x < $limit; $x++) {
$array[] = is_callable($fill) ? $fill($x) : $fill;
}
return $array;
}
/**
* A simple wrapper around array_map
* with a sane argument order
* @since 3.6.0
*/
public static function map(array $array, callable $map): array
{
return array_map($map, $array);
}
/**
* Move an array item to a new index
*/
public static function move(array $array, int $from, int $to): array
{
$total = count($array);
if ($from >= $total || $from < 0) {
throw new Exception('Invalid "from" index');
}
if ($to >= $total || $to < 0) {
throw new Exception('Invalid "to" index');
}
// remove the item from the array
$item = array_splice($array, $from, 1);
// inject it at the new position
array_splice($array, $to, 0, $item);
return $array;
callable $callback,
$initial = null
): mixed {
return array_reduce($array, $callback, $initial);
}
/**
@@ -685,6 +608,31 @@ class A
return array_values(array_diff($required, array_keys($array)));
}
/**
* Move an array item to a new index
*/
public static function move(array $array, int $from, int $to): array
{
$total = count($array);
if ($from >= $total || $from < 0) {
throw new Exception('Invalid "from" index');
}
if ($to >= $total || $to < 0) {
throw new Exception('Invalid "to" index');
}
// remove the item from the array
$item = array_splice($array, $from, 1);
// inject it at the new position
array_splice($array, $to, 0, $item);
return $array;
}
/**
* Normalizes an array into a nested form by converting
* dot notation in keys to nested structures
@@ -704,7 +652,7 @@ class A
foreach ($array as $fullKey => $value) {
// extract the first part of a multi-level key, keep the others
$subKeys = explode('.', $fullKey);
$subKeys = is_int($fullKey) ? [$fullKey] : explode('.', $fullKey);
$key = array_shift($subKeys);
// skip the magic for ignored keys
@@ -762,6 +710,105 @@ class A
];
}
/**
* Returns a number of random elements from an array,
* either in original or shuffled order
*/
public static function random(
array $array,
int $count = 1,
bool $shuffle = false
): array {
if ($shuffle === true) {
return array_slice(self::shuffle($array), 0, $count);
}
if ($count === 1) {
$key = array_rand($array);
return [$key => $array[$key]];
}
return self::get($array, array_rand($array, $count));
}
/**
* Shuffles an array and keeps the keys
*
* <code>
* $array = [
* 'cat' => 'miao',
* 'dog' => 'wuff',
* 'bird' => 'tweet'
* ];
*
* $shuffled = A::shuffle($array);
* // output: [
* // 'dog' => 'wuff',
* // 'cat' => 'miao',
* // 'bird' => 'tweet'
* // ];
* </code>
*
* @param array $array The source array
* @return array The shuffled result array
*/
public static function shuffle(array $array): array
{
$keys = array_keys($array);
$new = [];
shuffle($keys);
// resort the array
foreach ($keys as $key) {
$new[$key] = $array[$key];
}
return $new;
}
/**
* Returns a slice of an array
*/
public static function slice(
array $array,
int $offset,
int|null $length = null,
bool $preserveKeys = false
): array {
return array_slice($array, $offset, $length, $preserveKeys);
}
/**
* Checks if at least one element in the array passes the test
*
* <code>
* $array = [1, 30, 39, 29, 10, 'foo' => 12, 13];
*
* $isAboveThreshold = fn($value) => $value > 30;
* echo A::some($array, $isAboveThreshold) ? 'true' : 'false';
* // output: 'true'
*
* $isStringKey = fn($value, $key) => is_string($key);
* echo A::some($array, $isStringKey) ? 'true' : 'false';
* // output: 'true'
* </code>
*
* @since 3.9.8
* @param callable(mixed $value, int|string $key, array $array):bool $test
*/
public static function some(array $array, callable $test): bool
{
foreach ($array as $key => $value) {
if ($test($value, $key, $array)) {
return true;
}
}
return false;
}
/**
* Sorts a multi-dimensional array by a certain column
*
@@ -815,7 +862,7 @@ class A
array $array,
string $field,
string $direction = 'desc',
$method = SORT_REGULAR
int $method = SORT_REGULAR
): array {
$direction = strtolower($direction) === 'desc' ? SORT_DESC : SORT_ASC;
$helper = [];
@@ -842,63 +889,11 @@ class A
}
/**
* Checks whether an array is associative or not
*
* <code>
* $array = ['a', 'b', 'c'];
*
* A::isAssociative($array);
* // returns: false
*
* $array = ['a' => 'a', 'b' => 'b', 'c' => 'c'];
*
* A::isAssociative($array);
* // returns: true
* </code>
*
* @param array $array The array to analyze
* @return bool true: The array is associative false: It's not
* Sums an array
*/
public static function isAssociative(array $array): bool
public static function sum(array $array): int|float
{
return ctype_digit(implode('', array_keys($array))) === false;
}
/**
* Returns the average value of an array
*
* @param array $array The source array
* @param int $decimals The number of decimals to return
* @return float The average value
*/
public static function average(array $array, int $decimals = 0): float|null
{
if (empty($array) === true) {
return null;
}
return round((array_sum($array) / sizeof($array)), $decimals);
}
/**
* Merges arrays recursively
*
* <code>
* $defaults = [
* 'username' => 'admin',
* 'password' => 'admin',
* ];
*
* $options = A::extend($defaults, ['password' => 'super-secret']);
* // returns: [
* // 'username' => 'admin',
* // 'password' => 'super-secret'
* // ];
* </code>
*/
public static function extend(array ...$arrays): array
{
return array_merge_recursive(...$arrays);
return array_sum($array);
}
/**
@@ -938,6 +933,22 @@ class A
return $array;
}
/**
* Remove key(s) from an array
* @since 3.6.5
*/
public static function without(array $array, int|string|array $keys): array
{
if (is_int($keys) === true || is_string($keys) === true) {
$keys = static::wrap($keys);
}
return static::filter(
$array,
fn ($value, $key) => in_array($key, $keys, true) === false
);
}
/**
* Wraps the given value in an array
* if it's not an array yet.
@@ -954,30 +965,4 @@ class A
return $array;
}
/**
* Filter the array using the given callback
* using both value and key
* @since 3.6.5
*/
public static function filter(array $array, callable $callback): array
{
return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
}
/**
* Remove key(s) from an array
* @since 3.6.5
*/
public static function without(array $array, int|string|array $keys): array
{
if (is_int($keys) || is_string($keys)) {
$keys = static::wrap($keys);
}
return static::filter(
$array,
fn ($value, $key) => in_array($key, $keys, true) === false
);
}
}