Issues (52)

src/Arr.php (4 issues)

1
<?php
2
3
namespace MuCTS\Collections;
4
5
use ArrayAccess;
6
use InvalidArgumentException;
7
use MuCTS\Contracts\Collections\Enumerable;
8
use MuCTS\Macroable\Traits\Macroable;
9
10
class Arr
11
{
12
    use Macroable;
0 ignored issues
show
The trait MuCTS\Macroable\Traits\Macroable requires the property $name which is not provided by MuCTS\Collections\Arr.
Loading history...
13
14
    /**
15
     * Determine whether the given value is array accessible.
16
     *
17
     * @param mixed $value
18
     * @return bool
19
     */
20
    public static function accessible($value)
21
    {
22
        return is_array($value) || $value instanceof ArrayAccess;
23
    }
24
25
    /**
26
     * Add an element to an array using "dot" notation if it doesn't exist.
27
     *
28
     * @param array $array
29
     * @param string $key
30
     * @param mixed $value
31
     * @return array
32
     */
33
    public static function add($array, $key, $value)
34
    {
35
        if (is_null(static::get($array, $key))) {
36
            static::set($array, $key, $value);
37
        }
38
39
        return $array;
40
    }
41
42
    /**
43
     * Collapse an array of arrays into a single array.
44
     *
45
     * @param iterable $array
46
     * @return array
47
     */
48
    public static function collapse($array)
49
    {
50
        $results = [];
51
52
        foreach ($array as $values) {
53
            if ($values instanceof Collection) {
54
                $values = $values->all();
55
            } elseif (!is_array($values)) {
56
                continue;
57
            }
58
59
            $results[] = $values;
60
        }
61
62
        return array_merge([], ...$results);
63
    }
64
65
    /**
66
     * Cross join the given arrays, returning all possible permutations.
67
     *
68
     * @param iterable ...$arrays
69
     * @return array
70
     */
71
    public static function crossJoin(...$arrays)
72
    {
73
        $results = [[]];
74
75
        foreach ($arrays as $index => $array) {
76
            $append = [];
77
78
            foreach ($results as $product) {
79
                foreach ($array as $item) {
80
                    $product[$index] = $item;
81
82
                    $append[] = $product;
83
                }
84
            }
85
86
            $results = $append;
87
        }
88
89
        return $results;
90
    }
91
92
    /**
93
     * Divide an array into two arrays. One with keys and the other with values.
94
     *
95
     * @param array $array
96
     * @return array
97
     */
98
    public static function divide($array)
99
    {
100
        return [array_keys($array), array_values($array)];
101
    }
102
103
    /**
104
     * Flatten a multi-dimensional associative array with dots.
105
     *
106
     * @param iterable $array
107
     * @param string $prepend
108
     * @return array
109
     */
110
    public static function dot($array, $prepend = '')
111
    {
112
        $results = [];
113
114
        foreach ($array as $key => $value) {
115
            if (is_array($value) && !empty($value)) {
116
                $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
117
            } else {
118
                $results[$prepend . $key] = $value;
119
            }
120
        }
121
122
        return $results;
123
    }
124
125
    /**
126
     * Get all of the given array except for a specified array of keys.
127
     *
128
     * @param array $array
129
     * @param array|string $keys
130
     * @return array
131
     */
132
    public static function except($array, $keys)
133
    {
134
        static::forget($array, $keys);
135
136
        return $array;
137
    }
138
139
    /**
140
     * Determine if the given key exists in the provided array.
141
     *
142
     * @param ArrayAccess|array $array
143
     * @param string|int $key
144
     * @return bool
145
     */
146
    public static function exists($array, $key)
147
    {
148
        if ($array instanceof Enumerable) {
149
            return $array->has($key);
150
        }
151
152
        if ($array instanceof ArrayAccess) {
153
            return $array->offsetExists($key);
154
        }
155
156
        return array_key_exists($key, $array);
157
    }
158
159
    /**
160
     * Return the first element in an array passing a given truth test.
161
     *
162
     * @param iterable $array
163
     * @param callable|null $callback
164
     * @param mixed $default
165
     * @return mixed
166
     */
167
    public static function first($array, callable $callback = null, $default = null)
168
    {
169
        if (is_null($callback)) {
170
            if (empty($array)) {
171
                return value($default);
172
            }
173
174
            foreach ($array as $item) {
175
                return $item;
176
            }
177
        }
178
179
        foreach ($array as $key => $value) {
180
            if ($callback($value, $key)) {
181
                return $value;
182
            }
183
        }
184
185
        return value($default);
186
    }
187
188
    /**
189
     * Return the last element in an array passing a given truth test.
190
     *
191
     * @param array $array
192
     * @param callable|null $callback
193
     * @param mixed $default
194
     * @return mixed
195
     */
196
    public static function last($array, callable $callback = null, $default = null)
197
    {
198
        if (is_null($callback)) {
199
            return empty($array) ? value($default) : end($array);
200
        }
201
202
        return static::first(array_reverse($array, true), $callback, $default);
203
    }
204
205
    /**
206
     * Flatten a multi-dimensional array into a single level.
207
     *
208
     * @param iterable $array
209
     * @param int $depth
210
     * @return array
211
     */
212
    public static function flatten($array, $depth = INF)
213
    {
214
        $result = [];
215
216
        foreach ($array as $item) {
217
            $item = $item instanceof Collection ? $item->all() : $item;
218
219
            if (!is_array($item)) {
220
                $result[] = $item;
221
            } else {
222
                $values = $depth === 1
223
                    ? array_values($item)
224
                    : static::flatten($item, $depth - 1);
0 ignored issues
show
$depth - 1 of type double is incompatible with the type integer expected by parameter $depth of MuCTS\Collections\Arr::flatten(). ( Ignorable by Annotation )

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

224
                    : static::flatten($item, /** @scrutinizer ignore-type */ $depth - 1);
Loading history...
225
226
                foreach ($values as $value) {
227
                    $result[] = $value;
228
                }
229
            }
230
        }
231
232
        return $result;
233
    }
234
235
    /**
236
     * Remove one or many array items from a given array using "dot" notation.
237
     *
238
     * @param array $array
239
     * @param array|string $keys
240
     * @return void
241
     */
242
    public static function forget(&$array, $keys)
243
    {
244
        $original = &$array;
245
246
        $keys = (array)$keys;
247
248
        if (count($keys) === 0) {
249
            return;
250
        }
251
252
        foreach ($keys as $key) {
253
            // if the exact key exists in the top-level, remove it
254
            if (static::exists($array, $key)) {
255
                unset($array[$key]);
256
257
                continue;
258
            }
259
260
            $parts = explode('.', $key);
261
262
            // clean up before each pass
263
            $array = &$original;
264
265
            while (count($parts) > 1) {
266
                $part = array_shift($parts);
267
268
                if (isset($array[$part]) && is_array($array[$part])) {
269
                    $array = &$array[$part];
270
                } else {
271
                    continue 2;
272
                }
273
            }
274
275
            unset($array[array_shift($parts)]);
276
        }
277
    }
278
279
    /**
280
     * Get an item from an array using "dot" notation.
281
     *
282
     * @param ArrayAccess|array $array
283
     * @param string|int|null $key
284
     * @param mixed $default
285
     * @return mixed
286
     */
287
    public static function get($array, $key, $default = null)
288
    {
289
        if (!static::accessible($array)) {
290
            return value($default);
291
        }
292
293
        if (is_null($key)) {
294
            return $array;
295
        }
296
297
        if (static::exists($array, $key)) {
298
            return $array[$key];
299
        }
300
301
        if (strpos($key, '.') === false) {
302
            return $array[$key] ?? value($default);
303
        }
304
305
        foreach (explode('.', $key) as $segment) {
306
            if (static::accessible($array) && static::exists($array, $segment)) {
307
                $array = $array[$segment];
308
            } else {
309
                return value($default);
310
            }
311
        }
312
313
        return $array;
314
    }
315
316
    /**
317
     * Check if an item or items exist in an array using "dot" notation.
318
     *
319
     * @param ArrayAccess|array $array
320
     * @param string|array $keys
321
     * @return bool
322
     */
323
    public static function has($array, $keys)
324
    {
325
        $keys = (array)$keys;
326
327
        if (!$array || $keys === []) {
328
            return false;
329
        }
330
331
        foreach ($keys as $key) {
332
            $subKeyArray = $array;
333
334
            if (static::exists($array, $key)) {
335
                continue;
336
            }
337
338
            foreach (explode('.', $key) as $segment) {
339
                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
340
                    $subKeyArray = $subKeyArray[$segment];
341
                } else {
342
                    return false;
343
                }
344
            }
345
        }
346
347
        return true;
348
    }
