Passed
Push — develop ( 70ec16...430946 )
by Glynn
04:10 queued 01:28
created

last()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
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 pushing a value to the head of an array
39
 *
40
 * @param array<int|string, mixed> $array
41
 * @return Closure(mixed):array<int|string, mixed>
42
 */
43
function pushHead(array $array): Closure
44
{
45
    /**
46
     * @param mixed $value Adds value start of array.
47
     * @return array New array with value on head.
48
     */
49
    return function ($value) use ($array): array {
50
        array_unshift($array, $value);
51
        return $array;
52
    };
53
}
54
55
/**
56
 * Returns a Closure for pushing a value to the head of an array
57
 *
58
 * @param array<int|string, mixed> $array
59
 * @return Closure(mixed):array<int|string, mixed>
60
 */
61
function pushTail(array $array): Closure
62
{
63
    /**
64
     * @param mixed $value Adds value end of array.
65
     * @return array<int|string, mixed> New array with value on tail.
66
     */
67
    return function ($value) use ($array): array {
68
        $array[] = $value;
69
        return $array;
70
    };
71
}
72
73
/**
74
 * Gets the first value from an array.
75
 *
76
 * @param array<int|string, mixed> $array The array.
77
 * @return mixed Will return the first value is array is not empty, else null.
78
 */
79
function head(array $array)
80
{
81
    return !empty($array) ? array_values($array)[0] : null;
82
}
83
84
/**
85
 * Gets the last value from an array.
86
 *
87
 * @param array<int|string, mixed> $array The array.
88
 * @return mixed Will return the last value is array is not empty, else null.
89
 */
90
function last(array $array)
91
{
92
    return !empty($array) ? array_reverse($array, false)[0] : null;
93
}
94
95
/**
96
 * Gets the last value from an array.
97
 *
98
 * @param array<int|string, mixed> $array
99
 * @return mixed Will return the last value is array is not empty, else null.
100
 */
101
function tail(array $array)
102
{
103
    // Return null if empty.
104
    if (empty($array)) {
105
        return null;
106
    }
107
108
    // Remove the first item from the array.
109
    array_shift($array);
110
    return $array;
111
}
112
113
114
/**
115
 * Creates a Closure for concatenating arrays with a defined glue.
116
 *
117
 * @param string|null $glue The string to join each element. If null, will be no separation between elements.
118
 * @return Closure(array<int|string, mixed>):string
119
 *
120
 */
121
function toString(?string $glue = null): Closure
122
{
123
    /**
124
     * @param array<int|string, mixed> $array Array join
125
     * @return string
126
     */
127
    return function (array $array) use ($glue): string {
128
        return $glue ? \join($glue, $array) : \join($array);
129
    };
130
}
131
132
/**
133
 * Creates a Closure for zipping 2 arrays.
134
 *
135
 * @param array<mixed> $additional Values with the same key will be paired.
136
 * @param mixed $default The fallback value if the additional array doesn't have the same length
137
 * @return Closure(array<mixed>):array<array{mixed, mixed}>
138
 *
139
 */
140
function zip(array $additional, $default = null): Closure
141
{
142
    $additional = array_values($additional);
143
    return function (array $array) use ($additional, $default) {
144
        $array = array_values($array);
145
        return array_reduce(
146
            array_keys($array),
147
            function ($carry, $key) use ($array, $additional, $default): array {
148
                $carry[] = array(
149
                    $array[$key],
150
                    array_key_exists($key, $additional) ? $additional[$key] : $default,
151
                );
152
                return $carry;
153
            },
154
            array()
155
        );
156
    };
157
}
158
159
160
/*
161
 *                                ********************
162
 *                                * Filter Compilers *
163
 *                                ********************
164
 */
165
166
167
/**
168
 * Compiles an array if a value is passed.
169
 * Returns the array if nothing passed.
170
 *
171
 * @param mixed[] $inital Sets up the inner value.
172
 * @return Closure
173
 */
174
function arrayCompiler(array $inital = array()): Closure
175
{
176
    /**
177
     * @param mixed $value Adds value to inner array if value set, else returns.
178
     * @return mixed[]|Closure
179
     */
180
    return function ($value = null) use ($inital) {
181
        if ($value) {
182
            $inital[] = $value;
183
        }
184
        return !is_null($value) ? arrayCompiler($inital) : $inital;
185
    };
186
}
187
188
/**
189
 * Creates a typed array compiler.
190
 * All values which do not pass the validator are not added.
191
 *
192
 * @param Closure(mixed):bool $validator Used to validate values before adding to array.
193
 * @param mixed[] $inital The inital data to start with
194
 * @return Closure
195
 */
