Passed
Pull Request — develop (#55)
by Glynn
02:35
created

each()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 13
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;
1 ignored issue
show
Bug introduced by
The type PinkCrab\FunctionConstructors\Comparisons was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
88
 * @return mixed Will return the last value is array is not empty, else null.
89
 */
90
function tail(array $array)
91
{
92
    return ! empty($array) ? array_reverse($array, false)[0] : null;
93
}
94
95
96
/**
97
 * Creates a Closure for concatenating arrays with a defined glue.
98
 *
99
 * @param string|null $glue The string to join each element. If null, will be no separation between elements.
100
 * @return Closure(array<int|string, mixed>):string
101
 *
102
 */
103
function toString(?string $glue = null): Closure
104
{
105
    /**
106
     * @param array<int|string, mixed> $array Array join
107
     * @return string.
108
     */
109
    return function (array $array) use ($glue): string {
110
        return $glue ? \join($glue, $array) : \join($array);
1 ignored issue
show
Bug introduced by
The call to join() has too few arguments starting with array. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
        return $glue ? \join($glue, $array) : /** @scrutinizer ignore-call */ \join($array);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
111
    };
112
}
113
114
/**
115
 * Creates a Closure for zipping 2 arrays.
116
 *
117
 * @param array<mixed> $additional Values with the same key will be paired.
118
 * @param mixed $default The fallback value if the additional array doesn't have the same length
119
 * @return Closure(array<mixed>):array<array{mixed, mixed}>
120
 *
121
 */
122
function zip(array $additional, $default = null): Closure
123
{
124
    $additional = array_values($additional);
125
    return function (array $array) use ($additional, $default) {
126
        $array = array_values($array);
127
        return array_reduce(
128
            array_keys($array),
129
            function ($carry, $key) use ($array, $additional, $default): array {
130
                $carry[] = array(
131
                    $array[ $key ],
132
                    array_key_exists($key, $additional) ? $additional[ $key ] : $default,
133
                );
134
                return $carry;
135
            },
136
            array()
137
        );
138
    };
139
}
140
141
142
/*
143
 *                                ********************
144
 *                                * Filter Compilers *
145
 *                                ********************
146
 */
147
148
149
/**
150
 * Compiles an array if a value is passed.
151
 * Returns the array if nothing passed.
152
 *
153
 * @param mixed[] $inital Sets up the inner value.
154
 * @return Closure
155
 */
156
function arrayCompiler(array $inital = array()): Closure
157
{
158
    /**
159
     * @param mixed $value Adds value to inner array if value set, else returns.
160
     * @return mixed[]|Closure
161
     */
162
    return function ($value = null) use ($inital) {
163
        if ($value) {
164
            $inital[] = $value;
165
        }
166
        return ! is_null($value) ? arrayCompiler($inital) : $inital;
167
    };
168
}
169
170
/**
171
 * Creates a typed array compiler.
172
 * All values which do not pass the validator are not added.
173
 *
174
 * @param Closure(mixed):bool $validator Used to validate values before adding to array.
175
 * @param mixed[] $inital The inital data to start with
176
 * @return Closure
177
 */
178
function arrayCompilerTyped(callable $validator, array $inital = array()): Closure
179
{
180
    // Ensure all is validated from initial.
181
    $inital = array_filter($inital, $validator);
182
183
    /**
184
     * @param mixed $value
185
     * @return mixed[]|Closure
186
     */
187
    return function ($value = null) use ($validator, $inital) {
188
        if (! is_null($value) && $validator($value)) {
189
            $inital[] = $value;
190
        }
191
        return ! is_null($value) ? arrayCompilerTyped($validator, $inital) : $inital;
192
    };
193
}
194
195
196
197
/*
198
 *                                ********************
199
 *                                * Filter Functions *
200
 *                                ********************
201
 */
202
203
204
/**
205
 * Created a Closure for filtering an array.
206
 *
207
 * @param callable $callable The function to apply to the array.
208
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
209
 */
210
function filter(callable $callable): Closure
211
{
212
    /**
213
     * @param array<int|string, mixed> $source Array to filter
214
     * @return array<int|string, mixed> Filtered array.
215
     */
216
    return function (array $source) use ($callable): array {
217
        return array_filter($source, $callable);
218
    };
219
}
220
221
/**
222
 * Create a Closure for filtering an array by a key.
223
 *
224
 * @param callable $callable The function to apply to the array.
225
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
226
 */
227
function filterKey(callable $callable): Closure
228
{
229
    /**
230
     * @param array<int|string, mixed> $source Array to filter
231
     * @return array<int|string, mixed> Filtered array.
232
     */
233
    return function (array $source) use ($callable): array {
234
        return array_filter($source, $callable, \ARRAY_FILTER_USE_KEY);
235
    };
236
}
237
238
/**
239
 * Creates a Closure for running an array through various callbacks for all true response.
240
 * Wrapper for creating a AND group of callbacks and running through array filter.
241
 *
242
 * @param callable ...$callables
243
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
244
 */
245
function filterAnd(callable ...$callables): 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 ($callables): array {
252
        return array_filter($source, Comp\groupAnd(...$callables));
1 ignored issue
show
Bug introduced by
The function groupAnd was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

252
        return array_filter($source, /** @scrutinizer ignore-call */ Comp\groupAnd(...$callables));
Loading history...
253
    };
254
}
255
256
/**
257
 * Creates a Closure for running an array through various callbacks for any true response.
258
 * Wrapper for creating a OR 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 filterOr(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\groupOr(...$callables));
1 ignored issue
show
Bug introduced by
The function groupOr was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

270
        return array_filter($source, /** @scrutinizer ignore-call */ Comp\groupOr(...$callables));
