Passed
Push — develop ( f86ba6...45a75e )
by Glynn
59s queued 13s
created

mapWithKey()   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 for flattening and mapping an array
521
 *
522
 * @param callable(mixed):mixed $function The function to map the element. (Will no be called if resolves to array)
523
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
524
 * @return Closure(mixed[]):mixed[]
525
 */
526
function flatMap(callable $function, ?int $n = null): Closure
527
{
528
    /**
529
     * @param mixed[] $array
530
     * @return mixed[]
531
     */
532
    return function (array $array) use ($n, $function): array {
533
        return array_reduce(
534
            $array,
535
            /**
536
             * @param mixed[] $carry
537
             * @param mixed $element
538
             * @return mixed[]
539
             */
540
            function (array $carry, $element) use ($n, $function): array {
541
                if (is_array($element) && (is_null($n) || $n > 0)) {
542
                    $carry = array_merge($carry, flatMap($function, $n ? $n - 1 : null)($element));
543
                } else {
544
                    $carry[] = is_array($element) ? $element : $function($element);
545
                }
546
                return $carry;
547
            },
548
            array()
549
        );
550
    };
551
}
552
553
/*
554
 *                         **********************
555
 *                         * General Operations *
556
 *                         **********************
557
 */
558
559
560
/**
561
 * Creates a Closure for grouping an array.
562
 *
563
 * @param callable(mixed):(string|int) $function The function to group by.
564
 * @return Closure(mixed):mixed[]
565
 */
566
function groupBy(callable $function): Closure
567
{
568
    /**
569
     * @param mixed[] $array The array to be grouped
570
     * @return mixed[] Grouped array.
571
     */
572
    return function (array $array) use ($function): array {
573
        return array_reduce(
574
            $array,
575
            /**
576
             * @param mixed[] $carry
577
             * @param mixed $element
578
             * @return mixed[]
579
             */
580
            function ($carry, $item) use ($function): array {
581
                $carry[ call_user_func($function, $item) ][] = $item;
582
                return $carry;
583
            },
584
            array()
585
        );
586
    };
587
}
588
589
/**
590
 * Creates a Closure for chunking an array to set a limit.
591
 *
592
 * @param int $count The max size of each chunk. Must not be less than 1!
593
 * @param bool $preserveKeys Should inital keys be kept. Default false.
594
 * @return Closure(mixed[]):mixed[]
595
 */
596
function chunk(int $count, bool $preserveKeys = false): Closure
597
{
598
    /**
599
     * @param mixed[] $array Array to chunk
600
     * @return mixed[]
601
     */
602
    return function (array $array) use ($count, $preserveKeys): array {
603
        return array_chunk($array, max(1, $count), $preserveKeys);
604
    };
605
}
606
607
/**
608
 * Create callback for extracting a single column from an array.
609
 *
610
 * @param string $column Column to retrieve.
611
 * @param string $key Use column for assigning as the index. defaults to numeric keys if null.
612
 * @return Closure(mixed[]):mixed[]
613
 */
614
function column(string $column, ?string $key = null): Closure
615
{
616
    /**
617
     * @param mixed[] $array
618
     * @return mixed[]
619
     */
620
    return function (array $array) use ($column, $key): array {
621
        return array_column($array, $column, $key);
622
    };
623
}
624
625
/**
626
 * Returns a Closure for flattening an array to a defined depth
627
 *
628
 * @param int|null $n Depth of nodes to flatten. If null will flatten to n
629
 * @return Closure(mixed[] $var): mixed[]
630
 */