196
function arrayCompilerTyped(callable $validator, array $inital = array()): Closure
197
{
198
    // Ensure all is validated from initial.
199
    $inital = array_filter($inital, $validator);
200
201
    /**
202
     * @param mixed $value
203
     * @return mixed[]|Closure
204
     */
205
    return function ($value = null) use ($validator, $inital) {
206
        if (!is_null($value) && $validator($value)) {
207
            $inital[] = $value;
208
        }
209
        return !is_null($value) ? arrayCompilerTyped($validator, $inital) : $inital;
210
    };
211
}
212
213
214
215
/*
216
 *                                ********************
217
 *                                * Filter Functions *
218
 *                                ********************
219
 */
220
221
222
/**
223
 * Created a Closure for filtering an array.
224
 *
225
 * @param callable $callable The function to apply to the array.
226
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
227
 */
228
function filter(callable $callable): Closure
229
{
230
    /**
231
     * @param array<int|string, mixed> $source Array to filter
232
     * @return array<int|string, mixed> Filtered array.
233
     */
234
    return function (array $source) use ($callable): array {
235
        return array_filter($source, $callable);
236
    };
237
}
238
239
/**
240
 * Create a Closure for filtering an array by a key.
241
 *
242
 * @param callable $callable The function to apply to the array.
243
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
244
 */
245
function filterKey(callable $callable): Closure
246
{
247
    /**
248
     * @param array<int|string, mixed> $source Array to filter
249
     * @return array<int|string, mixed> Filtered array.
250
     */
251
    return function (array $source) use ($callable): array {
252
        return array_filter($source, $callable, \ARRAY_FILTER_USE_KEY);
253
    };
254
}
255
256
/**
257
 * Creates a Closure for running an array through various callbacks for all true response.
258
 * Wrapper for creating a AND group of callbacks and running through array filter.
259
 *
260
 * @param callable ...$callables
261
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
262
 */
263
function filterAnd(callable ...$callables): Closure
264
{
265
    /**
266
     * @param array<int|string, mixed> $source Array to filter
267
     * @return array<int|string, mixed> Filtered array.
268
     */
269
    return function (array $source) use ($callables): array {
270
        return array_filter($source, Comp\groupAnd(...$callables));
271
    };
272
}
273
274
/**
275
 * Creates a Closure for running an array through various callbacks for any true response.
276
 * Wrapper for creating a OR group of callbacks and running through array filter.
277
 *
278
 * @param callable ...$callables
279
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
280
 */
281
function filterOr(callable ...$callables): Closure
282
{
283
    /**
284
     * @param array<int|string, mixed> $source Array to filter
285
     * @return array<int|string, mixed> Filtered array.
286
     */
287
    return function (array $source) use ($callables): array {
288
        return array_filter($source, Comp\groupOr(...$callables));
289
    };
290
}
291
292
/**
293
 * Creates a Closure for running array filter and getting the first value.
294
 *
295
 * @param callable $func
296
 * @return Closure(array<int|string, mixed>):?mixed
297
 */
298
function filterFirst(callable $func): Closure
299
{
300
    /**
301
     * @param array<int|string, mixed> $array The array to filter
302
     * @return mixed|null The first element from the filtered array or null if filter returns empty
303
     */
304
    return function (array $array) use ($func) {
305
        return head(array_filter($array, $func));
306
    };
307
}
308
309
/**
310
 * Creates a Closure for running array filter and getting the last value.
311
 *
312
 * @param callable $func
313
 * @return Closure(array<int|string, mixed>):?mixed
314
 */
315
function filterLast(callable $func): Closure
316
{
317
    /**
318
     * @param array<int|string, mixed> $array The array to filter
319
     * @return mixed|null The last element from the filtered array.
320
     */
321
    return function (array $array) use ($func) {
322
        return last(array_filter($array, $func));
323
    };
324
}
325
326
/**
327
 * Creates a Closure which takes an array, applies a filter, then maps the
328
 * results of the map.
329
 *
330
 *
331
 * @param callable(mixed):bool $filter Function to of filter contents
332
 * @param callable(mixed):mixed $map Function to map results of filter function.
333
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
334
 */
