Passed
Pull Request — develop (#86)
by Glynn
05:26
created

pick()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Array functions.
5
 *
6
 * This file is part of PinkCrab Function Constructors.
7
 *
8
 * PinkCrab Function Constructors is free software: you can redistribute it and/or modify it under the terms of the
9
 * GNU General Public License as published by the Free Software Foundation, either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * PinkCrab Function Constructors is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
13
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 * See the GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with PinkCrab Function Constructors.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 *
19
 * @author Glynn Quelch <[email protected]>
20
 * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
21
 * @package PinkCrab\FunctionConstructors
22
 * @since 0.0.1
23
 *
24
 * @template Number of int|float
25
 * @phpstan-template Number of int|float
26
 * @psalm-template Number of int|float
27
 */
28
29
declare(strict_types=1);
30
31
namespace PinkCrab\FunctionConstructors\Arrays;
32
33
use Closure;
34
use stdClass;
35
use PinkCrab\FunctionConstructors\Comparisons as Comp;
36
37
/**
38
 * Returns a Closure for appending a value to an array.
39
 *
40
 * @param mixed $value
41
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
42
 */
43
function append($value): Closure
44
{
45
    /**
46
     * @param array<int|string, mixed> $array
47
     * @return array<int|string, mixed>
48
     */
49
    return function (array $array) use ($value): array {
50
        $array[] = $value;
51
        return $array;
52
    };
53
}
54
55
/**
56
 * Returns a Closure for prepending a value to an array.
57
 *
58
 * @param mixed $value
59
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
60
 */
61
function prepend($value): Closure
62
{
63
    /**
64
     * @param array<int|string, mixed> $array
65
     * @return array<int|string, mixed>
66
     */
67
    return function (array $array) use ($value): array {
68
        array_unshift($array, $value);
69
        return $array;
70
    };
71
}
72
73
/**
74
 * Returns a Closure for pushing a value to the head of an array
75
 *
76
 * @param array<int|string, mixed> $array
77
 * @return Closure(mixed):array<int|string, mixed>
78
 * @deprecated 0.3.0 Use prepend() instead.
79
 */
80
function pushHead(array $array): Closure
81
{
82
    trigger_error('Deprecated function called. This function will be removed in later versions.', E_USER_DEPRECATED);
83
    /**
84
     * @param mixed $value Adds value start of array.
85
     * @return array New array with value on head.
86
     */
87
    return function ($value) use ($array): array {
88
        array_unshift($array, $value);
89
        return $array;
90
    };
91
}
92
93
/**
94
 * Returns a Closure for pushing a value to the head of an array
95
 *
96
 * @param array<int|string, mixed> $array
97
 * @return Closure(mixed):array<int|string, mixed>
98
 * @deprecated 0.3.0 Use append() instead.
99
 */
100
function pushTail(array $array): Closure
101
{
102
    trigger_error('Deprecated function called. This function will be removed in later versions.', E_USER_DEPRECATED);
103
    /**
104
     * @param mixed $value Adds value end of array.
105
     * @return array<int|string, mixed> New array with value on tail.
106
     */
107
    return function ($value) use ($array): array {
108
        $array[] = $value;
109
        return $array;
110
    };
111
}
112
113
/**
114
 * Gets the first value from an array.
115
 *
116
 * @param array<int|string, mixed> $array The array.
117
 * @return mixed Will return the first value is array is not empty, else null.
118
 */
119
function head(array $array)
120
{
121
    return !empty($array) ? array_values($array)[0] : null;
122
}
123
124
/**
125
 * Gets the last value from an array.
126
 *
127
 * @param array<int|string, mixed> $array The array.
128
 * @return mixed Will return the last value is array is not empty, else null.
129
 */
130
function last(array $array)
131
{
132
    return !empty($array) ? array_reverse($array, false)[0] : null;
133
}
134
135
/**
136
 * Gets the remainder values from an array, after first item removed.
137
 *
138
 * @param array<int|string, mixed> $array
139
 * @return array<int|string, mixed>|null Will return the first value is array is not empty, else null.
140
 */
141
function tail(array $array)
142
{
143
    // Return null if empty.
144
    if (empty($array)) {
145
        return null;
146
    }
147
148
    // Remove the first item from the array.
149
    array_shift($array);
150
    return $array;
151
}
152
153
154
/**
155
 * Creates a Closure for concatenating arrays with a defined glue.
156
 *
157
 * @param string|null $glue The string to join each element. If null, will be no separation between elements.
158
 * @return Closure(array<int|string, mixed>):string
159
 *
160
 */
161
function toString(?string $glue = null): Closure
162
{
163
    /**
164
     * @param array<int|string, mixed> $array Array join
165
     * @return string
166
     */
167
    return function (array $array) use ($glue): string {
168
        return $glue ? \join($glue, $array) : \join($array);
169
    };
170
}
171
172
/**
173
 * Creates a Closure for zipping 2 arrays.
174
 *
175
 * @param array<mixed> $additional Values with the same key will be paired.
176
 * @param mixed $default The fallback value if the additional array doesn't have the same length
177
 * @return Closure(array<mixed>):array<array{mixed, mixed}>
178
 *
179
 */
180
function zip(array $additional, $default = null): Closure
181
{
182
    $additional = array_values($additional);
183
    return function (array $array) use ($additional, $default) {
184
        $array = array_values($array);
185
        return array_reduce(
186
            array_keys($array),
187
            function ($carry, $key) use ($array, $additional, $default): array {
188
                $carry[] = array(
189
                    $array[$key],
190
                    array_key_exists($key, $additional) ? $additional[$key] : $default,
191
                );
192
                return $carry;
193
            },
194
            array()
195
        );
196
    };
197
}
198
199
200
/*
201
 *                                ********************
202
 *                                * Filter Compilers *
203
 *                                ********************
204
 */
205
206
207
/**
208
 * Compiles an array if a value is passed.
209
 * Returns the array if nothing passed.
210
 *
211
 * @param mixed[] $inital Sets up the inner value.
212
 * @return Closure
213
 */
214
function arrayCompiler(array $inital = array()): Closure
215
{
216
    /**
217
     * @param mixed $value Adds value to inner array if value set, else returns.
218
     * @return mixed[]|Closure
219
     */
220
    return function ($value = null) use ($inital) {
221
        if ($value) {
222
            $inital[] = $value;
223
        }
224
        return !is_null($value) ? arrayCompiler($inital) : $inital;
225
    };
226
}
227
228
/**
229
 * Creates a typed array compiler.
230
 * All values which do not pass the validator are not added.
231
 *
232
 * @param Closure(mixed):bool $validator Used to validate values before adding to array.
233
 * @param mixed[] $inital The inital data to start with
234
 * @return Closure
235
 */
236
function arrayCompilerTyped(callable $validator, array $inital = array()): Closure
237
{
238
    // Ensure all is validated from initial.
239
    $inital = array_filter($inital, $validator);
240
241
    /**
242
     * @param mixed $value
243
     * @return mixed[]|Closure
244
     */
245
    return function ($value = null) use ($validator, $inital) {
246
        if (!is_null($value) && $validator($value)) {
247
            $inital[] = $value;
248
        }
249
        return !is_null($value) ? arrayCompilerTyped($validator, $inital) : $inital;
250
    };
251
}
252
253
254
255
/*
256
 *                                ********************
257
 *                                * Filter Functions *
258
 *                                ********************
259
 */
260
261
262
/**
263
 * Created a Closure for filtering an array.
264
 *
265
 * @param callable $callable The function to apply to the array.
266
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
267
 */
268
function filter(callable $callable): Closure
269
{
270
    /**
271
     * @param array<int|string, mixed> $source Array to filter
272
     * @return array<int|string, mixed> Filtered array.
273
     */
274
    return function (array $source) use ($callable): array {
275
        return array_filter($source, $callable);
276
    };
277
}
278
279
/**
280
 * Create a Closure for filtering an array by a key.
281
 *
282
 * @param callable $callable The function to apply to the array.
283
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
284
 */
285
function filterKey(callable $callable): Closure
286
{
287
    /**
288
     * @param array<int|string, mixed> $source Array to filter
289
     * @return array<int|string, mixed> Filtered array.
290
     */
291
    return function (array $source) use ($callable): array {
292
        return array_filter($source, $callable, \ARRAY_FILTER_USE_KEY);
293
    };
294
}
295
296
/**
297
 * Creates a Closure for running an array through various callbacks for all true response.
298
 * Wrapper for creating a AND group of callbacks and running through array filter.
299
 *
300
 * @param callable ...$callables
301
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
302
 */
303
function filterAnd(callable ...$callables): Closure
304
{
305
    /**
306
     * @param array<int|string, mixed> $source Array to filter
307
     * @return array<int|string, mixed> Filtered array.
308
     */
309
    return function (array $source) use ($callables): array {
310
        return array_filter($source, Comp\groupAnd(...$callables));
311
    };
312
}
313
314
/**
315
 * Creates a Closure for running an array through various callbacks for any true response.
316
 * Wrapper for creating a OR group of callbacks and running through array filter.
317
 *
318
 * @param callable ...$callables
319
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
320
 */
321
function filterOr(callable ...$callables): Closure
322
{
323
    /**
324
     * @param array<int|string, mixed> $source Array to filter
325
     * @return array<int|string, mixed> Filtered array.
326
     */
327
    return function (array $source) use ($callables): array {
328
        return array_filter($source, Comp\groupOr(...$callables));
329
    };
330
}
331
332
/**
333
 * Creates a Closure for running array filter and getting the first value.
334
 *
335
 * @param callable $func
336
 * @return Closure(array<int|string, mixed>):?mixed
337
 */
338
function filterFirst(callable $func): Closure
339
{
340
    /**
341
     * @param array<int|string, mixed> $array The array to filter
342
     * @return mixed|null The first element from the filtered array or null if filter returns empty
343
     */
344
    return function (array $array) use ($func) {
345
        foreach ($array as $value) {
346
            $result = $func($value);
347
            if (\is_bool($result) && $result) {
348
                return $value;
349
            }
350
        }
351
    };
352
}
353
354
/**
355
 * Creates a Closure for running array filter and getting the last value.
356
 *
357
 * @param callable $func
358
 * @return Closure(array<int|string, mixed>):?mixed
359
 */
360
function filterLast(callable $func): Closure
361
{
362
    /**
363
     * @param array<int|string, mixed> $array The array to filter
364
     * @return mixed|null The last element from the filtered array.
365
     */
366
    return function (array $array) use ($func) {
367
        while ($value = array_pop($array)) {
368
            $result = $func($value);
369
            if (\is_bool($result) && $result) {
370
                return $value;
371
            }
372
        }
373
    };
374
}
375
376
/**
377
 * Creates a Closure which takes an array, applies a filter, then maps the
378
 * results of the map.
379
 *
380
 *
381
 * @param callable(mixed):bool $filter Function to of filter contents
382
 * @param callable(mixed):mixed $map Function to map results of filter function.
383
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
384
 */
385
function filterMap(callable $filter, callable $map): Closure
386
{
387
    /**
388
     * @param array<int|string, mixed> $array The array to filter then map.
389
     * @return array<int|string, mixed>
390
     */
391
    return function (array $array) use ($filter, $map): array {
392
        return array_map($map, array_filter($array, $filter));
393
    };
394
}
395
396
/**
397
 * Runs an array through a filters, returns the total count of true
398
 *
399
 * @param callable $function
400
 * @return Closure(array<int|string, mixed>):int
401
 */
402
function filterCount(callable $function): Closure
403
{
404
    /**
405
     * @param array<int|string, mixed> $array
406
     * @return int Count
407
     */
408
    return function (array $array) use ($function) {
409
        return count(array_filter($array, $function));
410
    };
411
}
412
413
/**
414
 * Returns a Closure for partitioning an array based
415
 * on the results of a filter type function.
416
 * Callable will be cast to a bool, if truthy will be listed under 1 key, else 0 for falsey
417
 *
418
 * @param callable(mixed):(bool|int) $function
419
 * @return Closure(mixed[]):array{0:mixed[], 1:mixed[]}
420
 */
421
function partition(callable $function): Closure
422
{
423
    /**
424
     * @param mixed[] $array
425
     * @return array{0:mixed[], 1:mixed[]}
426
     */
427
    return function (array $array) use ($function): array {
428
        return array_reduce(
429
            $array,
430
            /**
431
             * @param array{0:mixed[], 1:mixed[]} $carry
432
             * @param mixed $element
433
             * @return array{0:mixed[], 1:mixed[]}
434
             */
435
            function ($carry, $element) use ($function): array {
436
                $key             = (bool) $function($element) ? 1 : 0;
437
                $carry[$key][] = $element;
438
                return $carry;
439
            },
440
            array(array(), array())
441
        );
442
    };
443
}
444
445
/**
446
 * Returns a closure for checking all elements pass a filter.
447
 *
448
 * @param callable(mixed):bool $function
449
 * @return Closure(mixed[]):bool
450
 */
451
function filterAll(callable $function): Closure
452
{
453
    /**
454
     * @param mixed[] $array
455
     * @return bool
456
     */
457
    return function (array $array) use ($function): bool {
458
        foreach ($array as $value) {
459
            if (false === $function($value)) {
460
                return false;
461
            }
462
        }
463
        return true;
464
    };
465
}
466
467
468
/**
469
 * Returns a closure for checking any elements pass a filter.
470
 *
471
 * @param callable(mixed):bool $function
472
 * @return Closure(mixed[]):bool
473
 */
474
function filterAny(callable $function): Closure
475
{
476
    /**
477
     * @param mixed[] $array
478
     * @return bool
479
     */
480
    return function (array $array) use ($function): bool {
481
        foreach ($array as $value) {
482
            if (true === $function($value)) {
483
                return true;
484
            }
485
        }
486
        return false;
487
    };
488
}
489
490
491
/*
492
 *                           *****************
493
 *                           * Map Functions *
494
 *                           *****************
495
 */
496
497
498
499
/**
500
 * Returns a Closure which can be passed an array.
501
 *
502
 * @param callable(mixed):mixed $func Callback to apply to each element in array.
503
 * @return Closure(mixed[]):mixed[]
504
 */
505
function map(callable $func): Closure
506
{
507
    /**
508
     * @param mixed[] $array The array to map
509
     * @return mixed[]
510
     */
511
    return function (array $array) use ($func): array {
512
        return array_map($func, $array);
513
    };
514
}
515
516
/**
517
 * Returns a Closure for mapping of an arrays keys.
518
 * Setting the key to an existing index will overwrite the current value at same index.
519
 *
520
 * @param callable $func
521
 * @return Closure(mixed[]):mixed[]
522
 */
523
function mapKey(callable $func): Closure
524
{
525
    /**
526
     * @param mixed[] $array The array to map
527
     * @return mixed[]
528
     */
529
    return function (array $array) use ($func): array {
530
        return array_reduce(
531
            array_keys($array),
532
            function ($carry, $key) use ($func, $array) {
533
                $carry[$func($key)] = $array[$key];
534
                return $carry;
535
            },
536
            array()
537
        );
538
    };
539
}
540
541
/**
542
 * Returns a Closure for mapping an array with additional data.
543
 *
544
 * @param callable(mixed ...$a):mixed $func
545
 * @param mixed ...$data
546
 * @return Closure(mixed[]):mixed[]
547
 */
548
function mapWith(callable $func, ...$data): Closure
549
{
550
    /**
551
     * @param mixed[] $array The array to map
552
     * @return mixed[]
553
     */
554
    return function (array $array) use ($func, $data): array {
555
        return array_map(
556
            function ($e) use ($data, $func) {
557
                return $func($e, ...$data);
558
            },
559
            $array
560
        );
561
    };
562
}
563
564
/**
565
 * Returns a Closure for mapping an array with access to value and key.
566
 *
567
 * @param callable(int|string $key, mixed $value):mixed $func
568
 * @return Closure(mixed[]):mixed[]
569
 */
570
function mapWithKey(callable $func): Closure
571
{
572
    /**
573
     * @param mixed[] $array The array to map
574
     * @return mixed[]
575
     */
576
    return function (array $array) use ($func): array {
577
        return array_map(
578
            function ($key, $value) use ($func) {
579
                return $func($value, $key);
580
            },
581
            $array,
582
            array_keys($array)
583
        );
584
    };
585
}
586
587
/**
588
 * Returns a Closure foreaching over an array
589
 *
590
 * @param callable(int|string $key, mixed $value):void $func
591
 * @return Closure(mixed[]):void
592
 */
593
function each(callable $func): Closure
594
{
595
    /**
596
     * @param mixed[] $array The array to map
597
     * @return void
598
     */
599
    return function (array $array) use ($func): void {
600
        array_map(
601
            function ($key, $value) use ($func) {
602
                $func($key, $value);
603
            },
604
            array_keys($array),
605
            $array
606
        );
607
    };
608
}
609
610
/**
611
 * Returns a Closure for flattening and mapping an array
612
 *
613
 * @param callable(mixed):mixed $function The function to map the element. (Will no be called if resolves to array)
614
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
615
 * @return Closure(mixed[]):mixed[]
616
 */
617
function flatMap(callable $function, ?int $n = null): Closure
618
{
619
    /**
620
     * @param mixed[] $array
621
     * @return mixed[]
622
     */
623
    return function (array $array) use ($n, $function): array {
624
        return array_reduce(
625
            $array,
626
            /**
627
             * @param mixed[] $carry
628
             * @param mixed $element
629
             * @return mixed[]
630
             */
631
            function (array $carry, $element) use ($n, $function): array {
632
                if (is_array($element) && (is_null($n) || $n > 0)) {
633
                    $carry = array_merge($carry, flatMap($function, $n ? $n - 1 : null)($element));
634
                } else {
635
                    $carry[] = is_array($element) ? $element : $function($element);
636
                }
637
                return $carry;
638
            },
639
            array()
640
        );
641
    };
642
}
643
644
/*
645
 *                         **********************
646
 *                         * General Operations *
647
 *                         **********************
648
 */
649
650
651
/**
652
 * Creates a Closure for grouping an array.
653
 *
654
 * @param callable(mixed):(string|int) $function The function to group by.
655
 * @return Closure(mixed):mixed[]
656
 */
657
function groupBy(callable $function): Closure
658
{
659
    /**
660
     * @param mixed[] $array The array to be grouped
661
     * @return mixed[] Grouped array.
662
     */
663
    return function (array $array) use ($function): array {
664
        return array_reduce(
665
            $array,
666
            /**
667
             * @param mixed[] $carry
668
             * @param mixed $element
669
             * @return mixed[]
670
             */
671
            function ($carry, $item) use ($function): array {
672
                $carry[call_user_func($function, $item)][] = $item;
673
                return $carry;
674
            },
675
            array()
676
        );
677
    };
678
}
679
680
/**
681
 * Creates a Closure for chunking an array to set a limit.
682
 *
683
 * @param int $count The max size of each chunk. Must not be less than 1!
684
 * @param bool $preserveKeys Should inital keys be kept. Default false.
685
 * @return Closure(mixed[]):mixed[]
686
 */
687
function chunk(int $count, bool $preserveKeys = false): Closure
688
{
689
    /**
690
     * @param mixed[] $array Array to chunk
691
     * @return mixed[]
692
     */
693
    return function (array $array) use ($count, $preserveKeys): array {
694
        return array_chunk($array, max(1, $count), $preserveKeys);
695
    };
696
}
697
698
/**
699
 * Create callback for extracting a single column from an array.
700
 *
701
 * @param string $column Column to retrieve.
702
 * @param string $key Use column for assigning as the index. defaults to numeric keys if null.
703
 * @return Closure(mixed[]):mixed[]
704
 */
705
function column(string $column, ?string $key = null): Closure
706
{
707
    /**
708
     * @param mixed[] $array
709
     * @return mixed[]
710
     */
711
    return function (array $array) use ($column, $key): array {
712
        return array_column($array, $column, $key);
713
    };
714
}
715
716
/**
717
 * Returns a Closure for flattening an array to a defined depth
718
 *
719
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
720
 * @return Closure(mixed[] $var): mixed[]
721
 */
722
function flattenByN(?int $n = null): Closure
723
{
724
    /**
725
     * @param mixed[] $array Array to flatten
726
     * @return mixed[]
727
     */
728
    return function (array $array) use ($n): array {
729
        return array_reduce(
730
            $array,
731
            /**
732
             * @param array<int|string, mixed> $carry
733
             * @param mixed|mixed[] $element
734
             * @return array<int|string, mixed>
735
             */
736
            function (array $carry, $element) use ($n): array {
737
                // Remove empty arrays.
738
                if (is_array($element) && empty($element)) {
739
                    return $carry;
740
                }
741
                // If the element is an array and we are still flattening, call again
742
                if (is_array($element) && (is_null($n) || $n > 0)) { // @phpstan-ignore-line
743
                    $carry = array_merge($carry, flattenByN($n ? $n - 1 : null)($element));
744
                } else {
745
                    // Else just add the element.
746
                    $carry[] = $element;
747
                }
748
                return $carry;
749
            },
750
            array()
751
        );
752
    };
753
}
754
755
/**
756
 * Returns a closure for recursively changing values in an array.
757
 *
758
 * @param mixed[] ...$with The array values to replace with
759
 * @return Closure(mixed[]):mixed[]
760
 */
761
function replaceRecursive(array ...$with): Closure
762
{
763
    /**
764
     * @param mixed[] $array The array to have elements replaced from.
765
     * @return mixed[] Array with replacements.
766
     */
767
    return function (array $array) use ($with): array {
768
        return array_replace_recursive($array, ...$with);
769
    };
770
}
771
772
/**
773
 * Returns a Closure for changing all values in a flat array, based on key.
774
 *
775
 * @param mixed[] ...$with Array with values to replace with, must have matching key with base array.
776
 * @return Closure(mixed[]):mixed[]
777
 */
778
function replace(array ...$with): Closure
779
{
780
    /**
781
     * @param mixed[] $array The array to have elements replaced from.
782
     * @return mixed[] Array with replacements.
783
     */
784
    return function (array $array) use ($with): array {
785
        return array_replace($array, ...$with);
786
    };
787
}
788
789
/**
790
 * Returns a Closure for doing array_sum with the results of a function/expression.
791
 *
792
 * @param callable(mixed):Number $function The function to return the value for array sum
793
 * @return Closure(mixed[]):Number
794
 */
795
function sumWhere(callable $function): Closure
796
{
797
    /**
798
     * @param mixed[] $array Array to do sum() on.
799
     * @return Number The total.
800
     */
801
    return function (array $array) use ($function) {
802
        return array_sum(array_map($function, $array));
803
    };
804
}
805
806
/**
807
 * Creates a closure for casting an array to an object.
808
 * Assumed all properties are public
809
 * None existing properties will be set as dynamic properties.
810
 *
811
 * @param object|null $object The object to cast to, defaults to stdClass
812
 * @return Closure(mixed[]):object
813
 * @throws \InvalidArgumentException If property does not exist or is not public.
814
 */
815
function toObject($object = null): Closure
816
{
817
    $object = $object ?? new stdClass();
818
819
    // Throws an exception if $object is not an object.
820
    if (!is_object($object)) {
821
        throw new \InvalidArgumentException('Object must be an object.');
822
    }
823
824
    /**
825
     * @param mixed[] $array
826
     * @return object
827
     */
828
    return function (array $array) use ($object) {
829
        foreach ($array as $key => $value) {
830
            // If key is not a string or numerical, skip it.
831
            if (!is_string($key) || is_numeric($key)) {
832
                continue;
833
            }
834
835
            try {
836
                $object->{$key} = $value;
837
            } catch (\Throwable $th) {
0 ignored issues
show
Unused Code introduced by
catch (\Throwable $th) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
838
                throw new \InvalidArgumentException("Property {$key} does not exist or is not public.");
839
            }
840
        }
841
        return $object;
842
    };
843
}
844
845
/**
846
 * Creates a closure for encoding json with defined flags/depth
847
 *
848
 * @param int $flags json_encode flags (default = 0)
849
 * @param int $depth Nodes deep to encode (default = 512)
850
 * @return \Closure(mixed):?string
851
 * @constants JSON_FORCE_OBJECT, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP,
852
 *            JSON_HEX_APOS, JSON_INVALID_UTF8_IGNORE,
853
 *            JSON_INVALID_UTF8_SUBSTITUTE, JSON_NUMERIC_CHECK,
854
 *            JSON_PARTIAL_OUTPUT_ON_ERROR, JSON_PRESERVE_ZERO_FRACTION,
855
 *            JSON_PRETTY_PRINT, JSON_UNESCAPED_LINE_TERMINATORS,
856
 *            JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, JSON_THROW_ON_ERROR
857
 */
858
function toJson(int $flags = 0, int $depth = 512): Closure
859
{
860
    /**
861
     * @param mixed $data
862
     * @return string|null
863
     */
864
    return function ($data) use ($flags, $depth): ?string {
865
        return \json_encode($data, $flags, max(1, $depth)) ?: null;
866
    };
867
}
868
869
870
/*
871
 *                         ****************
872
 *                         *  Array Sort  *
873
 *                         ****************
874
 */
875
876
877
/**
878
 * Returns a Closure for doing regular SORT against an array.
879
 * Doesn't maintain keys.
880
 *
881
 * @param int $flag Uses php stock sort constants or numerical values.
882
 * @return Closure(mixed[]):mixed[]
883
 */
884
function sort(int $flag = SORT_REGULAR): Closure
885
{
886
    /**
887
     *  @param mixed[]$array The array to sort
888
     *  @return mixed[] The sorted array (new array)
889
     */
890
    return function (array $array) use ($flag) {
891
        \sort($array, $flag);
892
        return $array;
893
    };
894
}
895
896
/**
897
 * Returns a Closure for doing regular Reverse SORT against an array.
898
 * Doesn't maintain keys.
899
 *
900
 * @param int $flag Uses php stock sort constants or numerical values.
901
 * @return Closure(mixed[]):mixed[]
902
 */
903
function rsort(int $flag = SORT_REGULAR): Closure
904
{
905
    /**
906
     *  @param mixed[]$array The array to sort
907
     *  @return mixed[] The sorted array (new array)
908
     */
909
    return function (array $array) use ($flag) {
910
        \rsort($array, $flag);
911
        return $array;
912
    };
913
}
914
915
916
/**
917
 * Returns a Closure for sorting an array by key in ascending order.
918
 *
919
 * @param int $flag Uses php stock sort constants or numerical values.
920
 * @return Closure(mixed[]):mixed[]
921
 */
922
function ksort(int $flag = SORT_REGULAR): Closure
923
{
924
    /**
925
     *  @param mixed[]$array The array to sort
926
     *  @return mixed[] The sorted array (new array)
927
     */
928
    return function (array $array) use ($flag) {
929
        \ksort($array, $flag);
930
        return $array;
931
    };
932
}
933
934
/**
935
 * Returns a Closure for sorting an array by key in descending (reverse) order.
936
 *
937
 * @param int $flag Uses php stock sort constants or numerical values.
938
 * @return Closure(mixed[]):mixed[]
939
 */
940
function krsort(int $flag = SORT_REGULAR): Closure
941
{
942
    /**
943
     *  @param mixed[]$array The array to sort
944
     *  @return mixed[] The sorted array (new array)
945
     */
946
    return function (array $array) use ($flag) {
947
        \krsort($array, $flag);
948
        return $array;
949
    };
950
}
951
952
/**
953
 * Returns a Closure for sorting an array by value in ascending order.
954
 * Maintain keys.
955
 *
956
 * @param int $flag Uses php stock sort constants or numerical values.
957
 * @return Closure(mixed[]):mixed[]
958
 */
959
function asort(int $flag = SORT_REGULAR): Closure
960
{
961
    /**
962
     *  @param mixed[]$array The array to sort
963
     *  @return mixed[] The sorted array (new array)
964
     */
965
    return function (array $array) use ($flag) {
966
        \asort($array, $flag);
967
        return $array;
968
    };
969
}
970
971
/**
972
 * Returns a Closure for sorting an array by value in descending (reverse) order.
973
 * Maintain keys.
974
 *
975
 * @param int $flag Uses php stock sort constants or numerical values.
976
 * @return Closure(mixed[]):mixed[]
977
 */
978
function arsort(int $flag = SORT_REGULAR): Closure
979
{
980
    /**
981
     *  @param mixed[]$array The array to sort
982
     *  @return mixed[] The sorted array (new array)
983
     */
984
    return function (array $array) use ($flag) {
985
        \arsort($array, $flag);
986
        return $array;
987
    };
988
}
989
990
/**
991
 * Returns a Closure for sorting an array using a "natural order" algorithm
992
 *
993
 * @return Closure(mixed[]):mixed[]
994
 */
995
function natsort(): Closure
996
{
997
    /**
998
     *  @param mixed[]$array The array to sort
999
     *  @return mixed[] The sorted array (new array)
1000
     */
1001
    return function (array $array) {
1002
        \natsort($array);
1003
        return $array;
1004
    };
1005
}
1006
1007
/**
1008
 * Returns a Closure for sorting an array using a case insensitive "natural order" algorithm
1009
 *
1010
 * @return Closure(mixed[]):mixed[]
1011
 */
1012
function natcasesort(): Closure
1013
{
1014
    /**
1015
     *  @param mixed[]$array The array to sort
1016
     *  @return mixed[] The sorted array (new array)
1017
     */
1018
    return function (array $array) {
1019
        \natcasesort($array);
1020
        return $array;
1021
    };
1022
}
1023
1024
/**
1025
 * Returns a Closure for sorting an array by key using a custom comparison function
1026
 *
1027
 * @param callable(mixed $a, mixed $b): int $function
1028
 * @return Closure(mixed[]):mixed[]
1029
 */
1030
function uksort(callable $function): Closure
1031
{
1032
    /**
1033
     *  @param mixed[] $array The array to sort
1034
     *  @return mixed[] The sorted array (new array)
1035
     */
1036
    return function (array $array) use ($function) {
1037
        \uksort($array, $function);
1038
        return $array;
1039
    };
1040
}
1041
1042
/**
1043
 * Returns a Closure for sorting an array using a custom comparison function
1044
 * Maintain keys.
1045
 *
1046
 * @param callable(mixed $a, mixed $b): int $function
1047
 * @return Closure(mixed[]):mixed[]
1048
 */
1049
function uasort(callable $function): Closure
1050
{
1051
    /**
1052
     *  @param mixed[]$array The array to sort
1053
     *  @return mixed[] The sorted array (new array)
1054
     */
1055
    return function (array $array) use ($function) {
1056
        \uasort($array, $function);
1057
        return $array;
1058
    };
1059
}
1060
1061
1062
/**
1063
 * Returns a Closure for sorting an array using a custom comparison function
1064
 * Doesn't maintain keys.
1065
 *
1066
 * @param callable(mixed $a, mixed $b): int $function
1067
 * @return Closure(mixed[]):mixed[]
1068
 */
1069
function usort(callable $function): Closure
1070
{
1071
    /**
1072
     *  @param mixed[]$array The array to sort
1073
     *  @return mixed[] The sorted array (new array)
1074
     */
1075
    return function (array $array) use ($function) {
1076
        \usort($array, $function);
1077
        return $array;
1078
    };
1079
}
1080
1081
1082
/**
1083
 * Returns a Closure for applying a function to every element of an array
1084
 *
1085
 * @param callable(mixed $carry, mixed $value):mixed $function
1086
 * @param mixed $initialValue
1087
 * @return Closure(mixed[]):mixed[]
1088
 */
1089
function scan(callable $function, $initialValue): Closure
1090
{
1091
    return function (array $array) use ($function, $initialValue) {
1092
        $carry[] = $initialValue;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$carry was never initialized. Although not strictly required by PHP, it is generally a good practice to add $carry = array(); before regardless.
Loading history...
1093
        foreach ($array as $key => $value) {
1094
            $initialValue = $function($initialValue, $value);
1095
            $carry[]      = $initialValue;
1096
        }
1097
        return $carry;
1098
    };
1099
}
1100
1101
/**
1102
 * Returns a Closure for applying a function to every element of an array
1103
 *
1104
 * @param callable(mixed $carry, mixed $value):mixed $function
1105
 * @param mixed $initialValue
1106
 * @return Closure(mixed[]):mixed[]
1107
 */
1108
function scanR(callable $function, $initialValue): Closure
1109
{
1110
    return function (array $array) use ($function, $initialValue) {
1111
        $carry[] = $initialValue;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$carry was never initialized. Although not strictly required by PHP, it is generally a good practice to add $carry = array(); before regardless.
Loading history...
1112
        foreach (array_reverse($array) as $key => $value) {
1113
            $initialValue = $function($initialValue, $value);
1114
            $carry[]      = $initialValue;
1115
        }
1116
        return \array_reverse($carry);
1117
    };
1118
}
1119
1120
/**
1121
 * Creates a function for defining the callback and initial for reduce/fold
1122
 *
1123
 * @param callable(mixed $carry, mixed $value): mixed $callable
1124
 * @param mixed $initial
1125
 * @return Closure(mixed[]):mixed
1126
 */
1127
function fold(callable $callable, $initial = array()): Closure
1128
{
1129
    /**
1130
     * @param mixed[] $array
1131
     * @return mixed
1132
     */
1133
    return function (array $array) use ($callable, $initial) {
1134
        return array_reduce($array, $callable, $initial);
1135
    };
1136
}
1137
1138
/**
1139
 * Creates a function for defining the callback and initial for reduce/fold
1140
 *
1141
 * @param callable(mixed $carry, mixed $value): mixed $callable
1142
 * @param mixed $initial
1143
 * @return Closure(mixed[]):mixed
1144
 */
1145
function foldR(callable $callable, $initial = array()): Closure
1146
{
1147
    /**
1148
     * @param mixed[] $array
1149
     * @return mixed
1150
     */
1151
    return function (array $array) use ($callable, $initial) {
1152
        return array_reduce(\array_reverse($array), $callable, $initial);
1153
    };
1154
}
1155
1156
/**
1157
 * Creates a function for defining the callback and initial for reduce/fold, with the key
1158
 * also passed to the callback.
1159
 *
1160
 * @param callable(mixed $carry, int|string $key, mixed $value): mixed $callable
1161
 * @param mixed $initial
1162
 * @return Closure(mixed[]):mixed
1163
 */
1164
function foldKeys(callable $callable, $initial = array()): Closure
1165
{
1166
    /**
1167
     * @param mixed[] $array
1168
     * @return mixed
1169
     */
1170
    return function (array $array) use ($callable, $initial) {
1171
        foreach ($array as $key => $value) {
1172
            $initial = $callable($initial, $key, $value);
1173
        }
1174
        return $initial;
1175
    };
1176
}
1177
1178
/**
1179
 * Creates a function which takes the first n elements from an array
1180
 *
1181
 * @param int $count
1182
 * @return Closure(mixed[]):mixed[]
1183
 * @throws \InvalidArgumentException if count is negative
1184
 */
1185
function take(int $count = 1): Closure
1186
{
1187
    // throw InvalidArgumentException if count is negative
1188
    if ($count < 0) {
1189
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1190
    }
1191
1192
    /**
1193
     * @param mixed[] $array
1194
     * @return mixed[]
1195
     */
1196
    return function (array $array) use ($count) {
1197
        return \array_slice($array, 0, $count);
1198
    };
1199
}
1200
1201
/**
1202
 * Creates a function which takes the last n elements from an array
1203
 *
1204
 * @param int $count
1205
 * @return Closure(mixed[]):mixed[]
1206
 * @throws \InvalidArgumentException if count is negative
1207
 */
1208
function takeLast(int $count = 1): Closure
1209
{
1210
    // throw InvalidArgumentException if count is negative
1211
    if ($count < 0) {
1212
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1213
    }
1214
1215
    // If count is 0, return an empty array
1216
    if ($count === 0) {
1217
        return function (array $array) {
1218
            return array();
1219
        };
1220
    }
1221
1222
    /**
1223
     * @param mixed[] $array
1224
     * @return mixed[]
1225
     */
1226
    return function (array $array) use ($count) {
1227
        return \array_slice($array, -$count);
1228
    };
1229
}
1230
1231
/**
1232
 * Creates a function that allows you to take a slice of an array until the passed conditional
1233
 * returns true.
1234
 *
1235
 * @param callable(mixed): bool $conditional
1236
 * @return Closure(mixed[]):mixed[]
1237
 */
1238
function takeUntil(callable $conditional): Closure
1239
{
1240
    /**
1241
     * @param mixed[] $array
1242
     * @return mixed[]
1243
     */
1244
    return function (array $array) use ($conditional) {
1245
        $carry = array();
1246
        foreach ($array as $key => $value) {
1247
            if (true === $conditional($value)) {
1248
                break;
1249
            }
1250
            $carry[$key] = $value;
1251
        }
1252
        return $carry;
1253
    };
1254
}
1255
1256
/**
1257
 * Creates a function that allows you to take a slice of an array until the passed conditional
1258
 * returns false.
1259
 *
1260
 * @param callable(mixed): bool $conditional
1261
 * @return Closure(mixed[]):mixed[]
1262
 */
1263
function takeWhile(callable $conditional): Closure
1264
{
1265
    /**
1266
     * @param mixed[] $array
1267
     * @return mixed[]
1268
     */
1269
    return function (array $array) use ($conditional) {
1270
        $carry = array();
1271
        foreach ($array as $key => $value) {
1272
            if (false === $conditional($value)) {
1273
                break;
1274
            }
1275
            $carry[$key] = $value;
1276
        }
1277
        return $carry;
1278
    };
1279
}
1280
1281
/**
1282
 * Picks selected indexes from an array
1283
 *
1284
 * @param string ...$indexes
1285
 * @return Closure(mixed[]):mixed[]
1286
 */
1287
function pick(string ...$indexes): Closure
1288
{
1289
    /**
1290
     * @param mixed[] $array
1291
     * @return mixed[]
1292
     */
1293
    return function (array $array) use ($indexes) {
1294
        return array_intersect_key($array, array_flip($indexes));
1295
    };
1296
}
1297