631
function flattenByN(?int $n = null): Closure
632
{
633
    /**
634
     * @param mixed[] $array Array to flatten
635
     * @return mixed[]
636
     */
637
    return function (array $array) use ($n): array {
638
        return array_reduce(
639
            $array,
640
            /**
641
             * @param array<int|string, mixed> $carry
642
             * @param mixed|mixed[] $element
643
             * @return array<int|string, mixed>
644
             */
645
            function (array $carry, $element) use ($n): array {
646
                // Remove empty arrays.
647
                if (is_array($element) && empty($element)) {
648
                    return $carry;
649
                }
650
                // If the element is an array and we are still flattening, call again
651
                if (is_array($element) && (is_null($n) || $n > 0)) { // @phpstan-ignore-line
652
                    $carry = array_merge($carry, flattenByN($n ? $n - 1 : null)($element));
653
                } else {
654
                    // Else just add the element.
655
                    $carry[] = $element;
656
                }
657
                return $carry;
658
            },
659
            array()
660
        );
661
    };
662
}
663
664
/**
665
 * Returns a closure for recursively changing values in an array.
666
 *
667
 * @param mixed[] ...$with The array values to replace with
668
 * @return Closure(mixed[]):mixed[]
669
 */
670
function replaceRecursive(array ...$with): Closure
671
{
672
    /**
673
     * @param mixed[] $array The array to have elements replaced from.
674
     * @return mixed[] Array with replacements.
675
     */
676
    return function (array $array) use ($with): array {
677
        return array_replace_recursive($array, ...$with);
678
    };
679
}
680
681
/**
682
 * Returns a Closure for changing all values in a flat array, based on key.
683
 *
684
 * @param mixed[] ...$with Array with values to replace with, must have matching key with base array.
685
 * @return Closure(mixed[]):mixed[]
686
 */
687
function replace(array ...$with): Closure
688
{
689
    /**
690
     * @param mixed[] $array The array to have elements replaced from.
691
     * @return mixed[] Array with replacements.
692
     */
693
    return function (array $array) use ($with): array {
694
        return array_replace($array, ...$with);
695
    };
696
}
697
698
/**
699
 * Returns a Closure for doing array_sum with the results of a function/expression.
700
 *
701
 * @param callable(mixed):Number $function The function to return the value for array sum
702
 * @return Closure(mixed[]):Number
703
 */
704
function sumWhere(callable $function): Closure
705
{
706
    /**
707
     * @param mixed[] $array Array to do sum() on.
708
     * @return Number The total.
709
     */
710
    return function (array $array) use ($function) {
711
        return array_sum(array_map($function, $array));
712
    };
713
}
714
715
/**
716
 * Creates a closure for casting an array to an object.
717
 * Assumed all properties are public
718
 * None existing properties will be set as dynamic properties.
719
 *
720
 * @param object|null $object The object to cast to, defaults to stdClass
721
 * @return Closure(mixed[]):object
722
 */
723
function toObject(?object $object = null): Closure
724
{
725
    $object = $object ?? new stdClass();
726
727
    /**
728
     * @param mixed[] $array
729
     * @return object
730
     */
731
    return function (array $array) use ($object): object {
732
        foreach ($array as $key => $value) {
733
            $key            = is_string($key) ? $key : (string) $key;
734
            $object->{$key} = $value;
735
        }
736
        return $object;
737
    };
738
}
739
740
/**
741
 * Creates a closure for encoding json with defined flags/depth
742
 *
743
 * @param int $flags json_encode flags (default = 0)
744
 * @param int $depth Nodes deep to encode (default = 512)
745
 * @return \Closure(mixed):?string
746
 * @constants JSON_FORCE_OBJECT, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP,
747
 *            JSON_HEX_APOS, JSON_INVALID_UTF8_IGNORE,
748
 *            JSON_INVALID_UTF8_SUBSTITUTE, JSON_NUMERIC_CHECK,
749
 *            JSON_PARTIAL_OUTPUT_ON_ERROR, JSON_PRESERVE_ZERO_FRACTION,
750
 *            JSON_PRETTY_PRINT, JSON_UNESCAPED_LINE_TERMINATORS,
751
 *            JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, JSON_THROW_ON_ERROR
752
 */
753
function toJson(int $flags = 0, int $depth = 512): Closure
754
{
755
    /**
756
     * @param mixed $data
757
     * @return string|null
758
     */
759
    return function ($data) use ($flags, $depth): ?string {
760
        return \json_encode($data, $flags, max(1, $depth)) ?: null;
761
    };
762
}
763
764
765
/*
766
 *                         ****************
767
 *                         *  Array Sort  *
768
 *                         ****************
769
 */