Loading history...
271
    };
272
}
273
274
/**
275
 * Creates a Closure for running array filter and getting the first value.
276
 *
277
 * @param callable $func
278
 * @return Closure(array<int|string, mixed>):?mixed
279
 */
280
function filterFirst(callable $func): Closure
281
{
282
    /**
283
     * @param array<int|string, mixed> $array The array to filter
284
     * @return mixed|null The first element from the filtered array or null if filter returns empty
285
     */
286
    return function (array $array) use ($func) {
287
        return head(array_filter($array, $func));
288
    };
289
}
290
291
/**
292
 * Creates a Closure for running array filter and getting the last value.
293
 *
294
 * @param callable $func
295
 * @return Closure(array<int|string, mixed>):?mixed
296
 */
297
function filterLast(callable $func): Closure
298
{
299
    /**
300
     * @param array<int|string, mixed> $array The array to filter
301
     * @return mixed|null The last element from the filtered array.
302
     */
303
    return function (array $array) use ($func) {
304
        return tail(array_filter($array, $func));
305
    };
306
}
307
308
/**
309
 * Creates a Closure which takes an array, applies a filter, then maps the
310
 * results of the map.
311
 *
312
 *
313
 * @param callable(mixed):bool $filter Function to of filter contents
314
 * @param callable(mixed):mixed $map Function to map results of filter function.
315
 * @return Closure(array<int|string, mixed>):array<int|string, mixed>
316
 */
317
function filterMap(callable $filter, callable $map): Closure
318
{
319
    /**
320
     * @param array<int|string, mixed> $array The array to filter then map.
321
     * @return array<int|string, mixed>
322
     */
323
    return function (array $array) use ($filter, $map): array {
324
        return array_map($map, array_filter($array, $filter));
325
    };
326
}
327
328
/**
329
 * Runs an array through a filters, returns the total count of true
330
 *
331
 * @param callable $function
332
 * @return Closure(array<int|string, mixed>):int
333
 */
334
function filterCount(callable $function): Closure
335
{
336
    /**
337
     * @param array<int|string, mixed> $array
338
     * @return int Count
339
     */
340
    return function (array $array) use ($function) {
341
        return count(array_filter($array, $function));
342
    };
343
}
344
345
/**
346
 * Returns a Closure for partitioning an array based
347
 * on the results of a filter type function.
348
 * Callable will be cast to a bool, if truthy will be listed under 1 key, else 0 for falsey
349
 *
350
 * @param callable(mixed):(bool|int) $function
351
 * @return Closure(mixed[]):array{0:mixed[], 1:mixed[]}
352
 */