349
350
    /**
351
     * Determine if any of the keys exist in an array using "dot" notation.
352
     *
353
     * @param ArrayAccess|array $array
354
     * @param string|array $keys
355
     * @return bool
356
     */
357
    public static function hasAny($array, $keys)
358
    {
359
        if (is_null($keys)) {
0 ignored issues
show
The condition is_null($keys) is always false.
Loading history...
360
            return false;
361
        }
362
363
        $keys = (array)$keys;
364
365
        if (!$array) {
366
            return false;
367
        }
368
369
        if ($keys === []) {
370
            return false;
371
        }
372
373
        foreach ($keys as $key) {
374
            if (static::has($array, $key)) {
375
                return true;
376
            }
377
        }
378
379
        return false;
380
    }
381
382
    /**
383
     * Determines if an array is associative.
384
     *
385
     * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
386
     *
387
     * @param array $array
388
     * @return bool
389
     */
390
    public static function isAssoc(array $array)
391
    {
392
        $keys = array_keys($array);
393
394
        return array_keys($keys) !== $keys;
395
    }
396
397
    /**
398
     * Get a subset of the items from the given array.
399
     *
400
     * @param array $array
401
     * @param array|string $keys
402
     * @return array
403
     */
404
    public static function only($array, $keys)
405
    {
406
        return array_intersect_key($array, array_flip((array)$keys));
407
    }