770
771
772
/**
773
 * Returns a Closure for doing regular SORT against an array.
774
 * Doesn't maintain keys.
775
 *
776
 * @param int $flag Uses php stock sort constants or numerical values.
777
 * @return Closure(mixed[]):mixed[]
778
 */
779
function sort(int $flag = SORT_REGULAR): Closure
780
{
781
    /**
782
     *  @param mixed[]$array The array to sort
783
     *  @return mixed[] The sorted array (new array)
784
     */
785
    return function (array $array) use ($flag) {
786
        \sort($array, $flag);
787
        return $array;
788
    };
789
}
790
791
/**
792
 * Returns a Closure for doing regular Reverse SORT against an array.
793
 * Doesn't maintain keys.
794
 *
795
 * @param int $flag Uses php stock sort constants or numerical values.
796
 * @return Closure(mixed[]):mixed[]
797
 */
798
function rsort(int $flag = SORT_REGULAR): Closure
799
{
800
    /**
801
     *  @param mixed[]$array The array to sort
802
     *  @return mixed[] The sorted array (new array)
803
     */
804
    return function (array $array) use ($flag) {
805
        \rsort($array, $flag);
806
        return $array;
807
    };
808
}
809
810
811
/**
812
 * Returns a Closure for sorting an array by key in ascending order.
813
 *
814
 * @param int $flag Uses php stock sort constants or numerical values.
815
 * @return Closure(mixed[]):mixed[]
816
 */
817
function ksort(int $flag = SORT_REGULAR): Closure
818
{
819
    /**
820
     *  @param mixed[]$array The array to sort
821
     *  @return mixed[] The sorted array (new array)
822
     */
823
    return function (array $array) use ($flag) {
824
        \ksort($array, $flag);
825
        return $array;
826
    };
827
}
828
829
/**
830
 * Returns a Closure for sorting an array by key in descending (reverse) order.
831
 *
832
 * @param int $flag Uses php stock sort constants or numerical values.
833
 * @return Closure(mixed[]):mixed[]
834
 */
835
function krsort(int $flag = SORT_REGULAR): Closure
836
{
837
    /**
838
     *  @param mixed[]$array The array to sort
839
     *  @return mixed[] The sorted array (new array)
840
     */
841
    return function (array $array) use ($flag) {
842
        \krsort($array, $flag);
843
        return $array;
844
    };
845
}
846
847
/**
848
 * Returns a Closure for sorting an array by value in ascending order.
849
 * Maintain keys.
850
 *
851
 * @param int $flag Uses php stock sort constants or numerical values.
852
 * @return Closure(mixed[]):mixed[]
853
 */
854
function asort(int $flag = SORT_REGULAR): Closure
855
{
856
    /**
857
     *  @param mixed[]$array The array to sort
858
     *  @return mixed[] The sorted array (new array)
859
     */
860
    return function (array $array) use ($flag) {
861
        \asort($array, $flag);
862
        return $array;
863
    };
864
}
865
866
/**
867
 * Returns a Closure for sorting an array by value in descending (reverse) order.
868
 * Maintain keys.
869
 *
870
 * @param int $flag Uses php stock sort constants or numerical values.
871
 * @return Closure(mixed[]):mixed[]
872
 */
873
function arsort(int $flag = SORT_REGULAR): Closure
874
{
875
    /**
876
     *  @param mixed[]$array The array to sort
877
     *  @return mixed[] The sorted array (new array)
878
     */
879
    return function (array $array) use ($flag) {
880
        \arsort($array, $flag);
881
        return $array;
882
    };
883
}
884
885
/**
886
 * Returns a Closure for sorting an array using a "natural order" algorithm
887
 *
888
 * @return Closure(mixed[]):mixed[]
889
 */
890
function natsort(): 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) {
897
        \natsort($array);
898
        return $array;
899
    };