353
function partition(callable $function): Closure
354
{
355
    /**
356
     * @param mixed[] $array
357
     * @return array{0:mixed[], 1:mixed[]}
358
     */
359
    return function (array $array) use ($function): array {
360
        return array_reduce(
361
            $array,
362
            /**
363
             * @param array{0:mixed[], 1:mixed[]} $carry
364
             * @param mixed $element
365
             * @return array{0:mixed[], 1:mixed[]}
366
             */
367
            function ($carry, $element) use ($function): array {
368
                $key             = (bool) $function($element) ? 1 : 0;
369
                $carry[ $key ][] = $element;
370
                return $carry;
371
            },
372
            array( array(), array() )
373
        );
374
    };
375
}
376
377
/**
378
 * Returns a closure for checking all elements pass a filter.
379
 *
380
 * @param callable(mixed):bool $function
381
 * @return Closure(mixed[]):bool
382
 */
383
function filterAll(callable $function): Closure
384
{
385
    /**
386
     * @param mixed[] $array
387
     * @return bool
388
     */
389
    return function (array $array) use ($function): bool {
390
        foreach ($array as $value) {
391
            if (false === $function($value)) {
392
                return false;
393
            }
394
        }
395
        return true;
396
    };
397
}
398
399
400
/**
401
 * Returns a closure for checking any elements pass a filter.
402
 *
403
 * @param callable(mixed):bool $function
404
 * @return Closure(mixed[]):bool
405
 */
406
function filterAny(callable $function): Closure
407
{
408
    /**
409
     * @param mixed[] $array
410
     * @return bool
411
     */
412
    return function (array $array) use ($function): bool {
413
        foreach ($array as $value) {
414
            if (true === $function($value)) {
415
                return true;
416
            }
417
        }
418
        return false;
419
    };
420
}
421
422
423
/*
424
 *                           *****************
425
 *                           * Map Functions *
426
 *                           *****************
427
 */
428
429
430
431
/**
432
 * Returns a Closure which can be passed an array.
433
 *
434
 * @param callable(mixed):mixed $func Callback to apply to each element in array.
435
 * @return Closure(mixed[]):mixed[]
436
 */
437
function map(callable $func): Closure
438
{
439
    /**
440
     * @param mixed[] $array The array to map
441
     * @return mixed[]
442
     */
443
    return function (array $array) use ($func): array {
444
        return array_map($func, $array);
445
    };
446
}
447
448
/**
449
 * Returns a Closure for mapping of an arrays keys.
450
 * Setting the key to an existing index will overwrite the current value at same index.
451
 *
452
 * @param callable $func
453
 * @return Closure(mixed[]):mixed[]
454
 */
455
function mapKey(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_reduce(
463
            array_keys($array),
464
            function ($carry, $key) use ($func, $array) {
465
                $carry[ $func($key) ] = $array[ $key ];
466
                return $carry;
467
            },
468
            array()
469
        );
470
    };
471
}
472
473
/**
474
 * Returns a Closure for mapping an array with additional data.
475
 *
476
 * @param callable(mixed ...$a):mixed $func
477
 * @param mixed ...$data
478
 * @return Closure(mixed[]):mixed[]
479
 */
480
function mapWith(callable $func, ...$data): Closure
481
{
482
    /**
483
     * @param mixed[] $array The array to map
484
     * @return mixed[]
485
     */
486
    return function (array $array) use ($func, $data): array {
487
        return array_map(
488
            function ($e) use ($data, $func) {
489
                return $func($e, ...$data);
490
            },
491
            $array
492
        );
493
    };
494
}
495
496
/**
497
 * Returns a Closure for mapping an array with access to value and key.
498
 *
499
 * @param callable(int|string $key, mixed $value):mixed $func
500
 * @return Closure(mixed[]):mixed[]
501
 */
502
function mapWithKey(callable $func): Closure
503
{
504
    /**
505
     * @param mixed[] $array The array to map
506
     * @return mixed[]
507
     */
508
    return function (array $array) use ($func): array {
509
        return array_map(
510
            function ($key, $value) use ($func) {
511
                return $func($value, $key);
512
            },
513
            $array,
514
            array_keys($array)
515
        );
516
    };
517
}
518
519
/**
520
 * Returns a Closure foreaching over an array
521
 *
522
 * @param callable(int|string $key, mixed $value):void $func
523
 * @return Closure(mixed[]):void
524
 */