335
function filterMap(callable $filter, callable $map): Closure
336
{
337
    /**
338
     * @param array<int|string, mixed> $array The array to filter then map.
339
     * @return array<int|string, mixed>
340
     */
341
    return function (array $array) use ($filter, $map): array {
342
        return array_map($map, array_filter($array, $filter));
343
    };
344
}
345
346
/**
347
 * Runs an array through a filters, returns the total count of true
348
 *
349
 * @param callable $function
350
 * @return Closure(array<int|string, mixed>):int
351
 */
352
function filterCount(callable $function): Closure
353
{
354
    /**
355
     * @param array<int|string, mixed> $array
356
     * @return int Count
357
     */
358
    return function (array $array) use ($function) {
359
        return count(array_filter($array, $function));
360
    };
361
}
362
363
/**
364
 * Returns a Closure for partitioning an array based
365
 * on the results of a filter type function.
366
 * Callable will be cast to a bool, if truthy will be listed under 1 key, else 0 for falsey
367
 *
368
 * @param callable(mixed):(bool|int) $function
369
 * @return Closure(mixed[]):array{0:mixed[], 1:mixed[]}
370
 */
371
function partition(callable $function): Closure
372
{
373
    /**
374
     * @param mixed[] $array
375
     * @return array{0:mixed[], 1:mixed[]}
376
     */
377
    return function (array $array) use ($function): array {
378
        return array_reduce(
379
            $array,
380
            /**
381
             * @param array{0:mixed[], 1:mixed[]} $carry
382
             * @param mixed $element
383
             * @return array{0:mixed[], 1:mixed[]}
384
             */
385
            function ($carry, $element) use ($function): array {
386
                $key             = (bool) $function($element) ? 1 : 0;
387
                $carry[$key][] = $element;
388
                return $carry;
389
            },
390
            array(array(), array())
391
        );
392
    };
393
}
394
395
/**
396
 * Returns a closure for checking all elements pass a filter.
397
 *
398
 * @param callable(mixed):bool $function
399
 * @return Closure(mixed[]):bool
400
 */
401
function filterAll(callable $function): Closure
402
{
403
    /**
404
     * @param mixed[] $array
405
     * @return bool
406
     */
407
    return function (array $array) use ($function): bool {
408
        foreach ($array as $value) {
409
            if (false === $function($value)) {
410
                return false;
411
            }
412
        }
413
        return true;
414
    };
415
}
416
417
418
/**
419
 * Returns a closure for checking any elements pass a filter.
420
 *
421
 * @param callable(mixed):bool $function
422
 * @return Closure(mixed[]):bool
423
 */
424
function filterAny(callable $function): Closure
425
{
426
    /**
427
     * @param mixed[] $array
428
     * @return bool
429
     */
430
    return function (array $array) use ($function): bool {
431
        foreach ($array as $value) {
432
            if (true === $function($value)) {
433
                return true;
434
            }
435
        }
436
        return false;
437
    };
438
}
439
440
441
/*
442
 *                           *****************
443
 *                           * Map Functions *
444
 *                           *****************
445
 */
446
447
448
449
/**
450
 * Returns a Closure which can be passed an array.
451
 *
452
 * @param callable(mixed):mixed $func Callback to apply to each element in array.
453
 * @return Closure(mixed[]):mixed[]
454
 */
455
function map(callable $func): Closure
456
{
457
    /**
458
     * @param mixed[] $array The array to map
459
     * @return mixed[]
460
     */
461
    return function (array $array) use ($func): array {
462
        return array_map($func, $array);
463
    };
464
}
465
466
/**
467
 * Returns a Closure for mapping of an arrays keys.
468
 * Setting the key to an existing index will overwrite the current value at same index.
469
 *
470
 * @param callable $func
471
 * @return Closure(mixed[]):mixed[]
472
 */
473
function mapKey(callable $func): Closure
474
{
475
    /**
476
     * @param mixed[] $array The array to map
477
     * @return mixed[]
478
     */
479
    return function (array $array) use ($func): array {
480
        return array_reduce(
481
            array_keys($array),
482
            function ($carry, $key) use ($func, $array) {
483
                $carry[$func($key)] = $array[$key];
484
                return $carry;
485
            },
486
            array()
487
        );
488
    };
489
}
490
491
/**
492
 * Returns a Closure for mapping an array with additional data.
493
 *
494
 * @param callable(mixed ...$a):mixed $func
495
 * @param mixed ...$data
496
 * @return Closure(mixed[]):mixed[]
497
 */
