Completed
Push — master ( 51f4c8...0429f3 )
by Daniel
08:06 queued 05:31
created

Arr::last()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 3
crap 3
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 125 can also be of type callable; however, Narrowspark\Arr\Arr::set() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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 20
    public static function exists($array, $key): bool
167
    {
168 20
        if ($array instanceof ArrayAccess) {
169
            return $array->offsetExists($key);
170
        }
171
172 20
        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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
Documentation introduced by
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 = $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 13
    public static function forget(array &$array, $keys)
252
    {
253 13
        $original = &$array;
254 13
        $keys = (array) $keys;
255
256 13
        if (count($keys) === 0) {
257 2
            return;
258
        }
259
260 11
        foreach ($keys as $key) {
261
            // if the exact key exists in the top-level, remove it
262 11
            if (static::exists($array, $key)) {
263 3
                unset($array[$key]);
264 3
                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 11
    }
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 = $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('/\./', (string) $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 = $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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 = $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
        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
1050
    }
1051
1052
    /**
1053
     * Returns whether every element of the array satisfies the given predicate or not.
1054
     * Works with Iterators too.
1055
     *
1056
     * @param array    $array
1057
     * @param callable $predicate
1058
     *
1059
     * @return bool
1060
     */
1061 1
    public static function all(array $array, callable $predicate)
1062
    {
1063 1
        foreach ($array as $key => $value) {
1064 1
            if (! $predicate($value, $key, $array)) {
1065 1
                return false;
1066
            }
1067
        }
1068
1069 1
        return true;
1070
    }
1071
1072
    /**
1073
     *  The opposite of filter().
1074
     *
1075
     *  @param array    $array
1076
     *  @param callable $callback Function to filter values.
1077
     *
1078
     *  @return array filtered array.
1079
     */
1080
    public static function reject(array $array, callable $callback): array
1081
    {
1082 1
        return static::filter($array, function ($value, $key) use ($callback) {
1083 1
            return ! $callback($value, $key);
1084 1
        });
1085
    }
1086
1087
    /**
1088
     * Filter the array using the given Closure.
1089
     *
1090
     * @param array    $array
1091
     * @param callable $callback
1092
     *
1093
     * @return array
1094
     */
1095 1
    public static function where(array $array, callable $callback): array
1096
    {
1097 1
        $filtered = [];
1098
1099 1
        foreach ($array as $key => $value) {
1100 1
            if ($callback($key, $value)) {
1101 1
                $filtered[$key] = $value;
1102
            }
1103
        }
1104
1105 1
        return $filtered;
1106
    }
1107
1108
    /**
1109
     * Return the first element in an array passing a given truth test.
1110
     *
1111
     * @param array         $array
1112
     * @param callable|null $callback
1113
     * @param mixed         $default
1114
     *
1115
     * @return mixed
1116
     */
1117 2
    public static function first(array $array, callable $callback = null, $default = null)
1118
    {
1119 2
        if (is_null($callback)) {
1120 1
            if (empty($array)) {
1121
                return static::value($default);
1122
            }
1123
1124 1
            foreach ($array as $item) {
1125 1
                return $item;
1126
            }
1127
        }
1128
1129 2
        foreach ($array as $key => $value) {
1130 2
            if ($callback($key, $value)) {
1131 2
                return $value;
1132
            }
1133
        }
1134
1135 1
        return static::value($default);
1136
    }
1137
1138
    /**
1139
     * Return the last element in an array passing a given truth test.
1140
     *
1141
     * @param array         $array
1142
     * @param callable|null $callback
1143
     * @param mixed         $default
1144
     *
1145
     * @return mixed
1146
     */
1147 1
    public static function last(array $array, callable $callback = null, $default = null)
1148
    {
1149 1
        if ($callback === null) {
1150 1
            return empty($array) ? static::value($default) : end($array);
1151
        }
1152
1153 1
        return static::first(array_reverse($array, true), $callback, $default);
1154
    }
1155
1156
    /**
1157
     * Get all of the given array except for a specified array of items.
1158
     *
1159
     * @param array        $array
1160
     * @param array|string $keys
1161
     *
1162
     * @return array
1163
     */
1164 1
    public static function except(array $array, $keys): array
1165
    {
1166 1
        static::forget($array, $keys);
1167
1168 1
        return $array;
1169
    }
1170
1171
    /**
1172
     * Return the default value of the given value.
1173
     *
1174
     * @param mixed $value
1175
     *
1176
     * @return mixed
1177
     */
1178 10
    public static function value($value)
1179
    {
1180 10
        return $value instanceof Closure ? $value() : $value;
1181
    }
1182
1183
    /**
1184
     * Recurse through an array, add the leaf items to the $newArray var
1185
     *
1186
     * @param array $subject
1187
     * @param array &$newArray
1188
     * @param array $stack
1189
     *
1190
     * @return string[]|null
1191
     */
1192 3
    private static function recurseCollapse(array $subject, array &$newArray, $stack = [])
1193
    {
1194 3
        foreach ($subject as $key => $value) {
1195 3
            $fstack = array_merge($stack, [$key]);
1196
1197 3
            if (is_array($value)) {
1198 3
                self::recurseCollapse($value, $newArray, $fstack);
1199
            } else {
1200 3
                $top = array_shift($fstack);
1201 3
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
1202 3
                $newArray[$top . $arrayPart] = $value;
1203
            }
1204
        }
1205 3
    }
1206
}
1207