Completed
Push — master ( 488d9a...c8b261 )
by Daniel
02:47
created

src/Arr/Arr.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
declare(strict_types=1);
3
namespace Narrowspark\Arr;
4
5
use ArrayAccess;
6
use Closure;
7
8
class Arr
9
{
10
    /**
11
     * Dotted array cache.
12
     *
13
     * @var array
14
     */
15
    protected static $dotted = [];
16
17
    /**
18
     * Determine whether the given value is array accessible.
19
     *
20
     * @param mixed $value
21
     *
22
     * @return bool
23
     */
24 8
    public static function accessible($value): bool
25
    {
26 8
        return is_array($value) || $value instanceof ArrayAccess;
27
    }
28
29
    /**
30
     * Get a value from the array, and remove it.
31
     *
32
     * @param array      $array
33
     * @param string|int $key
34
     * @param mixed      $default
35
     *
36
     * @return mixed
37
     */
38 3
    public static function pull(array &$array, $key, $default = null)
39
    {
40 3
        $value = static::get($array, $key, $default);
41
42 3
        static::forget($array, $key);
43
44 3
        return $value;
45
    }
46
47
    /**
48
     * Set an array item to a given value using "dot" notation.
49
     * If no key is given to the method, the entire array will be replaced.
50
     *
51
     * @param array       $array
52
     * @param string|null $key
53
     * @param mixed       $value
54
     *
55
     * @return array
56
     */
57 9
    public static function set(array $array, $key, $value): array
58
    {
59 9
        if ($key === null) {
60 1
            return $value;
61
        }
62
63 9
        $keys = explode('.', (string) $key);
64 9
        $current = &$array;
65
66 9
        while (count($keys) > 1) {
67 7
            $key = array_shift($keys);
68
69
            // If the key doesn't exist at this depth, we will just create an empty array
70
            // to hold the next value, allowing us to create the arrays to hold final
71
            // values at the correct depth. Then we'll keep digging into the array.
72 7
            if (! isset($current[$key]) || ! is_array($current[$key])) {
73 4
                $current[$key] = [];
74
            }
75
76 7
            $current = &$current[$key];
77
        }
78
79 9
        $current[array_shift($keys)] = $value;
80
81 9
        return $array;
82
    }
83
84
    /**
85
     * Get an item from an array using "dot" notation.
86
     * If key dont exist, you get a default value back.
87
     *
88
     * @param array           $array
89
     * @param string|int|null $key
90
     * @param mixed           $default
91
     *
92
     * @return mixed
93
     */
94 8
    public static function get(array $array, $key = null, $default = null)
95
    {
96 8
        if ($key === null) {
97 1
            return $array;
98
        }
99
100 8
        if (isset($array[$key])) {
101 5
            return static::value($array[$key]);
102
        }
103
104 4 View Code Duplication
        foreach (explode('.', (string) $key) as $segment) {
105 4
            if (! array_key_exists($segment, $array)) {
106 1
                return static::value($default);
107
            }
108
109 4
            $array = $array[$segment];
110
        }
111
112 3
        return $array;
113
    }
114
115
    /**
116
     * Add an element to the array at a specific location
117
     * using the "dot" notation.
118
     *
119
     * @param array                  $array
120
     * @param string[]|callable|null $key
121
     * @param mixed                  $value
122
     *
123
     * @return array
124
     */
125 4
    public static function add(array $array, $key, $value): array
126
    {
127 4
        $target = static::get($array, $key, []);
128
129 4
        if (! is_array($target)) {
130 2
            $target = [$target];
131
        }
132
133 4
        $target[] = $value;
134 4
        $array = static::set($array, $key, $target);
135
136 4
        return $array;
137
    }
138
139
    /**
140
     * Check if any item or items exist in an array using "dot" notation.
141
     *
142
     * @param array        $array
143
     * @param string|array $keys
144
     *
145
     * @return bool
146
     */
147 1
    public static function any(array $array, $keys): bool
148
    {
149 1
        foreach ((array) $keys as $key) {
150 1
            if (static::has($array, $key)) {
151 1
                return true;
152
            }
153
        }
154
155 1
        return false;
156
    }
157
158
    /**
159
     * Determine if the given key exists in the provided array.
160
     *
161
     * @param \ArrayAccess|array $array
162
     * @param string|int         $key
163
     *
164
     * @return bool
165
     */
166 19
    public static function exists($array, $key): bool
167
    {
168 19
        if ($array instanceof ArrayAccess) {
169
            return $array->offsetExists($key);
170
        }
171
172 19
        return array_key_exists($key, $array);
173
    }
174
175
    /**
176
     * Check if an item exists in an array using "dot" notation.
177
     *
178
     * @param \ArrayAccess|array $array
179
     * @param string|int         $keys
180
     *
181
     * @return bool
182
     */
183 9
    public static function has($array, $keys): bool
184
    {
185 9
        if (is_null($keys)) {
186 2
            return false;
187
        }
188
189 9
        $keys = (array) $keys;
190
191 9
        if (! $array) {
192 4
            return false;
193
        }
194
195 9
        if ($keys === []) {
196
            return false;
197
        }
198
199 9
        foreach ($keys as $key) {
200 9
            $subKeyArray = $array;
201
202 9
            if (static::exists($array, $key)) {
203 7
                continue;
204
            }
205
206 8 View Code Duplication
            foreach (explode('.', (string) $key) as $segment) {
207 8
                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
208 2
                    $subKeyArray = $subKeyArray[$segment];
209
                } else {
210 8
                    return false;
211
                }
212
            }
213
        }
214
215 7
        return true;
216
    }
