GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

CollectionMethods::group()   A
last analyzed

Complexity

Conditions 6
Paths 7

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 21
ccs 4
cts 4
cp 1
rs 9.2222
c 0
b 0
f 0
cc 6
nc 7
nop 3
crap 6
1
<?php
2
declare(strict_types=1);
3
4
/*
5
 * This file is part of Underscore.php
6
 *
7
 * (c) Maxime Fabre <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Underscore\Methods;
14
15
use Closure;
16
17
/**
18
 * Abstract Collection type
19
 * Methods that apply to both objects and arrays.
20
 */
21
abstract class CollectionMethods
22
{
23
    ////////////////////////////////////////////////////////////////////
24
    ///////////////////////////// ANALYZE //////////////////////////////
25
    ////////////////////////////////////////////////////////////////////
26
    /**
27
     * Check if an array has a given key.
28
     *
29
     * @param  array  $array
30
     *
31
     */
32
    public static function has(mixed $array, string $key) : bool
33
    {
34 9
        // Generate unique string to use as marker
35
        $unfound = StringsMethods::random(5);
36
37 9
        return static::get($array, $key, $unfound) !== $unfound;
38
    }
39 9
40
    ////////////////////////////////////////////////////////////////////
41
    //////////////////////////// FETCH FROM ////////////////////////////
42
    ////////////////////////////////////////////////////////////////////
43
    /**
44
     * Get a value from an collection using dot-notation.
45
     *
46
     * @param  array  $collection  The collection to get from
47
     * @param  string|int|null  $key  The key to look for
48
     * @param  mixed  $default  Default value to fallback to
49
     */
50
    public static function get(mixed $collection, mixed $key = null, mixed $default = null) : mixed
51
    {
52
        if ($key === null) {
53
            return $collection;
54
        }
55 60
56
        $collection = (array) $collection;
57 60
58 1
        if (isset($collection[$key])) {
59
            return $collection[$key];
60
        }
61 60
62
        // Crawl through collection, get key according to object or not
63 60
        foreach (explode('.', (string) $key) as $segment) {
64 24
            $collection = (array) $collection;
65
66
            if (!isset($collection[$segment])) {
67
                return $default instanceof Closure ? $default() : $default;
68 51
            }
69 51
70
            $collection = $collection[$segment];
71 51
        }
72 47
73
        return $collection;
74
    }
75 36
76
    /**
77
     * Set a value in a collection using dot notation.
78 6
     *
79
     * @param mixed  $collection The collection
80
     * @param string $key        The key to set
81
     * @param mixed  $value      Its value
82
     */
83
    public static function set(mixed $collection, string $key, mixed $value) : mixed
84
    {
85
        static::internalSet($collection, $key, $value);
86
87
        return $collection;
88
    }
89
90 11
    /**
91
     * Get a value from a collection and set it if it wasn't.
92 11
     *
93
     * @param mixed  $collection The collection
94 11
     * @param string $key        The key
95
     * @param mixed  $default    The default value to set if it isn't
96
     */
97
    public static function setAndGet(mixed &$collection, string $key, mixed $default = null) : mixed
98
    {
99
        // If the key doesn't exist, set it
100
        if (!static::has($collection, $key)) {
101
            $collection = static::set($collection, $key, $default);
102
        }
103
104
        return static::get($collection, $key);
105
    }
106 5
107
    /**
108
     * Remove a value from an array using dot notation.
109 5
     *
110 5
     *
111
     */
112
    public static function remove(mixed $collection, string|array $key) : mixed
113 5
    {
114
        // Recursive call
115
        if (\is_array($key)) {
0 ignored issues
show
introduced by
The condition is_array($key) is always true.
Loading history...
116
            foreach ($key as $k) {
117
                static::internalRemove($collection, $k);
118
            }
119
120
            return $collection;
121
        }
122
123
        static::internalRemove($collection, $key);
124 5
125
        return $collection;
126
    }
127 5
128 1
    /**
129 1
     * Fetches all columns $property from a multimensionnal array.
130
     *
131
     * @param $collection
132 1
     * @param $property
133
     */
134
    public static function pluck($collection, $property) : object|array
135 4
    {
136
        $plucked = array_map(fn($value): mixed => ArraysMethods::get($value, $property), (array) $collection);
137 4
138
        // Convert back to object if necessary
139
        if (\is_object($collection)) {
140
            return (object) $plucked;
141
        }
142
143
        return $plucked;
144
    }
145
146
    /**
147
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
148 3
     * property within that.
149
     *
150
     * @param              $collection
151 3
     * @param  string|null  $comparisonOp
152 3
     *
153
     */
154
    public static function filterBy($collection, string $property, mixed $value, string $comparisonOp = null) :
155 3
    object|array
156 1
    {
157
        if (!$comparisonOp) {
158
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
159 3
        }
160
161
        $ops = [
162
            'eq'          => fn($item, $prop, $value) : bool => $item[$prop] === $value,
163
            'gt'          => fn($item, $prop, $value) : bool => $item[$prop] > $value,
164
            'gte'         => fn($item, $prop, $value) : bool => $item[$prop] >= $value,
165
            'lt'          => fn($item, $prop, $value) : bool => $item[$prop] < $value,
166
            'lte'         => fn($item, $prop, $value) : bool => $item[$prop] <= $value,
167
            'ne'          => fn($item, $prop, $value) : bool => $item[$prop] !== $value,
168
            'contains'    => fn($item, $prop, $value) : bool => \in_array($item[$prop], (array) $value, true),
169
            'notContains' => fn($item, $prop, $value) : bool => ! \in_array($item[$prop], (array) $value, true),
170
            'newer'       => fn(
171
                $item,
172
                $prop,
173 4
                $value
174
            ) : bool => strtotime((string) $item[$prop]) > strtotime((string) $value),
175 4
            'older'       => fn(
176 2
                $item,
177
                $prop,
178
                $value
179
            ) : bool => strtotime((string) $item[$prop]) < strtotime((string) $value),
180 4
        ];
181 4
        $result = array_values(array_filter((array) $collection, function ($item) use (
182
            $property,
183
            $value,
184 4
            $ops,
185
            $comparisonOp
186
        ): bool {
187 4
            $item = (array) $item;
188
            $item[$property] = static::get($item, $property, []);
189 4
190 4
            return $ops[$comparisonOp]($item, $property, $value);
191
        }));
192
        if (\is_object($collection)) {
193 4
            return (object) $result;
194
        }
195 1
196 4
        return $result;
197
    }
198 1
199 4
    /**
200
     * @param        $collection
201
     * @param        $property
202 4
     * @param        $value
203
     *
204
     * @return array|mixed
205 4
     */
206
    public static function findBy($collection, string $property, $value, string $comparisonOp = 'eq'): mixed
207 1
    {
208 4
        $filtered = static::filterBy($collection, $property, $value, $comparisonOp);
209
210
        return ArraysMethods::first(\is_array($filtered) ? $filtered : (array) $filtered);
211 4
    }
212 4
213 4
    ////////////////////////////////////////////////////////////////////
214 4
    ///////////////////////////// ANALYZE //////////////////////////////
215
    ////////////////////////////////////////////////////////////////////
216 4
    /**
217 4
     * Get all keys from a collection.
218
     *
219 4
     * @param $collection
220 4
     */
221 4
    public static function keys($collection) : array
222
    {
223
        return array_keys((array) $collection);
224
    }
225 4
226
    /**
227
     * Get all values from a collection.
228
     *
229
     * @param $collection
230
     */
231
    public static function values($collection) : array
232
    {
233
        return array_values((array) $collection);
234
    }
235
236 2
    ////////////////////////////////////////////////////////////////////
237
    ////////////////////////////// ALTER ///////////////////////////////
238 2
    ////////////////////////////////////////////////////////////////////
239
    /**
240 2
     * Replace a key with a new key/value pair.
241
     *
242
     *
243
     */
244
    public static function replace(array|object $collection, string $replace, string $key, mixed $value) : mixed
245
    {
246
        $collection = static::remove($collection, $replace);
247
248
        return static::set($collection, $key, $value);
249
    }
250
251
    /**
252
     * Sort a collection by value, by a closure or by a property
253
     * If the sorter is null, the collection is sorted naturally.
254 2
     *
255
     * @param  string|callable|null  $sorter
256 2
     *
257
     */
258
    public static function sort(array|object $collection, string|callable $sorter = null, string $direction = 'asc') :
259
    array
260
    {
261
        $collection = (array) $collection;
262
263
        // Get correct PHP constant for direction
264
        $directionNumber = (strtolower($direction) === 'desc') ? SORT_DESC : SORT_ASC;
265
266 2
        // Transform all values into their results
267
        if ($sorter !== null) {
268 2
            $results = ArraysMethods::each(
269
                $collection,
270
                fn($value) => \is_callable($sorter) ? $sorter($value) : ArraysMethods::get($value, $sorter)
271
            );
272
        } else {
273
            $results = $collection;
274
        }
275
276
        // Sort by the results and replace by original values
277
        array_multisort($results, $directionNumber, SORT_REGULAR, $collection);
0 ignored issues
show
Bug introduced by
Underscore\Methods\SORT_REGULAR cannot be passed to array_multisort() as the parameter $rest expects a reference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

277
        array_multisort($results, $directionNumber, /** @scrutinizer ignore-type */ SORT_REGULAR, $collection);
Loading history...
278
279
        return $collection;
280
    }
281
282
    /**
283
     * Group values from a collection according to the results of a closure.
284
     *
285 2
     * @param      $collection
286
     * @param      $grouper
287 2
     *
288 2
     */
289
    public static function group(mixed $collection, callable|string $grouper, bool $saveKeys = false) : array
290 2
    {
291
        $collection = (array) $collection;
292
        $result     = [];
293
294
        // Iterate over values, group by property/results from closure
295
        foreach ($collection as $key => $value) {
296
            $groupKey = \is_callable($grouper) ? $grouper($value, $key) : ArraysMethods::get($value, $grouper);
297
            $newValue = static::get($result, $groupKey);
298
299
            // Add to results
300
            if ($groupKey !== null && $saveKeys) {
301
                $result[$groupKey] = $newValue;
302
                $result[$groupKey][$key] = $value;
303 2
            } elseif ($groupKey !== null) {
304
                $result[$groupKey] = $newValue;
305 2
                $result[$groupKey][] = $value;
306
            }
307
        }
308 2
309
        return $result;
310
    }
311 2
312
    ////////////////////////////////////////////////////////////////////
313 2
    ////////////////////////////// HELPERS /////////////////////////////
314 2
    ////////////////////////////////////////////////////////////////////
315
    /**
316 1
     * Internal mechanic of set method.
317
     *
318
     * @param $collection
319
     * @param $key
320 2
     * @param $value
321
     */
322 2
    protected static function internalSet(&$collection, $key, $value) : mixed
323
    {
324
        if ($key === null) {
325
            return $collection = $value;
326
        }
327
328
        // Explode the keys
329
        $keys = explode('.', (string) $key);
330
331
        // Crawl through the keys
332
        while (\count($keys) > 1) {
333
            $key = array_shift($keys);
334 3
335
            // If we're dealing with an object
336 3
            if (\is_object($collection)) {
337 3
                $collection->{$key} = static::get($collection, $key, []);
338
                $collection         = &$collection->{$key};
339
                // If we're dealing with an array
340 3
            } else {
341 3
                $collection[$key] = static::get($collection, $key, []);
342 3
                $collection       = &$collection[$key];
343
            }
344
        }
345 3
346 1
        // Bind final tree on the collection
347 1
        $key = array_shift($keys);
348 2
        if (\is_array($collection)) {
349 1
            $collection[$key] = $value;
350 1
        } else {
351
            $collection->{$key} = $value;
352
        }
353
354 3
        return $collection;
355
    }
356
357
    /**
358
     * Internal mechanics of remove method.
359
     *
360
     * @param  string  $key
361
     *
362
     */
363
    protected static function internalRemove(array|object &$collection, mixed $key) : bool
364
    {
365
        // Explode keys
366
        $keys = explode('.', $key);
367
368
        // Crawl though the keys
369
        while (\count($keys) > 1) {
370 11
            $key = array_shift($keys);
371
372 11
            if ( ! static::has($collection, $key)) {
373
                return false;
374
            }
375
376
            // If we're dealing with an object
377 11
            if (\is_object($collection)) {
378
                $collection = &$collection->{$key};
379
                // If we're dealing with an array
380 11
            } else {
381 2
                $collection = &$collection[$key];
382
            }
383
        }
384 2
385 1
        $key = array_shift($keys);
386 1
        if (\is_object($collection)) {
387
            unset($collection->{$key});
388
        } else {
389
            unset($collection[$key]);
390 2
        }
391 2
392
        return true;
393
    }
394
395
    /**
396 11
     * Given a list, and an iteratee function that returns
397 11
     * a key for each element in the list (or a property name),
398 8
     * returns an object with an index of each item.
399
     * Just like groupBy, but for when you know your keys are unique.
400
     */
401 3
    public static function indexBy(array $array, mixed $key) : array
402
    {
403 11
        $results = [];
404
405
        foreach ($array as $a) {
406
            if (isset($a[$key])) {
407
                $results[$a[$key]] = $a;
408
            }
409
        }
410
411
        return $results;
412
    }
413
}
414