Passed
Push — master ( 889d7e...2b2102 )
by Eric
01:52
created

Arrays::flatten()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 3
dl 0
loc 13
ccs 7
cts 7
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Esi\Utility.
7
 *
8
 * (c) 2017 - 2024 Eric Sizemore <[email protected]>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE.md file that was distributed with this source code.
12
 */
13
14
namespace Esi\Utility;
15
16
use ArrayAccess;
17
use RuntimeException;
18
19
use function array_map;
20
use function array_merge;
21
use function array_sum;
22
use function get_object_vars;
23
24
/**
25
 * Array utilities.
26
 *
27
 * @see Tests\ArraysTest
28
 */
29
final class Arrays
30
{
31
    /**
32
     * flatten().
33
     *
34
     * Flattens a multidimensional array.
35
     *
36
     * Keys are preserved based on $separator.
37
     *
38
     * @param array<mixed> $array     Array to flatten.
39
     * @param string       $separator The new keys are a list of original keys separated by $separator.
40
     * @param string       $prepend   A string to prepend to resulting array keys.
41
     *
42
     * @since 1.2.0
43
     *
44
     * @return array<mixed> The flattened array.
45
     */
46 1
    public static function flatten(array $array, string $separator = '.', string $prepend = ''): array
47
    {
48 1
        $result = [];
49
50 1
        foreach ($array as $key => $value) {
51 1
            if (\is_array($value) && $value !== []) {
52 1
                $result = array_merge($result, Arrays::flatten($value, $separator, $prepend . $key . $separator));
53
            } else {
54 1
                $result[$prepend . $key] = $value;
55
            }
56
        }
57
58 1
        return $result;
59
    }
60
61
    /**
62
     * get().
63
     *
64
     * Retrieve a value from an array.
65
     *
66
     * @param array<mixed>|ArrayAccess<mixed, mixed> $array   Array to retrieve value from.
67
     * @param string|int                             $key     Key to retrieve
68
     * @param mixed                                  $default A default value to return if $key does not exist
69
     *
70
     * @throws RuntimeException If $array is not accessible
71
     */
72 7
    public static function get(array | ArrayAccess $array, string | int $key, mixed $default = null): mixed
73
    {
74 7
        if (Arrays::keyExists($array, $key)) {
75 7
            return $array[$key];
76
        }
77
78 5
        return $default;
79
    }
80
81
    /**
82
     * Returns an associative array, grouped by $key, where the keys are the distinct values of $key,
83
     * and the values are arrays of items that share the same $key.
84
     *
85
     * *Important to note:* if a $key is provided that does not exist, the result will be an empty array.
86
     *
87
     * @since 2.0.0
88
     *
89
     * @param array<mixed, array<mixed>> $array Input array.
90
     * @param string                     $key   Key to use for grouping.
91
     *
92
     * @return array<mixed, array<mixed>>
93
     */
94 2
    public static function groupBy(array $array, string $key): array
95
    {
96 2
        $result = [];
97
98 2
        foreach ($array as $item) {
99 2
            if (!self::isAssociative($item)) {
100
                //@codeCoverageIgnoreStart
101
                continue;
102
                //@codeCoverageIgnoreEnd
103
            }
104
105 2
            if (!isset($item[$key])) {
106 1
                continue;
107
            }
108
109 1
            $groupKey = $item[$key];
110
111 1
            $result[$groupKey] ??= [];
112 1
            $result[$groupKey][] = $item;
113
        }
114
115 2
        return $result;
116
    }
117
118
    /**
119
     * interlace().
120
     *
121
     * Interlaces one or more arrays' values (not preserving keys).
122
     *
123
     * Example:
124
     *
125
     *      var_dump(Utility\Arrays::interlace(
126
     *          [1, 2, 3],
127
     *          ['a', 'b', 'c']
128
     *      ));
129
     *
130
     * Result:
131
     *      Array (
132
     *          [0] => 1
133
     *          [1] => a
134
     *          [2] => 2
135
     *          [3] => b
136
     *          [4] => 3
137
     *          [5] => c
138
     *      )
139
     *
140
     * @since 1.2.0
141
     *
142
     * @param array<mixed> ...$args
143
     *
144
     * @return array<mixed>|false
145
     */
146 1
    public static function interlace(array ...$args): array | false
147
    {
148 1
        $numArgs = \count($args);
149
150 1
        if ($numArgs === 0) {
151 1
            return false;
152
        }
153
154 1
        if ($numArgs === 1) {
155 1
            return $args[0];
156
        }
157
158 1
        $newArray      = [];
159 1
        $totalElements = array_sum(array_map('count', $args));
160
161 1
        for ($i = 0; $i < $totalElements; ++$i) {
162 1
            foreach ($args as $arg) {
163 1
                if (isset($arg[$i])) {
164 1
                    $newArray[] = $arg[$i];
165
                }
166
            }
167
        }
168
169 1
        return $newArray;
170
    }
171
172
    /**
173
     * isAssociative().
174
     *
175
     * Determines if the given array is an associative array.
176
     *
177
     * @param array<mixed> $array Array to check
178
     */
179 3
    public static function isAssociative(array $array): bool
180
    {
181 3
        return !array_is_list($array);
182
    }
183
184
    /**
185
     * Checks if a key exists in an array.
186
     *
187
     * @param array<mixed>|ArrayAccess<mixed, mixed> $array Array to check
188
     * @param string|int                             $key   Key to check
189
     */
190 8
    public static function keyExists(array | ArrayAccess $array, string | int $key): bool
191
    {
192 8
        if ($array instanceof ArrayAccess) {
193 1
            return $array->offsetExists($key);
194
        }
195
196 8
        return \array_key_exists($key, $array);
197
    }
198
199
    /**
200
     * mapDeep().
201
     *
202
     * Recursively applies a callback to all non-iterable elements of an array or an object.
203
     *
204
     * @since 1.2.0 - updated with inspiration from the WordPress map_deep() function.
205
     *      @see https://developer.wordpress.org/reference/functions/map_deep/
206
     *
207
     * @param mixed    $array    The array to apply $callback to.
208
     * @param callable $callback The callback function to apply.
209
     */
210 2
    public static function mapDeep(mixed $array, callable $callback): mixed
211
    {
212 2
        if (\is_array($array)) {
213 2
            foreach ($array as $key => $value) {
214 2
                $array[$key] = Arrays::mapDeep($value, $callback);
215
            }
216 2
        } elseif (\is_object($array)) {
217 1
            foreach (get_object_vars($array) as $key => $value) {
218
                // @phpstan-ignore-next-line
219 1
                $array->$key = Arrays::mapDeep($value, $callback);
220
            }
221
        } else {
222 2
            $array = \call_user_func($callback, $array);
223
        }
224
225 2
        return $array;
226
    }
227
228
    /**
229
     * set().
230
     *
231
     * Add a value to an array.
232
     *
233
     * @param array<mixed>|ArrayAccess<mixed, mixed> &$array Array to add value to.
234
     * @param string|int|null                        $key    Key to add
235
     * @param mixed                                  $value  Value to add
236
     *
237
     * @throws RuntimeException If $array is not accessible
238
     */
239 6
    public static function set(array | ArrayAccess &$array, string | int | null $key, mixed $value): void
240
    {
241 6
        if ($key === null) {
242 1
            $array = $value; // @phpstan-ignore-line
243
        } else {
244 6
            $array[$key] = $value;
245
        }
246
    }
247
248
    /**
249
     * Checks if a value exists in an array.
250
     *
251
     * @param array<mixed> $array Array to check
252
     * @param string|int   $value Value to check
253
     */
254 30
    public static function valueExists(array $array, string | int $value): bool
255
    {
256 30
        return \in_array($value, $array, true);
257
    }
258
}
259