217
218
    /**
219
     * Updates data at the given path.
220
     *
221
     * @param array    $array
222
     * @param sting    $key
0 ignored issues
show
Should the type for parameter $key not be string|sting?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
223
     * @param callable $callback Callback to update the value.
224
     *
225
     * @return mixed Updated data.
226
     */
227 1
    public static function update(array $array, string $key, callable $callback)
228
    {
229 1
        $keys = explode('.', $key);
230 1
        $current = &$array;
231
232 1
        foreach ($keys as $key) {
233 1
            if (! isset($current[$key])) {
234 1
                return $array;
235
            }
236
237 1
            $current = &$current[$key];
238
        }
239
240 1
        $current = call_user_func($callback, $current);
241
242 1
        return $array;
243
    }
244
245
    /**
246
     * Remove one or many array items from a given array using "dot" notation.
247
     *
248
     * @param array        $array
249
     * @param array|string $keys
250
     */
251 12
    public static function forget(array &$array, $keys)
252
    {
253 12
        $original = &$array;
254 12
        $keys = (array) $keys;
255
256 12
        if (count($keys) === 0) {
257 2
            return;
258
        }
259
260 10
        foreach ($keys as $key) {
261
            // if the exact key exists in the top-level, remove it
262 10
            if (static::exists($array, $key)) {
263 2
                unset($array[$key]);
264 2
                continue;
265
            }
266
267 8
            $parts = explode('.', (string) $key);
268
            // clean up before each pass
269 8
            $array = &$original;
270
271 8
            while (count($parts) > 1) {
272 8
                $part = array_shift($parts);
273 8
                if (isset($array[$part]) && is_array($array[$part])) {
274 8
                    $array = &$array[$part];
275
                } else {
276 5
                    continue 2;
277
                }
278
            }
279
280 4
            unset($array[array_shift($parts)]);
281
        }
282 10
    }
283
284
    /**
285
     * Get a random element from the array supplied.
286
     *
287
     * @param array $array the source array
288
     *
289
     * @return mixed
290
     */
291 1
    public static function random(array $array)
292
    {
293 1
        if (! count($array)) {
294 1
            return;
295
        }
296
297 1
        $keys = array_rand($array, 1);
298
299 1
        return static::value($array[$keys]);
300
    }
301
302
    /**
303
     * Get a subset of the items from the given array.
304
     *
305
     * @param string[] $array
306
     * @param string[] $keys
307
     *
308
     * @return string[]
309
     */