498
function mapWith(callable $func, ...$data): Closure
499
{
500
    /**
501
     * @param mixed[] $array The array to map
502
     * @return mixed[]
503
     */
504
    return function (array $array) use ($func, $data): array {
505
        return array_map(
506
            function ($e) use ($data, $func) {
507
                return $func($e, ...$data);
508
            },
509
            $array
510
        );
511
    };
512
}
513
514
/**
515
 * Returns a Closure for mapping an array with access to value and key.
516
 *
517
 * @param callable(int|string $key, mixed $value):mixed $func
518
 * @return Closure(mixed[]):mixed[]
519
 */
520
function mapWithKey(callable $func): Closure
521
{
522
    /**
523
     * @param mixed[] $array The array to map
524
     * @return mixed[]
525
     */
526
    return function (array $array) use ($func): array {
527
        return array_map(
528
            function ($key, $value) use ($func) {
529
                return $func($value, $key);
530
            },
531
            $array,
532
            array_keys($array)
533
        );
534
    };
535
}
536
537
/**
538
 * Returns a Closure foreaching over an array
539
 *
540
 * @param callable(int|string $key, mixed $value):void $func
541
 * @return Closure(mixed[]):void
542
 */
543
function each(callable $func): Closure
544
{
545
    /**
546
     * @param mixed[] $array The array to map
547
     * @return void
548
     */
549
    return function (array $array) use ($func): void {
550
        array_map(
551
            function ($key, $value) use ($func) {
552
                $func($key, $value);
553
            },
554
            array_keys($array),
555
            $array
556
        );
557
    };
558
}
559
560
/**
561
 * Returns a Closure for flattening and mapping an array
562
 *
563
 * @param callable(mixed):mixed $function The function to map the element. (Will no be called if resolves to array)
564
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
565
 * @return Closure(mixed[]):mixed[]
566
 */
567
function flatMap(callable $function, ?int $n = null): Closure
568
{
569
    /**
570
     * @param mixed[] $array
571
     * @return mixed[]
572
     */
573
    return function (array $array) use ($n, $function): array {
574
        return array_reduce(
575
            $array,
576
            /**
577
             * @param mixed[] $carry
578
             * @param mixed $element
579
             * @return mixed[]
580
             */
581
            function (array $carry, $element) use ($n, $function): array {
582
                if (is_array($element) && (is_null($n) || $n > 0)) {
583
                    $carry = array_merge($carry, flatMap($function, $n ? $n - 1 : null)($element));
584
                } else {
585
                    $carry[] = is_array($element) ? $element : $function($element);
586
                }
587
                return $carry;
588
            },
589
            array()
590
        );
591
    };
592
}
593
594
/*
595
 *                         **********************
596
 *                         * General Operations *
597
 *                         **********************
598
 */
599
600
601
/**
602
 * Creates a Closure for grouping an array.
603
 *
604
 * @param callable(mixed):(string|int) $function The function to group by.
605
 * @return Closure(mixed):mixed[]
606
 */
607
function groupBy(callable $function): Closure
608
{
609
    /**
610
     * @param mixed[] $array The array to be grouped
611
     * @return mixed[] Grouped array.
612
     */
613
    return function (array $array) use ($function): array {
614
        return array_reduce(
615
            $array,
616
            /**
617
             * @param mixed[] $carry
618
             * @param mixed $element
619
             * @return mixed[]
620
             */
621
            function ($carry, $item) use ($function): array {
622
                $carry[call_user_func($function, $item)][] = $item;
623
                return $carry;
624
            },
625
            array()
626
        );
627
    };
628
}
629
630
/**
631
 * Creates a Closure for chunking an array to set a limit.
632
 *
633
 * @param int $count The max size of each chunk. Must not be less than 1!
634
 * @param bool $preserveKeys Should inital keys be kept. Default false.
635
 * @return Closure(mixed[]):mixed[]
636
 */
637
function chunk(int $count, bool $preserveKeys = false): Closure
638
{
639
    /**
640
     * @param mixed[] $array Array to chunk
641
     * @return mixed[]
642
     */
643
    return function (array $array) use ($count, $preserveKeys): array {
644
        return array_chunk($array, max(1, $count), $preserveKeys);
645
    };
646
}
647
648
/**
649
 * Create callback for extracting a single column from an array.
650
 *
651
 * @param string $column Column to retrieve.
652
 * @param string $key Use column for assigning as the index. defaults to numeric keys if null.
653
 * @return Closure(mixed[]):mixed[]
654
 */