900
}
901
902
/**
903
 * Returns a Closure for sorting an array using a case insensitive "natural order" algorithm
904
 *
905
 * @return Closure(mixed[]):mixed[]
906
 */
907
function natcasesort(): Closure
908
{
909
    /**
910
     *  @param mixed[]$array The array to sort
911
     *  @return mixed[] The sorted array (new array)
912
     */
913
    return function (array $array) {
914
        \natcasesort($array);
915
        return $array;
916
    };
917
}
918
919
/**
920
 * Returns a Closure for sorting an array by key using a custom comparison function
921
 *
922
 * @param callable(mixed $a, mixed $b): int $function
923
 * @return Closure(mixed[]):mixed[]
924
 */
925
function uksort(callable $function): Closure
926
{
927
    /**
928
     *  @param mixed[] $array The array to sort
929
     *  @return mixed[] The sorted array (new array)
930
     */
931
    return function (array $array) use ($function) {
932
        \uksort($array, $function);
933
        return $array;
934
    };
935
}
936
937
/**
938
 * Returns a Closure for sorting an array using a custom comparison function
939
 * Maintain keys.
940
 *
941
 * @param callable(mixed $a, mixed $b): int $function
942
 * @return Closure(mixed[]):mixed[]
943
 */
944
function uasort(callable $function): Closure
945
{
946
    /**
947
     *  @param mixed[]$array The array to sort
948
     *  @return mixed[] The sorted array (new array)
949
     */
950
    return function (array $array) use ($function) {
951
        \uasort($array, $function);
952
        return $array;
953
    };
954
}
955
956
957
/**
958
 * Returns a Closure for sorting an array using a custom comparison function
959
 * Doesn't maintain keys.
960
 *
961
 * @param callable(mixed $a, mixed $b): int $function
962
 * @return Closure(mixed[]):mixed[]
963
 */
964
function usort(callable $function): Closure
965
{
966
    /**
967
     *  @param mixed[]$array The array to sort
968
     *  @return mixed[] The sorted array (new array)
969
     */
970
    return function (array $array) use ($function) {
971
        \usort($array, $function);
972
        return $array;
973
    };
974
}
975
976
/**
977
 * Returns a Closure for applying a function to every element of an array
978
 *
979
 * @param callable(mixed $carry, mixed $value):mixed $function
980
 * @param mixed $initialValue
981
 * @return Closure(mixed[]):mixed[]
982
 */
983
function scan(callable $function, $initialValue): Closure
984
{
985
    return function (array $array) use ($function, $initialValue) {
986
        $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...
987
        foreach ($array as $key => $value) {
988
            $initialValue = $function($initialValue, $value);
989
            $carry[]      = $initialValue;
990
        }
991
        return $carry;
992
    };
993
}
994
995
/**
996
 * Returns a Closure for applying a function to every element of an array
997
 *
998
 * @param callable(mixed $carry, mixed $value):mixed $function
999
 * @param mixed $initialValue
1000
 * @return Closure(mixed[]):mixed[]
1001
 */
1002
function scanR(callable $function, $initialValue): Closure
1003
{
1004
    return function (array $array) use ($function, $initialValue) {
1005
        $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...
1006
        foreach (array_reverse($array) as $key => $value) {
1007
            $initialValue = $function($initialValue, $value);
1008
            $carry[]      = $initialValue;
1009
        }
1010
        return \array_reverse($carry);
1011
    };
1012
}
1013
1014
/**
1015
 * Creates a function for defining the callback and initial for reduce/fold
1016
 *
1017
 * @param callable(mixed $carry, mixed $value): mixed $callable
1018
 * @param mixed $initial
1019
 * @return Closure(mixed[]):mixed
1020
 */
1021
function fold(callable $callable, $initial = array()): Closure
1022
{
1023
    /**
1024
     * @param mixed[] $array
1025
     * @return mixed
1026
     */
1027
    return function (array $array) use ($callable, $initial) {
1028
        return array_reduce($array, $callable, $initial);
1029
    };
1030
}
1031
1032
/**
1033
 * Creates a function for defining the callback and initial for reduce/fold
1034
 *
1035
 * @param callable(mixed $carry, mixed $value): mixed $callable
1036
 * @param mixed $initial
1037
 * @return Closure(mixed[]):mixed
1038
 */