310 1
    public static function only(array $array, array $keys)
311
    {
312 1
        return array_intersect_key($array, array_flip($keys));
313
    }
314
315
    /**
316
     * Determines if an array is associative.
317
     *
318
     * @param array $array
319
     *
320
     * @return bool
321
     */
322 10
    public static function isAssoc(array $array): bool
323
    {
324 10
        if ($array === []) {
325 1
            return true;
326
        }
327
328 9
        return array_keys($array) !== range(0, count($array) - 1);
329
    }
330
331
    /**
332
     * Split an array in the given amount of pieces.
333
     *
334
     * @param array $array
335
     * @param int   $numberOfPieces
336
     * @param bool  $preserveKeys
337
     *
338
     * @return array
339
     */
340 6
    public static function split(array $array, int $numberOfPieces = 2, bool $preserveKeys = false): array
341
    {
342 6
        if (count($array) === 0) {
343 1
            return [];
344
        }
345
346 5
        $splitSize = ceil(count($array) / $numberOfPieces);
347
348 5
        return array_chunk($array, (int) $splitSize, $preserveKeys);
349
    }
350
351
    /**
352
     * Check if an array has a numeric index.
353
     *
354
     * @param array $array
355
     *
356
     * @return bool
357
     */
358 1
    public static function isIndexed(array $array): bool
359
    {
360 1
        if ($array === []) {
361 1
            return true;
362
        }
363
364 1
        return ! static::isAssoc($array);
365
    }
366
367
    /**
368
     * Push an item onto the beginning of an array.
369
     *
370
     * @param array $array
371
     * @param mixed $value
372
     * @param mixed $key
373
     *
374
     * @return array
375
     */
376 1
    public static function prepend(array $array, $value, $key = null): array
377
    {
378 1
        if (is_null($key)) {
379 1
            array_unshift($array, $value);
380
        } else {
381 1
            $array = [$key => $value] + $array;
382
        }
383
384 1
        return $array;
385
    }
386
387
    /**
388
     * Return the closest found value from array.
389
     *
390
     * @param array  $array
391
     * @param string $value
392
     *
393
     * @return mixed
394
     */
395 1
    public static function closest(array $array, string $value)
396
    {
397 1
        sort($array);
398 1
        $closest = $array[0];
399
400 1
        for ($i = 1, $j = count($array), $k = 0; $i < $j; $i++, $k++) {
401 1
            $middleValue = ((int) $array[$i] - (int) $array[$k]) / 2 + (int) $array[$k];
402
403 1
            if ($value >= $middleValue) {
404 1
                $closest = $array[$i];
405
            }
406
        }
407
408 1
        return static::value($closest);
409
    }
410
411
    /**
412
     * Pop value from sub array.
413
     *
414
     * @param array  $array
415
     * @param string $key
416
     *
417
     * @return mixed
418
     */
419 1
    public static function pop(array $array, string $key)
420
    {
421 1
        $keys = explode('.', $key);
422
423 1
        foreach ($keys as $key) {
424 1
            if (! isset($array[$key])) {
425 1
                return;
426
            }
427
428 1
            $array = $array[$key];
429
        }
430
431 1
        if (! is_array($array)) {
432 1
            return;
433
        }
434
435 1
        return array_pop($array);
436
    }
437
438
    /**
439
     * Swap two elements between positions.
440
     *
441
     * @param array      $array array to swap
442
     * @param string|int $swapA
443
     * @param string|int $swapB
444
     *
445
     * @return array|null
446
     */
447 1
    public static function swap(array $array, $swapA, $swapB)
448
    {
449 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
450
451 1
        return $array;
452
    }
453
454
    /**
455
     * Create a new array consisting of every n-th element.
456
     *
457
     * @param array $array
458
     * @param int   $step
459
     * @param int   $offset
460
     *
461
     * @return array
462
     */
463 1
    public static function every(array $array, int $step, int $offset = 0): array