525
function each(callable $func): Closure
526
{
527
    /**
528
     * @param mixed[] $array The array to map
529
     * @return void
530
     */
531
    return function (array $array) use ($func): void {
532
        array_map(
533
            function ($key, $value) use ($func) {
534
                $func($key, $value);
535
            },
536
            array_keys($array),
537
            $array
538
        );
539
    };
540
}
541
542
/**
543
 * Returns a Closure for flattening and mapping an array
544
 *
545
 * @param callable(mixed):mixed $function The function to map the element. (Will no be called if resolves to array)
546
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
547
 * @return Closure(mixed[]):mixed[]
548
 */
549
function flatMap(callable $function, ?int $n = null): Closure
550
{
551
    /**
552
     * @param mixed[] $array
553
     * @return mixed[]
554
     */
555
    return function (array $array) use ($n, $function): array {
556
        return array_reduce(
557
            $array,
558
            /**
559
             * @param mixed[] $carry
560
             * @param mixed $element
561
             * @return mixed[]
562
             */
563
            function (array $carry, $element) use ($n, $function): array {
564
                if (is_array($element) && (is_null($n) || $n > 0)) {
565
                    $carry = array_merge($carry, flatMap($function, $n ? $n - 1 : null)($element));
566
                } else {
567
                    $carry[] = is_array($element) ? $element : $function($element);
568
                }
569
                return $carry;
570
            },
571
            array()
572
        );
573
    };
574
}
575
576
/*
577
 *                         **********************
578
 *                         * General Operations *
579
 *                         **********************
580
 */
581
582
583
/**
584
 * Creates a Closure for grouping an array.
585
 *
586
 * @param callable(mixed):(string|int) $function The function to group by.
587
 * @return Closure(mixed):mixed[]
588
 */
589
function groupBy(callable $function): Closure
590
{
591
    /**
592
     * @param mixed[] $array The array to be grouped
593
     * @return mixed[] Grouped array.
594
     */
595
    return function (array $array) use ($function): array {
596
        return array_reduce(
597
            $array,
598
            /**
599
             * @param mixed[] $carry
600
             * @param mixed $element
601
             * @return mixed[]
602
             */
603
            function ($carry, $item) use ($function): array {
604
                $carry[ call_user_func($function, $item) ][] = $item;
605
                return $carry;
606
            },
607
            array()
608
        );
609
    };
610
}
611
612
/**
613
 * Creates a Closure for chunking an array to set a limit.
614
 *
615
 * @param int $count The max size of each chunk. Must not be less than 1!
616
 * @param bool $preserveKeys Should inital keys be kept. Default false.
617
 * @return Closure(mixed[]):mixed[]
618
 */
619
function chunk(int $count, bool $preserveKeys = false): Closure
620
{
621
    /**
622
     * @param mixed[] $array Array to chunk
623
     * @return mixed[]
624
     */
625
    return function (array $array) use ($count, $preserveKeys): array {
626
        return array_chunk($array, max(1, $count), $preserveKeys);
627
    };
628
}
629
630
/**
631
 * Create callback for extracting a single column from an array.
632
 *
633
 * @param string $column Column to retrieve.
634
 * @param string $key Use column for assigning as the index. defaults to numeric keys if null.
635
 * @return Closure(mixed[]):mixed[]
636
 */
637
function column(string $column, ?string $key = null): Closure
638
{
639
    /**
640
     * @param mixed[] $array
641
     * @return mixed[]
642
     */
643
    return function (array $array) use ($column, $key): array {
644
        return array_column($array, $column, $key);
645
    };
646
}
647
648
/**
649
 * Returns a Closure for flattening an array to a defined depth
650
 *
651
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
652
 * @return Closure(mixed[] $var): mixed[]
653
 */
