Completed
Push — master ( 3e2377...524d53 )
by Daniel
10:37 queued 04:39
created

Arr::forget()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 0
cts 0
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 18
nc 6
nop 2
crap 56
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
    public static function accessible($value): bool
25
    {
26
        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 $key
34
     * @param mixed  $default
35
     *
36
     * @return mixed
37 8
     */
38
    public static function pull(array &$array, string $key, $default = null)
39 8
    {
40
        $value = static::get($array, $key, $default);
41 8
42 2
        static::forget($array, $key);
43
44
        return $value;
45 6
    }
46 6
47 6
    /**
48
     * Set an array item to a given value using "dot" notation.
49 2
     * 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 8
     * @return array
56
     */
57 8
    public static function set(array $array, $key, $value): array
58 8
    {
59 8
        if ($key === null) {
60 8
            return $value;
61
        }
62 8
63 8
        $keys = explode('.', $key);
64 8
        $current = &$array;
65 8
66 8
        while (count($keys) > 1) {
67 8
            $key = array_shift($keys);
68 8
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
            if (! isset($current[$key]) || ! is_array($current[$key])) {
73
                $current[$key] = [];
74
            }
75
76
            $current = &$current[$key];
77
        }
78
79
        $current[array_shift($keys)] = $value;
80
81
        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
    public static function get(array $array, $key = null, $default = null)
95
    {
96
        if ($key === null) {
97
            return $array;
98
        }
99
100
        if (isset($array[$key])) {
101
            return static::value($array[$key]);
102
        }
103
104 View Code Duplication
        foreach (explode('.', $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
            if (! array_key_exists($segment, $array)) {
106
                return static::value($default);
107
            }
108
109
            $array = $array[$segment];
110
        }
111
112
        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
    public static function add(array $array, $key, $value): array
126
    {
127
        $target = static::get($array, $key, []);
128
129
        if (! is_array($target)) {
130
            $target = [$target];
131
        }
132
133
        $target[] = $value;
134
        $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
        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
    public static function any(array $array, $keys): bool
148
    {
149
        foreach ((array) $keys as $key) {
150
            if (static::has($array, $key)) {
151
                return true;
152
            }
153
        }
154
155
        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
    public static function exists($array, $key): bool
167
    {
168
        if ($array instanceof ArrayAccess) {
169
            return $array->offsetExists($key);
170
        }
171
172
        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
    public static function has($array, $keys): bool
184
    {
185
        if (is_null($keys)) {
186
            return false;
187
        }
188
189
        $keys = (array) $keys;
190
191
        if (! $array) {
192
            return false;
193
        }
194
195
        if ($keys === []) {
196
            return false;
197
        }
198
199
        foreach ($keys as $key) {
200
            $subKeyArray = $array;
201
202
            if (static::exists($array, $key)) {
203
                continue;
204
            }
205
206 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
                if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
208
                    $subKeyArray = $subKeyArray[$segment];
209
                } else {
210
                    return false;
211
                }
212
            }
213
        }
214
215
        return true;
216
    }
217
218
    /**
219
     * Updates data at the given path.
220
     *
221
     * @param array          $array
222
     * @param array|string[] $key
223
     * @param callable       $callback Callback to update the value.
224
     *
225
     * @return mixed Updated data.
226
     */
227
    public static function update(array $array, $key, callable $callback)
228
    {
229
        $keys = explode('.', $key);
230
        $current = &$array;
231
232
        foreach ($keys as $key) {
233
            if (! isset($current[$key])) {
234
                return $array;
235
            }
236
237
            $current = &$current[$key];
238
        }
239
240
        $current = call_user_func($callback, $current);
241
242
        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
    public static function forget(array &$array, $keys)
252
    {
253
        $original = &$array;
254
        $keys = (array) $keys;
255
256
        if (count($keys) === 0) {
257
            return;
258
        }
259
260
        foreach ($keys as $key) {
261
            // if the exact key exists in the top-level, remove it
262
            if (static::exists($array, $key)) {
263
                unset($array[$key]);
264
                continue;
265
            }
266
267
            $parts = explode('.', $key);
268
            // clean up before each pass
269
            $array = &$original;
270
271
            while (count($parts) > 1) {
272
                $part = array_shift($parts);
273
                if (isset($array[$part]) && is_array($array[$part])) {
274
                    $array = &$array[$part];
275
                } else {
276
                    continue 2;
277
                }
278
            }
279
280
            unset($array[array_shift($parts)]);
281
        }
282
    }
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
    public static function random(array $array)
292
    {
293
        if (! count($array)) {
294
            return;
295
        }
296
297
        $keys = array_rand($array, 1);
298
299
        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
    public static function only(array $array, array $keys)
311
    {
312
        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
    public static function isAssoc(array $array): bool
323
    {
324
        if ($array === []) {
325
            return true;
326
        }
327
328
        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
    public static function split(array $array, int $numberOfPieces = 2, bool $preserveKeys = false): array
341
    {
342
        if (count($array) === 0) {
343
            return [];
344
        }
345
346
        $splitSize = ceil(count($array) / $numberOfPieces);
347
348
        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
    public static function isIndexed(array $array): bool
359
    {
360
        if ($array === []) {
361
            return true;
362
        }
363
364
        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
    public static function prepend(array $array, $value, $key = null): array
377
    {
378
        if (is_null($key)) {
379
            array_unshift($array, $value);
380
        } else {
381
            $array = [$key => $value] + $array;
382
        }
383
384
        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
    public static function closest(array $array, string $value)
396
    {
397
        sort($array);
398
        $closest = $array[0];
399
400
        for ($i = 1, $j = count($array), $k = 0; $i < $j; $i++, $k++) {
401
            $middleValue = ((int) $array[$i] - (int) $array[$k]) / 2 + (int) $array[$k];
402
403
            if ($value >= $middleValue) {
404
                $closest = $array[$i];
405
            }
406
        }
407
408
        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
    public static function pop(array $array, string $key)
420
    {
421
        $keys = explode('.', $key);
422
423
        foreach ($keys as $key) {
424
            if (! isset($array[$key])) {
425
                return;
426
            }
427
428
            $array = $array[$key];
429
        }
430
431
        if (! is_array($array)) {
432
            return;
433
        }
434
435
        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
    public static function swap(array $array, $swapA, $swapB)
448
    {
449
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
450
451
        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
    public static function every(array $array, int $step, int $offset = 0): array
464
    {
465
        $new = [];
466
467
        $position = 0;
468
469
        foreach ($array as $key => $item) {
470
            if ($position % $step === $offset) {
471
                $new[] = $item;
472
            }
473
474
            ++$position;
475
        }
476
477
        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
    public static function combine(array $array, callable $callback, bool $overwrite = true): array
490
    {
491
        $combined = [];
492
493
        foreach ($array as $key => $value) {
494
            $combinator = call_user_func($callback, $value, $key);
495
496
            // fix for hhvm #1871 bug
497
            if (defined('HHVM_VERSION') && version_compare(HHVM_VERSION, '3.10.0', '<=')) {
498
                $combinator->next();
499
            }
500
501
            $index = $combinator->key();
502
503
            if ($overwrite || ! isset($combined[$index])) {
504
                $combined[$index] = $combinator->current();
505
            }
506
        }
507
508
        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
    public static function collapse(array $array): array
519
    {
520
        $newArray = [];
521
522
        foreach ($array as $key => $value) {
523
            if (is_array($value)) {
524
                // strip any manually added '.'
525
                if (preg_match('/\./', $key)) {
526
                    $key = substr($key, 0, -2);
527
                }
528
529
                self::recurseCollapse($value, $newArray, (array) $key);
530
            } else {
531
                $newArray[$key] = $value;
532
            }
533
        }
534
535
        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
    public static function divide(array $array): array
546
    {
547
        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
    public static function stripEmpty(array $array): array
558
    {
559
        return array_filter($array, function ($item) {
560
            if (is_null($item)) {
561
                return false;
562
            }
563
564
            return (bool) trim($item);
565
        });
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
    public static function without(array $array, array $ignore): array
577
    {
578
        foreach ($array as $key => $node) {
579
            if (in_array($node, $ignore, true)) {
580
                unset($array[$key]);
581
            }
582
        }
583
584
        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
    public static function reindex(array $array, array $map, bool $unmapped = true): array
599
    {
600
        $reindexed = $unmapped
601
            ? $array
602
            : [];
603
604
        foreach ($map as $from => $to) {
605
            if (isset($array[$from])) {
606
                $reindexed[$to] = $array[$from];
607
            }
608
        }
609
610
        return $reindexed;
611
    }
612
613
    /**
614
     * Merges two or more arrays into one recursively.
615
     *
616
     * @return array
617
     */
618
    public static function merge(): array
619
    {
620
        $args = func_get_args();
621
        $array = array_shift($args);
622
623
        while (! empty($args)) {
624
            $next = array_shift($args);
625
626
            foreach ($next as $key => $value) {
627
                if (is_int($key)) {
628
                    if (isset($array[$key])) {
629
                        $array[] = $value;
630
                    } else {
631
                        $array[$key] = $value;
632
                    }
633
                } elseif (is_array($value) && isset($array[$key]) && is_array($array[$key])) {
634
                    $array[$key] = static::merge($array[$key], $value);
635
                } else {
636
                    $array[$key] = $value;
637
                }
638
            }
639
        }
640
641
        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
    public static function normalize(array $array, $default): array
654
    {
655
        $normalized = [];
656
657
        foreach ($array as $key => $value) {
658
            if (is_numeric($key)) {
659
                $key = $value;
660
                $value = $default;
661
            }
662
663
            $normalized[$key] = $value;
664
        }
665
666
        return $normalized;
667
    }
668
669
    /**
670
     * Extend one array with another.
671
     *
672
     * @return array
673
     */
674
    public static function extend(): array
675
    {
676
        $merged = [];
677
678
        foreach (func_get_args() as $array) {
679
            foreach ($array as $key => $value) {
680
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
681
                    $merged[$key] = static::extend($merged[$key], $value);
682
                } else {
683
                    $merged[$key] = $value;
684
                }
685
            }
686
        }
687
688
        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
    public static function asHierarchy(array $array): array
700
    {
701
        $hierarchy = [];
702
703
        foreach ($array as $key => $value) {
704
            $segments = explode('.', $key);
705
            $valueSegment = array_pop($segments);
706
            $branch = &$hierarchy;
707
708
            foreach ($segments as $segment) {
709
                if (! isset($branch[$segment])) {
710
                    $branch[$segment] = [];
711
                }
712
713
                $branch = &$branch[$segment];
714
            }
715
716
            $branch[$valueSegment] = $value;
717
        }
718
719
        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
    public static function groupBy(array $array, callable $callback = null): array
733
    {
734
        $callback = $callback ?: function ($value) {
735
            return $value;
736
        };
737
738
        return array_reduce(
739
            $array,
740
            function ($buckets, $value) use ($callback) {
741
                $key = call_user_func($callback, $value);
742
743
                if (! array_key_exists($key, $buckets)) {
744
                    $buckets[$key] = [];
745
                }
746
747
                $buckets[$key][] = $value;
748
749
                return $buckets;
750
            },
751
            []
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
    public static function dot(array $array, string $prepend = ''): array
764
    {
765
        $cache = serialize(['array' => $array, 'prepend' => $prepend]);
766
767
        if (array_key_exists($cache, self::$dotted)) {
768
            return self::$dotted[$cache];
769
        }
770
771
        $results = [];
772
773 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
            if (is_array($value)) {
775
                $results = array_merge($results, static::dot($value, $prepend . $key . '.'));
776
            } else {
777
                $results[$prepend . $key] = $value;
778
            }
779
        }
780
781
        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
    public static function unDot(array $array, $depth = INF): array
793
    {
794
        $results = [];
795
796
        foreach ($array as $key => $value) {
797
            if (count($dottedKeys = explode('.', $key, 2)) > 1) {
798
                $results[$dottedKeys[0]][$dottedKeys[1]] = $value;
799
            } else {
800
                $results[$key] = $value;
801
            }
802
        }
803
804
        foreach ($results as $key => $value) {
805
            if (is_array($value) && ! empty($value) && $depth > 1) {
806
                $results[$key] = static::unDot($value, $depth - 1);
807
            }
808
        }
809
810
        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
    public static function flatten(array $array, string $separator = null, string $prepend = ''): array
823
    {
824
        $flattened = [];
825
826 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
            if (is_array($value)) {
828
                $flattened = array_merge($flattened, static::flatten($value, $separator, $prepend . $key . $separator));
829
            } else {
830
                $flattened[$prepend . $key] = $value;
831
            }
832
        }
833
834
        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
    public static function expand(array $array, string $prepend = ''): array
846
    {
847
        $results = [];
848
849
        if ($prepend) {
850
            $prepend .= '.';
851
        }
852
853
        foreach ($array as $key => $value) {
854
            if ($prepend) {
855
                $pos = strpos($key, $prepend);
856
857
                if ($pos === 0) {
858
                    $key = substr($key, strlen($prepend));
859
                }
860
            }
861
862
            $results = static::set($results, $key, $value);
863
        }
864
865
        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
    public static function reset(array $array, $deep = false)
878
    {
879
        $target = [];
880
881
        foreach ($array as $key => $value) {
882
            if ($deep && is_array($value)) {
883
                $value = static::reset($value);
884
            }
885
886
            if (is_numeric($key)) {
887
                $target[] = $value;
888
            } else {
889
                $target[$key] = $value;
890
            }
891
        }
892
893
        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
    public static function extendDistinct()
903
    {
904
        $merged = [];
905
906
        foreach (func_get_args() as $array) {
907
            foreach ($array as $key => $value) {
908
                if (is_array($value) && static::has($merged, $key) && is_array($merged[$key])) {
909
                    if (static::isAssoc($value) && static::isAssoc($merged[$key])) {
910
                        $merged[$key] = static::extendDistinct($merged[$key], $value);
911
912
                        continue;
913
                    }
914
                }
915
916
                $merged[$key] = $value;
917
            }
918
        }
919
920
        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
    public static function sort(
934
        array $array,
935
        callable $callback,
936
        int $options = SORT_REGULAR,
937
        bool $descending = false
938
    ): array {
939
        $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
        foreach ($array as $key => $value) {
945
            $results[$key] = $callback($value, $key);
946
        }
947
948
        $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
        foreach (array_keys($results) as $key) {
954
            $results[$key] = $array[$key];
955
        }
956
957
        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
    public static function sortRecursive(array $array)
968
    {
969
        foreach ($array as &$value) {
970
            if (is_array($value)) {
971
                $value = static::sortRecursive($value);
972
            }
973
        }
974
975
        // sort associative array
976
        if (static::isAssoc($array)) {
977
            ksort($array);
978
            // sort regular array
979
        } else {
980
            sort($array);
981
        }
982
983
        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
    public static function zip()
993
    {
994
        $args = func_get_args();
995
        $originalArr = $args[0];
996
997
        array_shift($args);
998
999
        $array = [];
1000
1001
        foreach ($originalArr as $key => $value) {
1002
            $array[$key] = [$value];
1003
1004
            foreach ($args as $k => $v) {
1005
                $array[$key][] = current($args[$k]);
1006
1007
                if (next($args[$k]) === false && $args[$k] !== [null]) {
1008
                    $args[$k] = [null];
1009
                }
1010
            }
1011
        }
1012
1013
        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
    public static function map(array $array, callable $callback)
1025
    {
1026
        $newArray = [];
1027
1028
        foreach ($array as $key => $item) {
1029
            $result = call_user_func($callback, $item, $key);
1030
1031
            $newArray = is_array($result) ?
1032
                array_replace_recursive($array, $result) :
1033
                array_merge_recursive($array, (array) $result);
1034
        }
1035
1036
        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
    public static function filter(array $array, callable $callback)
1048
    {
1049
        $newArray = [];
1050
1051
        foreach ($array as $key => $item) {
1052
            if (call_user_func($callback, $item, $key)) {
1053
                $newArray[$key] = $item;
1054
            }
1055
        }
1056
1057
        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
    public static function all(array $array, callable $predicate)
1070
    {
1071
        foreach ($array as $key => $value) {
1072
            if (! call_user_func($predicate, $value, $key, $array)) {
1073
                return false;
1074
            }
1075
        }
1076
1077
        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
        return static::filter($array, function ($value, $key) use ($callback) {
1091
            return ! call_user_func($callback, $value, $key);
1092
        });
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
    public static function where(array $array, callable $callback): array
1104
    {
1105
        $filtered = [];
1106
1107
        foreach ($array as $key => $value) {
1108
            if (call_user_func($callback, $key, $value)) {
1109
                $filtered[$key] = $value;
1110
            }
1111
        }
1112
1113
        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
    public static function first(array $array, callable $callback, $default = null)
1126
    {
1127
        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
        foreach ($array as $key => $value) {
1138
            if (call_user_func($callback, $key, $value)) {
1139
                return $value;
1140
            }
1141
        }
1142
1143
        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
    public static function last(array $array, callable $callback, $default = null)
1156
    {
1157
        if (is_null($callback)) {
1158
            return empty($array) ? static::value($default) : end($array);
1159
        }
1160
1161
        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
    public static function value($value)
1172
    {
1173
        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
    private static function recurseCollapse(array $subject, array &$newArray, $stack = [])
1186
    {
1187
        foreach ($subject as $key => $value) {
1188
            $fstack = array_merge($stack, [$key]);
1189
1190
            if (is_array($value)) {
1191
                self::recurseCollapse($value, $newArray, $fstack);
1192
            } else {
1193
                $top = array_shift($fstack);
1194
                $arrayPart = count($fstack) ? '.' . implode('.', $fstack) : '';
1195
                $newArray[$top . $arrayPart] = $value;
1196
            }
1197
        }
1198
    }
1199
}
1200