464
    {
465 1
        $new = [];
466
467 1
        $position = 0;
468
469 1
        foreach ($array as $key => $item) {
470 1
            if ($position % $step === $offset) {
471 1
                $new[] = $item;
472
            }
473
474 1
            ++$position;
475
        }
476
477 1
        return $new;
478
    }
479
480
    /**
481
     * Indexes an array depending on the values it contains.
482
     *
483
     * @param array    $array
484
     * @param callable $callback  Function to combine values.
485
     * @param bool     $overwrite Should duplicate keys be overwritten?
486
     *
487
     * @return array Indexed values.
488
     */
489 1
    public static function combine(array $array, callable $callback, bool $overwrite = true): array
490
    {
491 1
        $combined = [];
492
493 1
        foreach ($array as $key => $value) {
494 1
            $combinator = call_user_func($callback, $value, $key);
495
496
            // fix for hhvm #1871 bug
497 1
            if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.10.0', '<=')) {
498
                $combinator->next();
499
            }
500
501 1
            $index = $combinator->key();
502
503 1
            if ($overwrite || ! isset($combined[$index])) {
504 1
                $combined[$index] = $combinator->current();
505
            }
506
        }
507
508 1
        return $combined;
509
    }
510
511
    /**
512
     * Collapse a nested array down to an array of flat key=>value pairs.
513
     *
514
     * @param array $array
515
     *
516
     * @return array
517
     */
518 3
    public static function collapse(array $array): array
519
    {
520 3
        $newArray = [];
521
522 3
        foreach ($array as $key => $value) {
523 3
            if (is_array($value)) {
524
                // strip any manually added '.'
525 3
                if (preg_match('/\./', $key)) {
526 2
                    $key = substr($key, 0, -2);
527
                }
528
529 3
                self::recurseCollapse($value, $newArray, (array) $key);
530
            } else {
531 3
                $newArray[$key] = $value;
532
            }
533
        }
534
535 3
        return $newArray;
536
    }
537
538
    /**
539
     * Divide an array into two arrays. One with keys and the other with values.
540
     *
541
     * @param array $array
542
     *
543
     * @return array
544
     */
545 1
    public static function divide(array $array): array
546
    {
547 1
        return [array_keys($array), array_values($array)];
548
    }
549
550
    /**
551
     * Stripe all empty items.
552
     *
553
     * @param array $array
554
     *
555
     * @return array
556
     */
557 1
    public static function stripEmpty(array $array): array
558
    {
559
        return array_filter($array, function ($item) {
560 1
            if (is_null($item)) {
561 1
                return false;
562
            }
563
564 1
            return (bool) trim($item);
565 1
        });
566
    }
567
568
    /**
569
     * Remove all instances of $ignore found in $elements (=== is used).
570
     *
571
     * @param array $array
572
     * @param array $ignore
573
     *
574
     * @return array
575
     */
576 1
    public static function without(array $array, array $ignore): array
577
    {
578 1
        foreach ($array as $key => $node) {
579 1
            if (in_array($node, $ignore, true)) {
580 1
                unset($array[$key]);
581
            }
582
        }
583
584 1
        return array_values($array);
585
    }
586
587
    /**
588
     * Reindexes a list of values.
589
     *
590
     * @param array $array
591
     * @param array $map      An map of correspondances of the form
592
     *                        ['currentIndex' => 'newIndex'].
593
     * @param bool  $unmapped Whether or not to keep keys that are not
594
     *                        remapped.
595
     *
596
     * @return array
597
     */
598 1
    public static function reindex(array $array, array $map, bool $unmapped = true): array
599
    {
600 1
        $reindexed = $unmapped
601 1
            ? $array
602 1
            : [];
603
604 1
        foreach ($map as $from => $to) {
605 1
            if (isset($array[$from])) {
606 1
                $reindexed[$to] = $array[$from];
607
            }
608
        }
609
610 1
        return $reindexed;
611
    }
612
613
    /**
614
     * Merges two or more arrays into one recursively.
615
     *
616
     * @return array
617
     */
618 4
    public static function merge(): array