655
function column(string $column, ?string $key = null): Closure
656
{
657
    /**
658
     * @param mixed[] $array
659
     * @return mixed[]
660
     */
661
    return function (array $array) use ($column, $key): array {
662
        return array_column($array, $column, $key);
663
    };
664
}
665
666
/**
667
 * Returns a Closure for flattening an array to a defined depth
668
 *
669
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
670
 * @return Closure(mixed[] $var): mixed[]
671
 */
672
function flattenByN(?int $n = null): Closure
673
{
674
    /**
675
     * @param mixed[] $array Array to flatten
676
     * @return mixed[]
677
     */
678
    return function (array $array) use ($n): array {
679
        return array_reduce(
680
            $array,
681
            /**
682
             * @param array<int|string, mixed> $carry
683
             * @param mixed|mixed[] $element
684
             * @return array<int|string, mixed>
685
             */
686
            function (array $carry, $element) use ($n): array {
687
                // Remove empty arrays.
688
                if (is_array($element) && empty($element)) {
689
                    return $carry;
690
                }
691
                // If the element is an array and we are still flattening, call again
692
                if (is_array($element) && (is_null($n) || $n > 0)) { // @phpstan-ignore-line
693
                    $carry = array_merge($carry, flattenByN($n ? $n - 1 : null)($element));
694
                } else {
695
                    // Else just add the element.
696
                    $carry[] = $element;
697
                }
698
                return $carry;
699
            },
700
            array()
701
        );
702
    };
703
}
704
705
/**
706
 * Returns a closure for recursively changing values in an array.
707
 *
708
 * @param mixed[] ...$with The array values to replace with
709
 * @return Closure(mixed[]):mixed[]
710
 */
711
function replaceRecursive(array ...$with): Closure
712
{
713
    /**
714
     * @param mixed[] $array The array to have elements replaced from.
715
     * @return mixed[] Array with replacements.
716
     */
717
    return function (array $array) use ($with): array {
718
        return array_replace_recursive($array, ...$with);
719
    };
720
}
721
722
/**
723
 * Returns a Closure for changing all values in a flat array, based on key.
724
 *
725
 * @param mixed[] ...$with Array with values to replace with, must have matching key with base array.
726
 * @return Closure(mixed[]):mixed[]
727
 */
728
function replace(array ...$with): Closure
729
{
730
    /**
731
     * @param mixed[] $array The array to have elements replaced from.
732
     * @return mixed[] Array with replacements.
733
     */
734
    return function (array $array) use ($with): array {
735
        return array_replace($array, ...$with);
736
    };
737
}
738
739
/**
740
 * Returns a Closure for doing array_sum with the results of a function/expression.
741
 *
742
 * @param callable(mixed):Number $function The function to return the value for array sum
743
 * @return Closure(mixed[]):Number
744
 */
745
function sumWhere(callable $function): Closure
746
{
747
    /**
748
     * @param mixed[] $array Array to do sum() on.
749
     * @return Number The total.
750
     */
751
    return function (array $array) use ($function) {
752
        return array_sum(array_map($function, $array));
753
    };
754
}
755
756
/**
757
 * Creates a closure for casting an array to an object.
758
 * Assumed all properties are public
759
 * None existing properties will be set as dynamic properties.
760
 *
761
 * @param object|null $object The object to cast to, defaults to stdClass
762
 * @return Closure(mixed[]):object
763
 * @throws \InvalidArgumentException If property does not exist or is not public.
764
 */
765
function toObject($object = null): Closure
766
{
767
    $object = $object ?? new stdClass();
768
769
    // Throws an exception if $object is not an object.
770
    if (!is_object($object)) {
771
        throw new \InvalidArgumentException('Object must be an object.');
772
    }
773
774
    /**
775
     * @param mixed[] $array
776
     * @return object
777
     */
778
    return function (array $array) use ($object) {
779
        foreach ($array as $key => $value) {
780
            // If key is not a string or numerical, skip it.
781
            if (!is_string($key) || is_numeric($key)) {
782
                continue;
783
            }
784
785
            try {
786
                $object->{$key} = $value;
787
            } 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...
788
                throw new \InvalidArgumentException("Property {$key} does not exist or is not public.");
789
            }
790
        }
791
        return $object;
792
    };