408
409
    /**
410
     * Pluck an array of values from an array.
411
     *
412
     * @param iterable $array
413
     * @param string|array $value
414
     * @param string|array|null $key
415
     * @return array
416
     */
417
    public static function pluck($array, $value, $key = null)
418
    {
419
        $results = [];
420
421
        [$value, $key] = static::explodePluckParameters($value, $key);
422
423
        foreach ($array as $item) {
424
            $itemValue = data_get($item, $value);
425
426
            // If the key is "null", we will just append the value to the array and keep
427
            // looping. Otherwise we will key the array using the value of the key we
428
            // received from the developer. Then we'll return the final array form.
429
            if (is_null($key)) {
430
                $results[] = $itemValue;
431
            } else {
432
                $itemKey = data_get($item, $key);
433
434
                if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
435
                    $itemKey = (string)$itemKey;
436
                }
437
438
                $results[$itemKey] = $itemValue;
439
            }
440
        }
441
442
        return $results;
443
    }
444
445
    /**
446
     * Explode the "value" and "key" arguments passed to "pluck".
447
     *
448
     * @param string|array $value
449
     * @param string|array|null $key
450
     * @return array
451
     */
452
    protected static function explodePluckParameters($value, $key)
453
    {
454
        $value = is_string($value) ? explode('.', $value) : $value;
455
456
        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
457
458
        return [$value, $key];
459
    }
460
461
    /**
462
     * Push an item onto the beginning of an array.
463
     *
464
     * @param array $array
465
     * @param mixed $value
466
     * @param mixed $key
467
     * @return array
468
     */
469
    public static function prepend($array, $value, $key = null)
470
    {
471
        if (is_null($key)) {
472
            array_unshift($array, $value);
473
        } else {
474
            $array = [$key => $value] + $array;
475
        }
476
477
        return $array;
478
    }
479
480
    /**
481
     * Get a value from the array, and remove it.
482
     *
483
     * @param array $array
484
     * @param string $key
485
     * @param mixed $default
486
     * @return mixed
487
     */
488
    public static function pull(&$array, $key, $default = null)
489
    {
490
        $value = static::get($array, $key, $default);
491
492
        static::forget($array, $key);
493
494
        return $value;
495
    }
496
497
    /**
498
     * Get one or a specified number of random values from an array.
499
     *
500
     * @param array $array
501
     * @param int|null $number
502
     * @param bool|false $preserveKeys
503
     * @return mixed
504
     *
505
     * @throws \InvalidArgumentException
506
     */
507
    public static function random($array, $number = null, $preserveKeys = false)