654
function flattenByN(?int $n = null): Closure
655
{
656
    /**
657
     * @param mixed[] $array Array to flatten
658
     * @return mixed[]
659
     */
660
    return function (array $array) use ($n): array {
661
        return array_reduce(
662
            $array,
663
            /**
664
             * @param array<int|string, mixed> $carry
665
             * @param mixed|mixed[] $element
666
             * @return array<int|string, mixed>
667
             */
668
            function (array $carry, $element) use ($n): array {
669
                // Remove empty arrays.
670
                if (is_array($element) && empty($element)) {
671
                    return $carry;
672
                }
673
                // If the element is an array and we are still flattening, call again
674
                if (is_array($element) && (is_null($n) || $n > 0)) { // @phpstan-ignore-line
675
                    $carry = array_merge($carry, flattenByN($n ? $n - 1 : null)($element));
676
                } else {
677
                    // Else just add the element.
678
                    $carry[] = $element;
679
                }
680
                return $carry;
681
            },
682
            array()
683
        );
684
    };
685
}
686
687
/**
688
 * Returns a closure for recursively changing values in an array.
689
 *
690
 * @param mixed[] ...$with The array values to replace with
691
 * @return Closure(mixed[]):mixed[]
692
 */
693
function replaceRecursive(array ...$with): Closure
694
{
695
    /**
696
     * @param mixed[] $array The array to have elements replaced from.
697
     * @return mixed[] Array with replacements.
698
     */
699
    return function (array $array) use ($with): array {
700
        return array_replace_recursive($array, ...$with);
701
    };
702
}
703
704
/**
705
 * Returns a Closure for changing all values in a flat array, based on key.
706
 *
707
 * @param mixed[] ...$with Array with values to replace with, must have matching key with base array.
708
 * @return Closure(mixed[]):mixed[]
709
 */
710
function replace(array ...$with): Closure
711
{
712
    /**
713
     * @param mixed[] $array The array to have elements replaced from.
714
     * @return mixed[] Array with replacements.
715
     */
716
    return function (array $array) use ($with): array {
717
        return array_replace($array, ...$with);
718
    };
719
}
720
721
/**
722
 * Returns a Closure for doing array_sum with the results of a function/expression.
723
 *
724
 * @param callable(mixed):Number $function The function to return the value for array sum
725
 * @return Closure(mixed[]):Number
726
 */
727
function sumWhere(callable $function): Closure
728
{
729
    /**
730
     * @param mixed[] $array Array to do sum() on.
731
     * @return Number The total.
732
     */
733
    return function (array $array) use ($function) {
734
        return array_sum(array_map($function, $array));
735
    };
736
}
737
738
/**
739
 * Creates a closure for casting an array to an object.
740
 * Assumed all properties are public
741
 * None existing properties will be set as dynamic properties.
742
 *
743
 * @param object|null $object The object to cast to, defaults to stdClass
744
 * @return Closure(mixed[]):object
745
 */
746
function toObject(?object $object = null): Closure
747
{
748
    $object = $object ?? new stdClass();
749
750
    /**
751
     * @param mixed[] $array
752
     * @return object
753
     */
754
    return function (array $array) use ($object): object {
755
        foreach ($array as $key => $value) {
756
            $key            = is_string($key) ? $key : (string) $key;
757
            $object->{$key} = $value;
758
        }
759
        return $object;
760
    };
761
}
762
763
/**
764
 * Creates a closure for encoding json with defined flags/depth
765
 *
766
 * @param int $flags json_encode flags (default = 0)
767
 * @param int $depth Nodes deep to encode (default = 512)
768
 * @return \Closure(mixed):?string
769
 * @constants JSON_FORCE_OBJECT, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP,
770
 *            JSON_HEX_APOS, JSON_INVALID_UTF8_IGNORE,
771
 *            JSON_INVALID_UTF8_SUBSTITUTE, JSON_NUMERIC_CHECK,
772
 *            JSON_PARTIAL_OUTPUT_ON_ERROR, JSON_PRESERVE_ZERO_FRACTION,
773
 *            JSON_PRETTY_PRINT, JSON_UNESCAPED_LINE_TERMINATORS,
774
 *            JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, JSON_THROW_ON_ERROR
775
 */
776
function toJson(int $flags = 0, int $depth = 512): Closure
777
{
778
    /**
779
     * @param mixed $data
780
     * @return string|null
781
     */
782
    return function ($data) use ($flags, $depth): ?string {
783
        return \json_encode($data, $flags, max(1, $depth)) ?: null;
784
    };
785
}
786
787
788
/*
789
 *                         ****************
790
 *                         *  Array Sort  *
791
 *                         ****************
792
 */