1039
function foldR(callable $callable, $initial = array()): Closure
1040
{
1041
    /**
1042
     * @param mixed[] $array
1043
     * @return mixed
1044
     */
1045
    return function (array $array) use ($callable, $initial) {
1046
        return array_reduce(\array_reverse($array), $callable, $initial);
1047
    };
1048
}
1049
1050
/**
1051
 * Creates a function for defining the callback and initial for reduce/fold, with the key
1052
 * also passed to the callback.
1053
 *
1054
 * @param callable(mixed $carry, int|string $key, mixed $value): mixed $callable
1055
 * @param mixed $initial
1056
 * @return Closure(mixed[]):mixed
1057
 */
1058
function foldKeys(callable $callable, $initial = array()): Closure
1059
{
1060
    /**
1061
     * @param mixed[] $array
1062
     * @return mixed
1063
     */
1064
    return function (array $array) use ($callable, $initial) {
1065
        foreach ($array as $key => $value) {
1066
            $initial = $callable($initial, $key, $value);
1067
        }
1068
        return $initial;
1069
    };
1070
}
1071
1072
/**
1073
 * Creates a function which takes the first n elements from an array
1074
 *
1075
 * @param int $count
1076
 * @return Closure(mixed[]):mixed[]
1077
 * @throws \InvalidArgumentException if count is negative
1078
 */
1079
function take(int $count = 1): Closure
1080
{
1081
    // throw InvalidArgumentException if count is negative
1082
    if ($count < 0) {
1083
        throw new \InvalidArgumentException(__FUNCTION__ . ' count must be greater than or equal to 0');
1084
    }
1085
1086
    /**
1087
     * @param mixed[] $array
1088
     * @return mixed[]
1089
     */
1090
    return function (array $array) use ($count) {
1091
        return \array_slice($array, 0, $count);
1092
    };
1093
}
1094
1095
/**
1096
 * Creates a function which takes the last 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 takeLast(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
    // If count is 0, return an empty array
1110
    if ($count === 0) {
1111
        return function (array $array) {
1112
            return array();
1113
        };
1114
    }
1115
1116
    /**
1117
     * @param mixed[] $array
1118
     * @return mixed[]
1119
     */
1120
    return function (array $array) use ($count) {
1121
        return \array_slice($array, - $count);
1122
    };
1123
}
1124
1125
/**
1126
 * Creates a function that allows you to take a slice of an array until the passed conditional
1127
 * returns true.
1128
 *
1129
 * @param callable(mixed): bool $conditional
1130
 * @return Closure(mixed[]):mixed[]
1131
 */
1132
function takeUntil(callable $conditional): Closure
1133
{
1134
    /**
1135
     * @param mixed[] $array
1136
     * @return mixed[]
1137
     */
1138
    return function (array $array) use ($conditional) {
1139
        $carry = array();
1140
        foreach ($array as $key => $value) {
1141
            if (true === $conditional($value)) {
1142
                break;
1143
            }
1144
            $carry[ $key ] = $value;
1145
        }
1146
        return $carry;
1147
    };
1148
}
1149
1150
/**
1151
 * Creates a function that allows you to take a slice of an array until the passed conditional
1152
 * returns false.
1153
 *
1154
 * @param callable(mixed): bool $conditional
1155
 * @return Closure(mixed[]):mixed[]
1156
 */
1157
function takeWhile(callable $conditional): Closure
1158
{
1159
    /**
1160
     * @param mixed[] $array
1161
     * @return mixed[]
1162
     */
1163
    return function (array $array) use ($conditional) {
1164
        $carry = array();
1165
        foreach ($array as $key => $value) {
1166
            if (false === $conditional($value)) {
1167
                break;
1168
            }
1169
            $carry[ $key ] = $value;
1170
        }
1171
        return $carry;
1172
    };
1173
}
1174