508
    {
509
        $requested = is_null($number) ? 1 : $number;
510
511
        $count = count($array);
512
513
        if ($requested > $count) {
514
            throw new InvalidArgumentException(
515
                "You requested {$requested} items, but there are only {$count} items available."
516
            );
517
        }
518
519
        if (is_null($number)) {
520
            return $array[array_rand($array)];
521
        }
522
523
        if ((int)$number === 0) {
524
            return [];
525
        }
526
527
        $keys = array_rand($array, $number);
528
529
        $results = [];
530
531
        if ($preserveKeys) {
532
            foreach ((array)$keys as $key) {
533
                $results[$key] = $array[$key];
534
            }
535
        } else {
536
            foreach ((array)$keys as $key) {
537
                $results[] = $array[$key];
538
            }
539
        }
540
541
        return $results;
542
    }
543
544
    /**
545
     * Set an array item to a given value using "dot" notation.
546
     *
547
     * If no key is given to the method, the entire array will be replaced.
548
     *
549
     * @param array $array
550
     * @param string|null $key
551
     * @param mixed $value
552
     * @return array
553
     */
554
    public static function set(&$array, $key, $value)
555
    {
556
        if (is_null($key)) {
557
            return $array = $value;
558
        }
559
560
        $keys = explode('.', $key);
561
562
        foreach ($keys as $i => $key) {
0 ignored issues
show
$key is overwriting one of the parameters of this function.
Loading history...
563
            if (count($keys) === 1) {
564
                break;
565
            }
566
567
            unset($keys[$i]);
568
569
            // If the key doesn't exist at this depth, we will just create an empty array
570
            // to hold the next value, allowing us to create the arrays to hold final
571
            // values at the correct depth. Then we'll keep digging into the array.
572
            if (!isset($array[$key]) || !is_array($array[$key])) {
573
                $array[$key] = [];
574
            }
575
576
            $array = &$array[$key];
577
        }
578
579
        $array[array_shift($keys)] = $value;
580
581
        return $array;
582
    }
583
584
    /**
585
     * Shuffle the given array and return the result.
586
     *
587
     * @param array $array
588
     * @param int|null $seed
589
     * @return array
590
     */
591
    public static function shuffle($array, $seed = null)
592
    {
593
        if (is_null($seed)) {
594
            shuffle($array);
595
        } else {
596
            mt_srand($seed);
597
            shuffle($array);
598
            mt_srand();
599
        }
600
601
        return $array;
602
    }
603
604
    /**
605
     * Sort the array using the given callback or "dot" notation.
606
     *
607
     * @param array $array
608
     * @param callable|string|null $callback
609
     * @return array
610
     */
611
    public static function sort($array, $callback = null)
612
    {
613
        return Collection::make($array)->sortBy($callback)->all();
614
    }
615
616
    /**
617
     * Recursively sort an array by keys and values.
618
     *
619
     * @param array $array
620
     * @param int $options
621
     * @param bool $descending
622
     * @return array
623
     */
624
    public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
625
    {
626
        foreach ($array as &$value) {
627
            if (is_array($value)) {
628
                $value = static::sortRecursive($value, $options, $descending);
629
            }
630
        }
631
632
        if (static::isAssoc($array)) {
633
            $descending
634
                ? krsort($array, $options)
635
                : ksort($array, $options);
636
        } else {
637
            $descending
638
                ? rsort($array, $options)
639
                : sort($array, $options);
640
        }
641
642
        return $array;
643
    }
644
645
    /**
646
     * Convert the array into a query string.
647
     *
648
     * @param array $array
649
     * @return string
650
     */
651
    public static function query($array)
652
    {
653
        return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
654
    }
655
656
    /**
657
     * Filter the array using the given callback.
658
     *
659
     * @param array $array
660
     * @param callable $callback
661
     * @return array
662
     */
663
    public static function where($array, callable $callback)
664
    {
665
        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
666
    }
667
668
    /**
669
     * If the given value is not an array and not null, wrap it in one.
670
     *
671
     * @param mixed $value
672
     * @return array
673
     */
674
    public static function wrap($value)
675
    {
676
        if (is_null($value)) {
677
            return [];
678
        }
679
680
        return is_array($value) ? $value : [$value];
681
    }
682
}
683