793
794
795
/**
796
 * Returns a Closure for doing regular SORT against an array.
797
 * Doesn't maintain keys.
798
 *
799
 * @param int $flag Uses php stock sort constants or numerical values.
800
 * @return Closure(mixed[]):mixed[]
801
 */
802
function sort(int $flag = SORT_REGULAR): Closure
803
{
804
    /**
805
     *  @param mixed[]$array The array to sort
806
     *  @return mixed[] The sorted array (new array)
807
     */
808
    return function (array $array) use ($flag) {
809
        \sort($array, $flag);
810
        return $array;
811
    };
812
}
813
814
/**
815
 * Returns a Closure for doing regular Reverse SORT against an array.
816
 * Doesn't maintain keys.
817
 *
818
 * @param int $flag Uses php stock sort constants or numerical values.
819
 * @return Closure(mixed[]):mixed[]
820
 */
821
function rsort(int $flag = SORT_REGULAR): Closure
822
{
823
    /**
824
     *  @param mixed[]$array The array to sort
825
     *  @return mixed[] The sorted array (new array)
826
     */
827
    return function (array $array) use ($flag) {
828
        \rsort($array, $flag);
829
        return $array;
830
    };
831
}
832
833
834
/**
835
 * Returns a Closure for sorting an array by key in ascending order.
836
 *
837
 * @param int $flag Uses php stock sort constants or numerical values.
838
 * @return Closure(mixed[]):mixed[]
839
 */
840
function ksort(int $flag = SORT_REGULAR): Closure
841
{
842
    /**
843
     *  @param mixed[]$array The array to sort
844
     *  @return mixed[] The sorted array (new array)
845
     */
846
    return function (array $array) use ($flag) {
847
        \ksort($array, $flag);
848
        return $array;
849
    };
850
}
851
852
/**
853
 * Returns a Closure for sorting an array by key in descending (reverse) order.
854
 *
855
 * @param int $flag Uses php stock sort constants or numerical values.
856
 * @return Closure(mixed[]):mixed[]
857
 */
858
function krsort(int $flag = SORT_REGULAR): Closure
859
{
860
    /**
861
     *  @param mixed[]$array The array to sort
862
     *  @return mixed[] The sorted array (new array)
863
     */
864
    return function (array $array) use ($flag) {
865
        \krsort($array, $flag);
866
        return $array;
867
    };
868
}
869
870
/**
871
 * Returns a Closure for sorting an array by value in ascending order.
872
 * Maintain keys.
873
 *
874
 * @param int $flag Uses php stock sort constants or numerical values.
875
 * @return Closure(mixed[]):mixed[]
876
 */
877
function asort(int $flag = SORT_REGULAR): Closure
878
{
879
    /**
880
     *  @param mixed[]$array The array to sort
881
     *  @return mixed[] The sorted array (new array)
882
     */
883
    return function (array $array) use ($flag) {
884
        \asort($array, $flag);
885
        return $array;
886
    };
887
}
888
889
/**
890
 * Returns a Closure for sorting an array by value in descending (reverse) order.
891
 * Maintain keys.
892
 *
893
 * @param int $flag Uses php stock sort constants or numerical values.
894
 * @return Closure(mixed[]):mixed[]
895
 */
896
function arsort(int $flag = SORT_REGULAR): Closure
897
{
898
    /**
899
     *  @param mixed[]$array The array to sort
900
     *  @return mixed[] The sorted array (new array)
901
     */
902
    return function (array $array) use ($flag) {
903
        \arsort($array, $flag);
904
        return $array;
905
    };
906
}
907
908
/**
909
 * Returns a Closure for sorting an array using a "natural order" algorithm
910
 *
911
 * @return Closure(mixed[]):mixed[]
912
 */
913
function natsort(): Closure
914
{
915
    /**
916
     *  @param mixed[]$array The array to sort
917
     *  @return mixed[] The sorted array (new array)
918
     */
919
    return function (array $array) {
920
        \natsort($array);
921
        return $array;
922
    };
923
}
924
925
/**
926
 * Returns a Closure for sorting an array using a case insensitive "natural order" algorithm
927
 *
928
 * @return Closure(mixed[]):mixed[]
929
 */