793
}
794
795
/**
796
 * Creates a closure for encoding json with defined flags/depth
797
 *
798
 * @param int $flags json_encode flags (default = 0)
799
 * @param int $depth Nodes deep to encode (default = 512)
800
 * @return \Closure(mixed):?string
801
 * @constants JSON_FORCE_OBJECT, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP,
802
 *            JSON_HEX_APOS, JSON_INVALID_UTF8_IGNORE,
803
 *            JSON_INVALID_UTF8_SUBSTITUTE, JSON_NUMERIC_CHECK,
804
 *            JSON_PARTIAL_OUTPUT_ON_ERROR, JSON_PRESERVE_ZERO_FRACTION,
805
 *            JSON_PRETTY_PRINT, JSON_UNESCAPED_LINE_TERMINATORS,
806
 *            JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, JSON_THROW_ON_ERROR
807
 */
808
function toJson(int $flags = 0, int $depth = 512): Closure
809
{
810
    /**
811
     * @param mixed $data
812
     * @return string|null
813
     */
814
    return function ($data) use ($flags, $depth): ?string {
815
        return \json_encode($data, $flags, max(1, $depth)) ?: null;
816
    };
817
}
818
819
820
/*
821
 *                         ****************
822
 *                         *  Array Sort  *
823
 *                         ****************
824
 */
825
826
827
/**
828
 * Returns a Closure for doing regular SORT against an array.
829
 * Doesn't maintain keys.
830
 *
831
 * @param int $flag Uses php stock sort constants or numerical values.
832
 * @return Closure(mixed[]):mixed[]
833
 */
834
function sort(int $flag = SORT_REGULAR): Closure
835
{
836
    /**
837
     *  @param mixed[]$array The array to sort
838
     *  @return mixed[] The sorted array (new array)
839
     */
840
    return function (array $array) use ($flag) {
841
        \sort($array, $flag);
842
        return $array;
843
    };
844
}
845
846
/**
847
 * Returns a Closure for doing regular Reverse SORT against an array.
848
 * Doesn't maintain keys.
849
 *
850
 * @param int $flag Uses php stock sort constants or numerical values.
851
 * @return Closure(mixed[]):mixed[]
852
 */
853
function rsort(int $flag = SORT_REGULAR): Closure
854
{
855
    /**
856
     *  @param mixed[]$array The array to sort
857
     *  @return mixed[] The sorted array (new array)
858
     */
859
    return function (array $array) use ($flag) {
860
        \rsort($array, $flag);
861
        return $array;
862
    };
863
}
864
865
866
/**
867
 * Returns a Closure for sorting an array by key in ascending order.
868
 *
869
 * @param int $flag Uses php stock sort constants or numerical values.
870
 * @return Closure(mixed[]):mixed[]
871
 */
872
function ksort(int $flag = SORT_REGULAR): Closure
873
{
874
    /**
875
     *  @param mixed[]$array The array to sort
876
     *  @return mixed[] The sorted array (new array)
877
     */
878
    return function (array $array) use ($flag) {
879
        \ksort($array, $flag);
880
        return $array;
881
    };
882
}
883
884
/**
885
 * Returns a Closure for sorting an array by key in descending (reverse) order.
886
 *
887
 * @param int $flag Uses php stock sort constants or numerical values.
888
 * @return Closure(mixed[]):mixed[]
889
 */
890
function krsort(int $flag = SORT_REGULAR): Closure
891
{
892
    /**
893
     *  @param mixed[]$array The array to sort
894
     *  @return mixed[] The sorted array (new array)
895
     */
896
    return function (array $array) use ($flag) {
897
        \krsort($array, $flag);
898
        return $array;
899
    };
900
}
901
902
/**
903
 * Returns a Closure for sorting an array by value in ascending order.
904
 * Maintain keys.
905
 *
906
 * @param int $flag Uses php stock sort constants or numerical values.
907
 * @return Closure(mixed[]):mixed[]
908
 */
909
function asort(int $flag = SORT_REGULAR): Closure
910
{
911
    /**
912
     *  @param mixed[]$array The array to sort
913
     *  @return mixed[] The sorted array (new array)
914
     */
915
    return function (array $array) use ($flag) {
916
        \asort($array, $flag);
917
        return $array;
918
    };
919
}
920
921
/**
922
 * Returns a Closure for sorting an array by value in descending (reverse) order.
923
 * Maintain keys.
924
 *
925
 * @param int $flag Uses php stock sort constants or numerical values.
926
 * @return Closure(mixed[]):mixed[]
927
 */
928
function arsort(int $flag = SORT_REGULAR): Closure
929
{
930
    /**
931
     *  @param mixed[]$array The array to sort
932
     *  @return mixed[] The sorted array (new array)
933
     */
934
    return function (array $array) use ($flag) {
935
        \arsort($array, $flag);
936
        return $array;
937
    };
938
}
939
940
/**
941
 * Returns a Closure for sorting an array using a "natural order" algorithm
942
 *
943
 * @return Closure(mixed[]):mixed[]
944
 */