619
    {
620 4
        $args = func_get_args();
621 4
        $array = array_shift($args);
622
623 4
        while (! empty($args)) {
624 4
            $next = array_shift($args);
625
626 4
            foreach ($next as $key => $value) {
627 4
                if (is_int($key)) {
628 3
                    if (isset($array[$key])) {
629 2
                        $array[] = $value;
630
                    } else {
631 3
                        $array[$key] = $value;
632
                    }
633 2
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
634 1
                    $array[$key] = static::merge($array[$key], $value);
635
                } else {
636 4
                    $array[$key] = $value;
637
                }
638
            }
639
        }
640
641 4
        return $array;
642
    }
643
644
    /**
645
     *  Makes every value that is numerically indexed a key, given $default
646
     *  as value.
647
     *
648
     *  @param array $array
649
     *  @param mixed $default
650
     *
651
     *  @return array
652
     */
653 1
    public static function normalize(array $array, $default): array
654
    {
655 1
        $normalized = [];
656
657 1
        foreach ($array as $key => $value) {
658 1
            if (is_numeric($key)) {
659 1
                $key = $value;
660 1
                $value = $default;
661
            }
662
663 1
            $normalized[$key] = $value;
664
        }
665
666 1
        return $normalized;
667
    }
668
669
    /**
670
     * Extend one array with another.
671
     *
672
     * @return array
673
     */
674 3
    public static function extend(): array
675
    {
676 3
        $merged = [];
677
678 3
        foreach (func_get_args() as $array) {
679 3
            foreach ($array as $key => $value) {
680 3
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
681 2
                    $merged[$key] = static::extend($merged[$key], $value);
682
                } else {
683 3
                    $merged[$key] = $value;
684
                }
685
            }
686
        }
687
688 3
        return $merged;
689
    }
690
691
    /**
692
     * Transforms a 1-dimensional array into a multi-dimensional one,
693
     * exploding keys according to a separator.
694
     *
695
     * @param array $array
696
     *
697
     * @return array
698
     */
699 1
    public static function asHierarchy(array $array): array
700
    {
701 1
        $hierarchy = [];
702
703 1
        foreach ($array as $key => $value) {
704 1
            $segments = explode('.', (string) $key);
705 1
            $valueSegment = array_pop($segments);
706 1
            $branch = &$hierarchy;
707
708 1
            foreach ($segments as $segment) {
709 1
                if (! isset($branch[$segment])) {
710 1
                    $branch[$segment] = [];
711
                }
712
713 1
                $branch = &$branch[$segment];
714
            }
715
716 1
            $branch[$valueSegment] = $value;
717
        }
718
719 1
        return $hierarchy;
720
    }
721
722
    /**
723
     * Separates elements from an array into groups.
724
     * The function maps an element to the key that will be used for grouping.
725
     * If no function is passed, the element itself will be used as key.
726
     *
727
     * @param array         $array
728
     * @param callable|null $callback
729
     *
730
     * @return array
731
     */
732 1
    public static function groupBy(array $array, callable $callback = null): array
733
    {
734
        $callback = $callback ?: function ($value) {
735 1
            return $value;
736 1
        };
737
738 1
        return array_reduce(
739
            $array,
740
            function ($buckets, $value) use ($callback) {
741 1
                $key = call_user_func($callback, $value);
742
743 1
                if (! array_key_exists($key, $buckets)) {
744 1
                    $buckets[$key] = [];
745
                }
746
747 1
                $buckets[$key][] = $value;
748
749 1
                return $buckets;
750 1
            },
751 1
            []
752
        );
753
    }
754
755
    /**
756
     * Flatten a multi-dimensional associative array with dots.
757
     *
758
     * @param array  $array
759
     * @param string $prepend
760
     *
761
     * @return array
762
     */
763 4
    public static function dot(array $array, string $prepend = ''): array
764
    {
765 4
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
766
767 4
        if (array_key_exists($cache, self::$dotted)) {
768 1
            return self::$dotted[$cache];
769
        }
770
771 3
        $results = [];
772
773 3 View Code Duplication
        foreach ($array as $key => $value) {
774 3
            if (is_array($value)) {
775 2
                $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
776
            } else {
777 3
                $results[$prepend . $key] = $value;
778
            }
779
        }
780
781 3
        return self::$dotted[$cache] = $results;
782
    }