930
function natcasesort(): Closure
931
{
932
    /**
933
     *  @param mixed[]$array The array to sort
934
     *  @return mixed[] The sorted array (new array)
935
     */
936
    return function (array $array) {
937
        \natcasesort($array);
938
        return $array;
939
    };
940
}
941
942
/**
943
 * Returns a Closure for sorting an array by key using a custom comparison function
944
 *
945
 * @param callable(mixed $a, mixed $b): int $function
946
 * @return Closure(mixed[]):mixed[]
947
 */
948
function uksort(callable $function): Closure
949
{
950
    /**
951
     *  @param mixed[] $array The array to sort
952
     *  @return mixed[] The sorted array (new array)
953
     */
954
    return function (array $array) use ($function) {
955
        \uksort($array, $function);
956
        return $array;
957
    };
958
}
959
960
/**
961
 * Returns a Closure for sorting an array using a custom comparison function
962
 * Maintain keys.
963
 *
964
 * @param callable(mixed $a, mixed $b): int $function
965
 * @return Closure(mixed[]):mixed[]
966
 */
967
function uasort(callable $function): Closure
968
{
969
    /**
970
     *  @param mixed[]$array The array to sort
971
     *  @return mixed[] The sorted array (new array)
972
     */
973
    return function (array $array) use ($function) {
974
        \uasort($array, $function);
975
        return $array;
976
    };
977
}
978
979
980
/**
981
 * Returns a Closure for sorting an array using a custom comparison function
982
 * Doesn't maintain keys.
983
 *
984
 * @param callable(mixed $a, mixed $b): int $function
985
 * @return Closure(mixed[]):mixed[]
986
 */
987
function usort(callable $function): Closure
988
{
989
    /**
990
     *  @param mixed[]$array The array to sort
991
     *  @return mixed[] The sorted array (new array)
992
     */
993
    return function (array $array) use ($function) {
994
        \usort($array, $function);
995
        return $array;
996
    };
997
}
998
999
/**
1000
 * Returns a Closure for applying a function to every element of an array
1001
 *
1002
 * @param callable(mixed $carry, mixed $value):mixed $function
1003
 * @param mixed $initialValue
1004
 * @return Closure(mixed[]):mixed[]
1005
 */
1006
function scan(callable $function, $initialValue): Closure
1007
{
1008
    return function (array $array) use ($function, $initialValue) {
1009
        $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...
1010
        foreach ($array as $key => $value) {
1011
            $initialValue = $function($initialValue, $value);
1012
            $carry[]      = $initialValue;
1013
        }
1014
        return $carry;
1015
    };
1016
}
1017
1018
/**
1019
 * Returns a Closure for applying a function to every element of an array
1020
 *
1021
 * @param callable(mixed $carry, mixed $value):mixed $function
1022
 * @param mixed $initialValue
1023
 * @return Closure(mixed[]):mixed[]
1024
 */
1025
function scanR(callable $function, $initialValue): Closure
1026
{
1027
    return function (array $array) use ($function, $initialValue) {
1028
        $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...
1029
        foreach (array_reverse($array) as $key => $value) {
1030
            $initialValue = $function($initialValue, $value);
1031
            $carry[]      = $initialValue;
1032
        }
1033
        return \array_reverse($carry);
1034
    };
1035
}
1036
1037
/**
1038
 * Creates a function for defining the callback and initial for reduce/fold
1039
 *
1040
 * @param callable(mixed $carry, mixed $value): mixed $callable
1041
 * @param mixed $initial
1042
 * @return Closure(mixed[]):mixed
1043
 */
1044
function fold(callable $callable, $initial = array()): Closure
1045
{
1046
    /**
1047
     * @param mixed[] $array
1048
     * @return mixed
1049
     */
1050
    return function (array $array) use ($callable, $initial) {
1051
        return array_reduce($array, $callable, $initial);
1052
    };
1053
}
1054
1055
/**
1056
 * Creates a function for defining the callback and initial for reduce/fold
1057
 *
1058
 * @param callable(mixed $carry, mixed $value): mixed $callable
1059
 * @param mixed $initial
1060
 * @return Closure(mixed[]):mixed
1061
 */