945
function natsort(): Closure
946
{
947
    /**
948
     *  @param mixed[]$array The array to sort
949
     *  @return mixed[] The sorted array (new array)
950
     */
951
    return function (array $array) {
952
        \natsort($array);
953
        return $array;
954
    };
955
}
956
957
/**
958
 * Returns a Closure for sorting an array using a case insensitive "natural order" algorithm
959
 *
960
 * @return Closure(mixed[]):mixed[]
961
 */
962
function natcasesort(): Closure
963
{
964
    /**
965
     *  @param mixed[]$array The array to sort
966
     *  @return mixed[] The sorted array (new array)
967
     */
968
    return function (array $array) {
969
        \natcasesort($array);
970
        return $array;
971
    };
972
}
973
974
/**
975
 * Returns a Closure for sorting an array by key using a custom comparison function
976
 *
977
 * @param callable(mixed $a, mixed $b): int $function
978
 * @return Closure(mixed[]):mixed[]
979
 */
980
function uksort(callable $function): Closure
981
{
982
    /**
983
     *  @param mixed[] $array The array to sort
984
     *  @return mixed[] The sorted array (new array)
985
     */
986
    return function (array $array) use ($function) {
987
        \uksort($array, $function);
988
        return $array;
989
    };
990
}
991
992
/**
993
 * Returns a Closure for sorting an array using a custom comparison function
994
 * Maintain keys.
995
 *
996
 * @param callable(mixed $a, mixed $b): int $function
997
 * @return Closure(mixed[]):mixed[]
998
 */
999
function uasort(callable $function): Closure
1000
{
1001
    /**
1002
     *  @param mixed[]$array The array to sort
1003
     *  @return mixed[] The sorted array (new array)
1004
     */
1005
    return function (array $array) use ($function) {
1006
        \uasort($array, $function);
1007
        return $array;
1008
    };
1009
}
1010
1011
1012
/**
1013
 * Returns a Closure for sorting an array using a custom comparison function
1014
 * Doesn't maintain keys.
1015
 *
1016
 * @param callable(mixed $a, mixed $b): int $function
1017
 * @return Closure(mixed[]):mixed[]
1018
 */
1019
function usort(callable $function): Closure
1020
{
1021
    /**
1022
     *  @param mixed[]$array The array to sort
1023
     *  @return mixed[] The sorted array (new array)
1024
     */
1025
    return function (array $array) use ($function) {
1026
        \usort($array, $function);
1027
        return $array;
1028
    };
1029
}
1030
1031
1032
/**
1033
 * Returns a Closure for applying a function to every element of an array
1034
 *
1035
 * @param callable(mixed $carry, mixed $value):mixed $function
1036
 * @param mixed $initialValue
1037
 * @return Closure(mixed[]):mixed[]
1038
 */
1039
function scan(callable $function, $initialValue): Closure
1040
{
1041
    return function (array $array) use ($function, $initialValue) {
1042
        $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...
1043
        foreach ($array as $key => $value) {
1044
            $initialValue = $function($initialValue, $value);
1045
            $carry[]      = $initialValue;
1046
        }
1047
        return $carry;
1048
    };
1049
}
1050
1051
/**
1052
 * Returns a Closure for applying a function to every element of an array
1053
 *
1054
 * @param callable(mixed $carry, mixed $value):mixed $function
1055
 * @param mixed $initialValue
1056
 * @return Closure(mixed[]):mixed[]
1057
 */
1058
function scanR(callable $function, $initialValue): Closure
1059
{
1060
    return function (array $array) use ($function, $initialValue) {
1061
        $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...
1062
        foreach (array_reverse($array) as $key => $value) {
1063
            $initialValue = $function($initialValue, $value);
1064
            $carry[]      = $initialValue;
1065
        }
1066
        return \array_reverse($carry);
1067
    };
1068
}
1069
1070
/**
1071
 * Creates a function for defining the callback and initial for reduce/fold
1072
 *
1073
 * @param callable(mixed $carry, mixed $value): mixed $callable
1074
 * @param mixed $initial
1075
 * @return Closure(mixed[]):mixed
1076
 */