783
784
    /**
785
     * Expand a dotted array. Acts the opposite way of Arr::dot().
786
     *
787
     * @param array     $array
788
     * @param int|float $depth
789
     *
790
     * @return array
791
     */
792 10
    public static function unDot(array $array, $depth = INF): array
793
    {
794 10
        $results = [];
795
796 10
        foreach ($array as $key => $value) {
797 9
            if (count($dottedKeys = explode('.', (string) $key, 2)) > 1) {
798 9
                $results[$dottedKeys[0]][$dottedKeys[1]] = $value;
799
            } else {
800 9
                $results[$key] = $value;
801
            }
802
        }
803
804 10
        foreach ($results as $key => $value) {
805 9
            if (is_array($value) && ! empty($value) && $depth > 1) {
806 9
                $results[$key] = static::unDot($value, $depth - 1);
807
            }
808
        }
809
810 10
        return $results;
811
    }
812
813
    /**
814
     * Flatten a nested array to a separated key.
815
     *
816
     * @param array       $array
817
     * @param string|null $separator
818
     * @param string      $prepend
819
     *
820
     * @return array
821
     */
822 1
    public static function flatten(array $array, string $separator = null, string $prepend = ''): array
823
    {
824 1
        $flattened = [];
825
826 1 View Code Duplication
        foreach ($array as $key => $value) {
827 1
            if (is_array($value)) {
828 1
                $flattened = array_merge($flattened, static::flatten($value, $separator, $prepend . $key . $separator));
829
            } else {
830 1
                $flattened[$prepend . $key] = $value;
831
            }
832
        }
833
834 1
        return $flattened;
835
    }
836
837
    /**
838
     * Expand a flattened array with dots to a multi-dimensional associative array.
839
     *
840
     * @param array  $array
841
     * @param string $prepend
842
     *
843
     * @return array
844
     */
845 4
    public static function expand(array $array, string $prepend = ''): array
846
    {
847 4
        $results = [];
848
849 4
        if ($prepend) {
850 2
            $prepend .= '.';
851
        }
852
853 4
        foreach ($array as $key => $value) {
854 4
            if ($prepend) {
855 2
                $pos = strpos($key, $prepend);
856
857 2
                if ($pos === 0) {
858 1
                    $key = substr($key, strlen($prepend));
859
                }
860
            }
861
862 4
            $results = static::set($results, $key, $value);
863
        }
864
865 4
        return $results;
866
    }
867
868
    /**
869
     * Reset all numerical indexes of an array (start from zero).
870
     * Non-numerical indexes will stay untouched. Returns a new array.
871
     *
872
     * @param array      $array
873
     * @param bool|false $deep
874
     *
875
     * @return array
876
     */
877 3
    public static function reset(array $array, $deep = false)
878
    {
879 3
        $target = [];
880
881 3
        foreach ($array as $key => $value) {
882 3
            if ($deep && is_array($value)) {
883 1
                $value = static::reset($value);
884
            }
885
886 3
            if (is_numeric($key)) {
887 3
                $target[] = $value;
888
            } else {
889 3
                $target[$key] = $value;
890
            }
891
        }
892
893 3
        return $target;
894
    }
895
896
    /**
897
     * Extend one array with another. Non associative arrays will not be merged
898
     * but rather replaced.
899
     *
900
     * @return array
901
     */
902 4
    public static function extendDistinct()
903
    {
904 4
        $merged = [];
905
906 4
        foreach (func_get_args() as $array) {
907 4
            foreach ($array as $key => $value) {
908 4
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
909 3
                    if (static::isAssoc($value) && static::isAssoc($merged[$key])) {
910 2
                        $merged[$key] = static::extendDistinct($merged[$key], $value);
911
912 2
                        continue;
913
                    }
914
                }
915
916 4
                $merged[$key] = $value;
917
            }
918
        }