1062
function foldR(callable $callable, $initial = array()): Closure
1063
{
1064
    /**
1065
     * @param mixed[] $array
1066
     * @return mixed
1067
     */
1068
    return function (array $array) use ($callable, $initial) {
1069
        return array_reduce(\array_reverse($array), $callable, $initial);
1070
    };
1071
}
1072
1073
/**
1074
 * Creates a function for defining the callback and initial for reduce/fold, with the key
1075
 * also passed to the callback.
1076
 *
1077
 * @param callable(mixed $carry, int|string $key, mixed $value): mixed $callable
1078
 * @param mixed $initial
1079
 * @return Closure(mixed[]):mixed
1080
 */
1081
function foldKeys(callable $callable, $initial = array()): Closure
1082
{
1083
    /**
1084
     * @param mixed[] $array
1085
     * @return mixed
1086
     */
1087
    return function (array $array) use ($callable, $initial) {
1088
        foreach ($array as $key => $value) {
1089
            $initial = $callable($initial, $key, $value);
1090
        }
1091
        return $initial;
1092
    };
1093
}
1094
1095
/**
1096
 * Creates a function which takes the first n elements from an array
1097
 *
1098
 * @param int $count
1099
 * @return Closure(mixed[]):mixed[]
1100
 * @throws \InvalidArgumentException if count is negative
1101
 */
1102
function take(int $count = 1): Closure
1103
{
1104
    // throw InvalidArgumentException if count is negative
1105
    if ($count < 0) {
1106
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1107
    }
1108
1109
    /**
1110
     * @param mixed[] $array
1111
     * @return mixed[]
1112
     */
1113
    return function (array $array) use ($count) {
1114
        return \array_slice($array, 0, $count);
1115
    };
1116
}
1117
1118
/**
1119
 * Creates a function which takes the last n elements from an array
1120
 *
1121
 * @param int $count
1122
 * @return Closure(mixed[]):mixed[]
1123
 * @throws \InvalidArgumentException if count is negative
1124
 */
1125
function takeLast(int $count = 1): Closure
1126
{
1127
    // throw InvalidArgumentException if count is negative
1128
    if ($count < 0) {
1129
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1130
    }
1131
1132
    // If count is 0, return an empty array
1133
    if ($count === 0) {
1134
        return function (array $array) {
1135
            return array();
1136
        };
1137
    }
1138
1139
    /**
1140
     * @param mixed[] $array
1141
     * @return mixed[]
1142
     */
1143
    return function (array $array) use ($count) {
1144
        return \array_slice($array, - $count);
1145
    };
1146
}
1147
1148
/**
1149
 * Creates a function that allows you to take a slice of an array until the passed conditional
1150
 * returns true.
1151
 *
1152
 * @param callable(mixed): bool $conditional
1153
 * @return Closure(mixed[]):mixed[]
1154
 */
1155
function takeUntil(callable $conditional): Closure
1156
{
1157
    /**
1158
     * @param mixed[] $array
1159
     * @return mixed[]
1160
     */
1161
    return function (array $array) use ($conditional) {
1162
        $carry = array();
1163
        foreach ($array as $key => $value) {
1164
            if (true === $conditional($value)) {
1165
                break;
1166
            }
1167
            $carry[ $key ] = $value;
1168
        }
1169
        return $carry;
1170
    };
1171
}
1172
1173
/**
1174
 * Creates a function that allows you to take a slice of an array until the passed conditional
1175
 * returns false.
1176
 *
1177
 * @param callable(mixed): bool $conditional
1178
 * @return Closure(mixed[]):mixed[]
1179
 */
1180
function takeWhile(callable $conditional): Closure
1181
{
1182
    /**
1183
     * @param mixed[] $array
1184
     * @return mixed[]
1185
     */
1186
    return function (array $array) use ($conditional) {
1187
        $carry = array();
1188
        foreach ($array as $key => $value) {
1189
            if (false === $conditional($value)) {
1190
                break;
1191
            }
1192
            $carry[ $key ] = $value;
1193
        }
1194
        return $carry;
1195
    };
1196
}
1197