1077
function fold(callable $callable, $initial = array()): Closure
1078
{
1079
    /**
1080
     * @param mixed[] $array
1081
     * @return mixed
1082
     */
1083
    return function (array $array) use ($callable, $initial) {
1084
        return array_reduce($array, $callable, $initial);
1085
    };
1086
}
1087
1088
/**
1089
 * Creates a function for defining the callback and initial for reduce/fold
1090
 *
1091
 * @param callable(mixed $carry, mixed $value): mixed $callable
1092
 * @param mixed $initial
1093
 * @return Closure(mixed[]):mixed
1094
 */
1095
function foldR(callable $callable, $initial = array()): Closure
1096
{
1097
    /**
1098
     * @param mixed[] $array
1099
     * @return mixed
1100
     */
1101
    return function (array $array) use ($callable, $initial) {
1102
        return array_reduce(\array_reverse($array), $callable, $initial);
1103
    };
1104
}
1105
1106
/**
1107
 * Creates a function for defining the callback and initial for reduce/fold, with the key
1108
 * also passed to the callback.
1109
 *
1110
 * @param callable(mixed $carry, int|string $key, mixed $value): mixed $callable
1111
 * @param mixed $initial
1112
 * @return Closure(mixed[]):mixed
1113
 */
1114
function foldKeys(callable $callable, $initial = array()): Closure
1115
{
1116
    /**
1117
     * @param mixed[] $array
1118
     * @return mixed
1119
     */
1120
    return function (array $array) use ($callable, $initial) {
1121
        foreach ($array as $key => $value) {
1122
            $initial = $callable($initial, $key, $value);
1123
        }
1124
        return $initial;
1125
    };
1126
}
1127
1128
/**
1129
 * Creates a function which takes the first n elements from an array
1130
 *
1131
 * @param int $count
1132
 * @return Closure(mixed[]):mixed[]
1133
 * @throws \InvalidArgumentException if count is negative
1134
 */
1135
function take(int $count = 1): Closure
1136
{
1137
    // throw InvalidArgumentException if count is negative
1138
    if ($count < 0) {
1139
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1140
    }
1141
1142
    /**
1143
     * @param mixed[] $array
1144
     * @return mixed[]
1145
     */
1146
    return function (array $array) use ($count) {
1147
        return \array_slice($array, 0, $count);
1148
    };
1149
}
1150
1151
/**
1152
 * Creates a function which takes the last n elements from an array
1153
 *
1154
 * @param int $count
1155
 * @return Closure(mixed[]):mixed[]
1156
 * @throws \InvalidArgumentException if count is negative
1157
 */
1158
function takeLast(int $count = 1): Closure
1159
{
1160
    // throw InvalidArgumentException if count is negative
1161
    if ($count < 0) {
1162
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1163
    }
1164
1165
    // If count is 0, return an empty array
1166
    if ($count === 0) {
1167
        return function (array $array) {
1168
            return array();
1169
        };
1170
    }
1171
1172
    /**
1173
     * @param mixed[] $array
1174
     * @return mixed[]
1175
     */
1176
    return function (array $array) use ($count) {
1177
        return \array_slice($array, -$count);
1178
    };
1179
}
1180
1181
/**
1182
 * Creates a function that allows you to take a slice of an array until the passed conditional
1183
 * returns true.
1184
 *
1185
 * @param callable(mixed): bool $conditional
1186
 * @return Closure(mixed[]):mixed[]
1187
 */
1188
function takeUntil(callable $conditional): Closure
1189
{
1190
    /**
1191
     * @param mixed[] $array
1192
     * @return mixed[]
1193
     */
1194
    return function (array $array) use ($conditional) {
1195
        $carry = array();
1196
        foreach ($array as $key => $value) {
1197
            if (true === $conditional($value)) {
1198
                break;
1199
            }
1200
            $carry[$key] = $value;
1201
        }
1202
        return $carry;
1203
    };
1204
}
1205
1206
/**
1207
 * Creates a function that allows you to take a slice of an array until the passed conditional
1208
 * returns false.
1209
 *
1210
 * @param callable(mixed): bool $conditional
1211
 * @return Closure(mixed[]):mixed[]
1212
 */
1213
function takeWhile(callable $conditional): Closure
1214
{
1215
    /**
1216
     * @param mixed[] $array
1217
     * @return mixed[]
1218
     */
1219
    return function (array $array) use ($conditional) {
1220
        $carry = array();
1221
        foreach ($array as $key => $value) {
1222
            if (false === $conditional($value)) {
1223
                break;
1224
            }
1225
            $carry[$key] = $value;
1226
        }
1227
        return $carry;
1228
    };
1229
}
1230