919
920 4
        return $merged;
921
    }
922
923
    /**
924
     * Sort the array using the given callback.
925
     *
926
     * @param array    $array
927
     * @param callable $callback
928
     * @param int      $options
929
     * @param bool     $descending
930
     *
931
     * @return array
932
     */
933 1
    public static function sort(
934
        array $array,
935
        callable $callback,
936
        int $options = SORT_REGULAR,
937
        bool $descending = false
938
    ): array {
939 1
        $results = [];
940
941
        // First we will loop through the items and get the comparator from a callback
942
        // function which we were given. Then, we will sort the returned values and
943
        // and grab the corresponding values for the sorted keys from this array.
944 1
        foreach ($array as $key => $value) {
945 1
            $results[$key] = $callback($value, $key);
946
        }
947
948 1
        $descending ? arsort($results, $options) : asort($results, $options);
949
950
        // Once we have sorted all of the keys in the array, we will loop through them
951
        // and grab the corresponding model so we can set the underlying items list
952
        // to the sorted version. Then we'll just return the collection instance.
953 1
        foreach (array_keys($results) as $key) {
954 1
            $results[$key] = $array[$key];
955
        }
956
957 1
        return $results;
958
    }
959
960
    /**
961
     * Recursively sort an array by keys and values.
962
     *
963
     * @param array $array
964
     *
965
     * @return array
966
     */
967 1
    public static function sortRecursive(array $array)
968
    {
969 1
        foreach ($array as &$value) {
970 1
            if (is_array($value)) {
971 1
                $value = static::sortRecursive($value);
972
            }
973
        }
974
975
        // sort associative array
976 1
        if (static::isAssoc($array)) {
977 1
            ksort($array);
978
            // sort regular array
979
        } else {
980 1
            sort($array);
981
        }
982
983 1
        return $array;
984
    }
985
986
    /**
987
     * Will turn each element in $arr into an array then appending
988
     * the associated indexs from the other arrays into this array as well.
989
     *
990
     * @return array<*,array>
991
     */
992 2
    public static function zip()
993
    {
994 2
        $args = func_get_args();
995 2
        $originalArr = $args[0];
996
997 2
        array_shift($args);
998
999 2
        $array = [];
1000
1001 2
        foreach ($originalArr as $key => $value) {
1002 2
            $array[$key] = [$value];
1003
1004 2
            foreach ($args as $k => $v) {
1005 2
                $array[$key][] = current($args[$k]);
1006
1007 2
                if (next($args[$k]) === false && $args[$k] !== [null]) {
1008 2
                    $args[$k] = [null];
1009
                }
1010
            }
1011
        }
1012
1013 2
        return $array;
1014
    }
1015
1016
    /**
1017
     * Applies the callback to the elements of the given arrays
1018
     *
1019
     * @param array    $array
1020
     * @param callable $callback
1021
     *
1022
     * @return array
1023
     */
1024 1
    public static function map(array $array, callable $callback)
1025
    {
1026 1
        $newArray = [];
1027
1028 1
        foreach ($array as $key => $item) {
1029 1
            $result = call_user_func($callback, $item, $key);
1030
1031 1
            $newArray = is_array($result) ?
1032 1
                array_replace_recursive($array, $result) :
1033 1
                array_merge_recursive($array, (array) $result);
1034
        }
1035
1036 1
        return $newArray;
1037
    }
1038
1039
    /**
1040
     * Filters each of the given values through a function.
1041
     *
1042
     * @param array    $array
1043
     * @param callable $callback
1044
     *
1045
     * @return array
1046
     */
1047 2
    public static function filter(array $array, callable $callback)
1048
    {
1049 2
        $newArray = [];
1050
1051 2
        foreach ($array as $key => $item) {
1052 2
            if (call_user_func($callback, $item, $key)) {
1053 2
                $newArray[$key] = $item;
1054
            }
1055
        }
1056
1057 2
        return $newArray;
1058
    }
