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) { |
|
|
|
|
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; |
|
|
|
|
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; |
|
|
|
|
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
|
|
|
|
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
orexit
statements that have been added for debug purposes.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.