1059
1060
    /**
1061
     * Returns whether every element of the array satisfies the given predicate or not.
1062
     * Works with Iterators too.
1063
     *
1064
     * @param array    $array
1065
     * @param callable $predicate
1066
     *
1067
     * @return bool
1068
     */
1069 1
    public static function all(array $array, callable $predicate)
1070
    {
1071 1
        foreach ($array as $key => $value) {
1072 1
            if (! call_user_func($predicate, $value, $key, $array)) {
1073 1
                return false;
1074
            }
1075
        }
1076
1077 1
        return true;
1078
    }
1079
1080
    /**
1081
     *  The opposite of filter().
1082
     *
1083
     *  @param array    $array
1084
     *  @param callable $callback Function to filter values.
1085
     *
1086
     *  @return array filtered array.
1087
     */
1088
    public static function reject(array $array, callable $callback): array
1089
    {
1090 1
        return static::filter($array, function ($value, $key) use ($callback) {
1091 1
            return ! call_user_func($callback, $value, $key);
1092 1
        });
1093
    }
1094
1095
    /**
1096
     * Filter the array using the given Closure.
1097
     *
1098
     * @param array    $array
1099
     * @param callable $callback
1100
     *
1101
     * @return array
1102
     */
1103 1
    public static function where(array $array, callable $callback): array
1104
    {
1105 1
        $filtered = [];
1106
1107 1
        foreach ($array as $key => $value) {
1108 1
            if (call_user_func($callback, $key, $value)) {
1109 1
                $filtered[$key] = $value;
1110
            }
1111
        }
1112
1113 1
        return $filtered;
1114
    }
1115
1116
    /**
1117
     * Return the first element in an array passing a given truth test.
1118
     *
1119
     * @param array    $array
1120
     * @param callable $callback
1121
     * @param mixed    $default
1122
     *
1123
     * @return mixed
1124
     */
1125 2
    public static function first(array $array, callable $callback, $default = null)
1126
    {
1127 2
        if (is_null($callback)) {
1128
            if (empty($array)) {
1129
                return value($default);
1130
            }
1131
1132
            foreach ($array as $item) {
1133
                return $item;
1134
            }
1135
        }
1136
1137 2
        foreach ($array as $key => $value) {
1138 2
            if (call_user_func($callback, $key, $value)) {
1139 2
                return $value;
1140
            }
1141
        }
1142
1143 1
        return static::value($default);
1144
    }
1145
1146
    /**
1147
     * Return the last element in an array passing a given truth test.
1148
     *
1149
     * @param array    $array
1150
     * @param callable $callback
1151
     * @param mixed    $default
1152
     *
1153
     * @return mixed
1154
     */
1155 1
    public static function last(array $array, callable $callback, $default = null)
1156
    {
1157 1
        if (is_null($callback)) {
1158
            return empty($array) ? static::value($default) : end($array);
1159
        }
1160
1161 1
        return static::first(array_reverse($array), $callback, $default);
1162
    }
1163
1164
    /**
1165
     * Return the default value of the given value.
1166
     *
1167
     * @param mixed $value
1168
     *
1169
     * @return mixed
1170
     */
1171 10
    public static function value($value)
1172
    {
1173 10
        return $value instanceof Closure ? $value() : $value;
1174
    }
1175
1176
    /**
1177
     * Recurse through an array, add the leaf items to the $newArray var
1178
     *
1179
     * @param array $subject
1180
     * @param array &$newArray
1181
     * @param array $stack
1182
     *
1183
     * @return string[]|null
1184
     */
1185 3
    private static function recurseCollapse(array $subject, array &$newArray, $stack = [])
1186
    {
1187 3
        foreach ($subject as $key => $value) {
1188 3
            $fstack = array_merge($stack, [$key]);
1189
1190 3
            if (is_array($value)) {
1191 3
                self::recurseCollapse($value, $newArray, $fstack);
1192
            } else {
1193 3
                $top = array_shift($fstack);
1194 3
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
1195 3
                $newArray[$top . $arrayPart] = $value;
1196
            }
1197
        }
1198 3
    }
1199
}
1200