1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DusanKasan\Knapsack; |
4
|
|
|
|
5
|
|
|
use DusanKasan\Knapsack\Exceptions\ItemNotFound; |
6
|
|
|
use DusanKasan\Knapsack\Exceptions\NoMoreItems; |
7
|
|
|
use Traversable; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Converts $collection to array. If there are multiple items with the same key, only the last will be preserved. |
11
|
|
|
* |
12
|
|
|
* @param array|Traversable $collection |
13
|
|
|
* @return array |
14
|
|
|
*/ |
15
|
|
|
function toArray($collection) |
16
|
|
|
{ |
17
|
64 |
|
$arr = []; |
18
|
64 |
|
foreach ($collection as $key => $value) { |
19
|
64 |
|
$arr[$key] = $value; |
20
|
64 |
|
} |
21
|
|
|
|
22
|
64 |
|
return $arr; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Returns a lazy collection of distinct items in $collection. |
27
|
|
|
* |
28
|
|
|
* @param array|Traversable $collection |
29
|
|
|
* @return Collection |
30
|
|
|
*/ |
31
|
|
|
function distinct($collection) |
32
|
|
|
{ |
33
|
|
|
$generatorFactory = function () use ($collection) { |
34
|
1 |
|
$distinctValues = []; |
35
|
|
|
|
36
|
1 |
|
foreach ($collection as $key => $value) { |
37
|
1 |
|
if (!in_array($value, $distinctValues)) { |
38
|
1 |
|
$distinctValues[] = $value; |
39
|
1 |
|
yield $key => $value; |
40
|
1 |
|
} |
41
|
1 |
|
} |
42
|
1 |
|
}; |
43
|
|
|
|
44
|
1 |
|
return new Collection($generatorFactory); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Returns number of items in $collection. |
49
|
|
|
* |
50
|
|
|
* @param array|Traversable $collection |
51
|
|
|
* @return int |
52
|
|
|
*/ |
53
|
|
|
function size($collection) |
54
|
|
|
{ |
55
|
8 |
|
$result = 0; |
56
|
8 |
|
foreach ($collection as $value) { |
57
|
8 |
|
$result++; |
58
|
8 |
|
} |
59
|
|
|
|
60
|
8 |
|
return $result; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Returns a non-lazy collection with items from $collection in reversed order. |
65
|
|
|
* |
66
|
|
|
* @param array|Traversable $collection |
67
|
|
|
* @return Collection |
68
|
|
|
*/ |
69
|
|
|
function reverse($collection) |
70
|
|
|
{ |
71
|
|
|
$generatorFactory = function () use ($collection) { |
72
|
4 |
|
$array = []; |
73
|
4 |
|
foreach ($collection as $key => $value) { |
74
|
3 |
|
$array[] = [$key, $value]; |
75
|
4 |
|
} |
76
|
|
|
|
77
|
4 |
|
return map( |
78
|
5 |
|
indexBy( |
79
|
4 |
|
array_reverse($array), |
80
|
|
|
function($item) { |
81
|
3 |
|
return $item[0]; |
82
|
|
|
} |
83
|
4 |
|
), |
84
|
|
|
function($item) { |
85
|
3 |
|
return $item[1]; |
86
|
|
|
} |
87
|
4 |
|
); |
88
|
4 |
|
}; |
89
|
|
|
|
90
|
4 |
|
return new Collection($generatorFactory); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Returns a lazy collection of values from $collection (i.e. the keys are reset). |
95
|
|
|
* |
96
|
|
|
* @param array|Traversable $collection |
97
|
|
|
* @return Collection |
98
|
|
|
*/ |
99
|
|
|
function values($collection) |
100
|
|
|
{ |
101
|
|
|
$generatorFactory = function () use ($collection) { |
102
|
32 |
|
foreach ($collection as $value) { |
103
|
30 |
|
yield $value; |
104
|
26 |
|
} |
105
|
32 |
|
}; |
106
|
|
|
|
107
|
32 |
|
return new Collection($generatorFactory); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Returns a lazy collection of keys from $collection. |
112
|
|
|
* |
113
|
|
|
* @param array|Traversable $collection |
114
|
|
|
* @return Collection |
115
|
|
|
*/ |
116
|
|
|
function keys($collection) |
117
|
|
|
{ |
118
|
|
|
$generatorFactory = function () use ($collection) { |
119
|
1 |
|
foreach ($collection as $key => $value) { |
120
|
1 |
|
yield $key; |
121
|
1 |
|
} |
122
|
1 |
|
}; |
123
|
|
|
|
124
|
1 |
|
return new Collection($generatorFactory); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Returns a lazy collection of items from $collection repeated infinitely. |
129
|
|
|
* |
130
|
|
|
* @param array|Traversable $collection |
131
|
|
|
* @return Collection |
132
|
|
|
*/ |
133
|
|
|
function cycle($collection) |
134
|
|
|
{ |
135
|
|
|
$generatorFactory = function () use ($collection) { |
136
|
1 |
|
while (true) { |
137
|
1 |
|
foreach ($collection as $key => $value) { |
138
|
1 |
|
yield $key => $value; |
139
|
1 |
|
} |
140
|
1 |
|
} |
141
|
1 |
|
}; |
142
|
|
|
|
143
|
1 |
|
return new Collection($generatorFactory); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Returns a non-lazy collection of shuffled items from $collection. |
148
|
|
|
* |
149
|
|
|
* @param array|Traversable $collection |
150
|
|
|
* @return Collection |
151
|
|
|
*/ |
152
|
|
|
function shuffle($collection) |
153
|
|
|
{ |
154
|
1 |
|
$buffer = []; |
155
|
1 |
|
foreach ($collection as $key => $value) { |
156
|
1 |
|
$buffer[] = [$key, $value]; |
157
|
1 |
|
} |
158
|
|
|
|
159
|
1 |
|
\shuffle($buffer); |
160
|
|
|
|
161
|
1 |
|
return dereferenceKeyValue($buffer); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Returns true if $collection does not contain any items. |
166
|
|
|
* |
167
|
|
|
* @param array|Traversable $collection |
168
|
|
|
* @return bool |
169
|
|
|
*/ |
170
|
|
|
function isEmpty($collection) |
171
|
|
|
{ |
172
|
3 |
|
foreach ($collection as $value) { |
173
|
1 |
|
return false; |
174
|
2 |
|
} |
175
|
|
|
|
176
|
2 |
|
return true; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Returns true if $collection does contain any items. |
181
|
|
|
* |
182
|
|
|
* @param array|Traversable $collection |
183
|
|
|
* @return bool |
184
|
|
|
*/ |
185
|
|
|
function isNotEmpty($collection) |
186
|
|
|
{ |
187
|
2 |
|
return !isEmpty($collection); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Returns a collection where keys are distinct values from $collection and values are number of occurrences of each |
192
|
|
|
* value. |
193
|
|
|
* |
194
|
|
|
* @param array|Traversable $collection |
195
|
|
|
* @return Collection |
196
|
|
|
*/ |
197
|
|
|
function frequencies($collection) |
198
|
|
|
{ |
199
|
1 |
|
return countBy($collection, '\DusanKasan\Knapsack\identity'); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Returns the first item of $collection or throws ItemNotFound if #collection is empty. |
204
|
|
|
* |
205
|
|
|
* @param array|Traversable $collection |
206
|
|
|
* @return mixed |
207
|
|
|
*/ |
208
|
|
|
function first($collection) |
209
|
|
|
{ |
210
|
13 |
|
return get(values($collection), 0); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Returns the last item of $collection or throws ItemNotFound if #collection is empty. |
215
|
|
|
* |
216
|
|
|
* @param array|Traversable $collection |
217
|
|
|
* @return mixed |
218
|
|
|
*/ |
219
|
|
|
function last($collection) |
220
|
|
|
{ |
221
|
2 |
|
return first(reverse($collection)); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Returns a lazy collection of items of $collection where value of each item is set to the return value of calling |
226
|
|
|
* $function on its value and key. |
227
|
|
|
* |
228
|
|
|
* @param array|Traversable $collection |
229
|
|
|
* @param callable $function ($value, $key) |
230
|
|
|
* @return Collection |
231
|
|
|
*/ |
232
|
|
View Code Duplication |
function map($collection, callable $function) |
|
|
|
|
233
|
|
|
{ |
234
|
|
|
$generatorFactory = function () use ($collection, $function) { |
235
|
15 |
|
foreach ($collection as $key => $value) { |
236
|
14 |
|
yield $key => $function($value, $key); |
237
|
14 |
|
} |
238
|
15 |
|
}; |
239
|
|
|
|
240
|
15 |
|
return new Collection($generatorFactory); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Returns a lazy collection of items from $collection for which $function returns true. |
245
|
|
|
* |
246
|
|
|
* @param array|Traversable $collection |
247
|
|
|
* @param callable|null $function ($value, $key) |
248
|
|
|
* @return Collection |
249
|
|
|
*/ |
250
|
|
|
function filter($collection, callable $function = null) |
251
|
|
|
{ |
252
|
7 |
|
if (null === $function) { |
253
|
|
|
$function = function ($value) { |
254
|
1 |
|
return (bool)$value; |
255
|
1 |
|
}; |
256
|
1 |
|
}; |
257
|
|
|
|
258
|
|
|
$generatorFactory = function () use ($collection, $function) { |
259
|
7 |
|
foreach ($collection as $key => $value) { |
260
|
7 |
|
if ($function($value, $key)) { |
261
|
5 |
|
yield $key => $value; |
262
|
5 |
|
} |
263
|
7 |
|
} |
264
|
7 |
|
}; |
265
|
|
|
|
266
|
7 |
|
return new Collection($generatorFactory); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Returns a lazy collection with items from all $collections passed as argument appended together |
271
|
|
|
* |
272
|
|
|
* @param array|Traversable ...$collections |
273
|
|
|
* @return Collection |
274
|
|
|
*/ |
275
|
|
|
function concat(...$collections) |
276
|
|
|
{ |
277
|
|
|
$generatorFactory = function () use ($collections) { |
278
|
5 |
|
foreach ($collections as $collection) { |
279
|
5 |
|
foreach ($collection as $key => $value) { |
280
|
5 |
|
yield $key => $value; |
281
|
5 |
|
} |
282
|
5 |
|
} |
283
|
5 |
|
}; |
284
|
|
|
|
285
|
5 |
|
return new Collection($generatorFactory); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Reduces the collection to single value by iterating over the collection and calling $reduction while |
290
|
|
|
* passing $startValue and current key/item as parameters. The output of $function is used as $startValue in |
291
|
|
|
* next iteration. The output of $function on last element is the return value of this function. |
292
|
|
|
* |
293
|
|
|
* @param array|Traversable $collection |
294
|
|
|
* @param callable $function ($value, $key) |
295
|
|
|
* @param mixed $startValue |
296
|
|
|
* @return mixed |
297
|
|
|
*/ |
298
|
|
|
function reduce($collection, callable $function, $startValue) |
299
|
|
|
{ |
300
|
3 |
|
$tmp = duplicate($startValue); |
301
|
|
|
|
302
|
3 |
|
foreach ($collection as $key => $value) { |
303
|
3 |
|
$tmp = $function($tmp, $value, $key); |
304
|
3 |
|
} |
305
|
|
|
|
306
|
3 |
|
return $tmp; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Flattens multiple levels of nesting in collection. If $levelsToFlatten is not specified, flattens all levels of |
311
|
|
|
* nesting. |
312
|
|
|
* |
313
|
|
|
* @param array|Traversable $collection |
314
|
|
|
* @param int $levelsToFlatten -1 to flatten everything |
315
|
|
|
* @return Collection |
316
|
|
|
*/ |
317
|
|
|
function flatten($collection, $levelsToFlatten = -1) |
318
|
|
|
{ |
319
|
|
|
$generatorFactory = function () use ($collection, $levelsToFlatten) { |
320
|
3 |
|
$flattenNextLevel = $levelsToFlatten < 0 || $levelsToFlatten > 0; |
321
|
3 |
|
$childLevelsToFlatten = $levelsToFlatten > 0 ? $levelsToFlatten - 1 : $levelsToFlatten; |
322
|
|
|
|
323
|
3 |
|
foreach ($collection as $key => $value) { |
324
|
3 |
|
if ($flattenNextLevel && (is_array($value) || $value instanceof Traversable)) { |
325
|
3 |
|
foreach (flatten($value, $childLevelsToFlatten) as $childKey => $childValue) { |
326
|
3 |
|
yield $childKey => $childValue; |
327
|
3 |
|
} |
328
|
3 |
|
} else { |
329
|
3 |
|
yield $key => $value; |
330
|
|
|
} |
331
|
3 |
|
} |
332
|
3 |
|
}; |
333
|
|
|
|
334
|
3 |
|
return new Collection($generatorFactory); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Returns a non-lazy collection sorted using $collection($item1, $item2, $key1, $key2 ). $collection should |
339
|
|
|
* return true if first item is larger than the second and false otherwise. |
340
|
|
|
* |
341
|
|
|
* @param array|Traversable $collection |
342
|
|
|
* @param callable $function ($value1, $value2, $key1, $key2) |
343
|
|
|
* @return Collection |
344
|
|
|
*/ |
345
|
|
|
function sort($collection, callable $function) |
346
|
|
|
{ |
347
|
2 |
|
$array = iterator_to_array( |
348
|
2 |
|
values( |
349
|
2 |
|
map( |
350
|
2 |
|
$collection, |
351
|
|
|
function ($value, $key) { |
352
|
2 |
|
return [$key, $value]; |
353
|
|
|
} |
354
|
2 |
|
) |
355
|
2 |
|
) |
356
|
2 |
|
); |
357
|
|
|
|
358
|
2 |
|
uasort( |
359
|
2 |
|
$array, |
360
|
|
|
function ($a, $b) use ($function) { |
361
|
2 |
|
return $function($a[1], $b[1], $a[0], $b[0]); |
362
|
|
|
} |
363
|
2 |
|
); |
364
|
|
|
|
365
|
2 |
|
return dereferenceKeyValue($array); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
/** |
369
|
|
|
* Returns a lazy collection that is a part of $collection starting from $from position and ending in $to position. |
370
|
|
|
* If $to is not provided, the returned collection is contains all items from $from until end of $collection. All items |
371
|
|
|
* before $from are iterated over, but not included in result. |
372
|
|
|
* |
373
|
|
|
* @param array|Traversable $collection |
374
|
|
|
* @param int $from |
375
|
|
|
* @param int $to -1 to slice until end |
376
|
|
|
* @return Collection |
377
|
|
|
*/ |
378
|
|
|
function slice($collection, $from, $to = -1) |
379
|
|
|
{ |
380
|
|
|
$generatorFactory = function () use ($collection, $from, $to) { |
381
|
15 |
|
$index = 0; |
382
|
15 |
|
foreach ($collection as $key => $value) { |
383
|
15 |
|
if ($index >= $from && ($index < $to || $to == -1)) { |
384
|
15 |
|
yield $key => $value; |
385
|
15 |
|
} elseif ($index >= $to && $to >= 0) { |
386
|
12 |
|
break; |
387
|
|
|
} |
388
|
|
|
|
389
|
15 |
|
$index++; |
390
|
15 |
|
} |
391
|
15 |
|
}; |
392
|
|
|
|
393
|
15 |
|
return new Collection($generatorFactory); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Returns a non-lazy collection of items grouped by the result of $function. |
398
|
|
|
* |
399
|
|
|
* @param array|Traversable $collection |
400
|
|
|
* @param callable $function ($value, $key) |
401
|
|
|
* @return Collection |
402
|
|
|
*/ |
403
|
|
|
function groupBy($collection, callable $function) |
404
|
|
|
{ |
405
|
4 |
|
$result = []; |
406
|
|
|
|
407
|
4 |
|
foreach ($collection as $key => $value) { |
408
|
4 |
|
$newKey = $function($value, $key); |
409
|
|
|
|
410
|
4 |
|
$group = isset($result[$newKey]) ? $result[$newKey] : new Collection([]); |
411
|
4 |
|
$result[$newKey] = $group->append($value); |
412
|
4 |
|
} |
413
|
|
|
|
414
|
4 |
|
return Collection::from($result); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Returns a non-lazy collection of items grouped by the value at given key. Ignores non-collection items and items |
420
|
|
|
* without the given keys |
421
|
|
|
* |
422
|
|
|
* @param array|Traversable $collection |
423
|
|
|
* @param mixed $key |
424
|
|
|
* @return Collection |
425
|
|
|
*/ |
426
|
|
|
function groupByKey($collection, $key) |
427
|
|
|
{ |
428
|
|
|
$generatorFactory = function () use ($collection, $key) { |
429
|
|
|
|
430
|
1 |
|
return groupBy( |
431
|
1 |
|
filter( |
432
|
1 |
|
$collection, |
433
|
|
|
function ($item) use ($key) { |
434
|
1 |
|
return isCollection($item) && has($item, $key); |
435
|
|
|
} |
436
|
1 |
|
), |
437
|
|
|
function($value) use ($key) { |
438
|
1 |
|
return get($value, $key); |
439
|
|
|
} |
440
|
1 |
|
); |
441
|
1 |
|
}; |
442
|
|
|
|
443
|
1 |
|
return new Collection($generatorFactory); |
444
|
|
|
} |
445
|
|
|
/** |
446
|
|
|
* Executes $function for each item in $collection |
447
|
|
|
* |
448
|
|
|
* @param array|Traversable $collection |
449
|
|
|
* @param callable $function ($value, $key) |
450
|
|
|
* @return Collection |
451
|
|
|
*/ |
452
|
|
View Code Duplication |
function each($collection, callable $function) |
|
|
|
|
453
|
|
|
{ |
454
|
|
|
$generatorFactory = function () use ($collection, $function) { |
455
|
1 |
|
foreach ($collection as $key => $value) { |
456
|
1 |
|
$function($value, $key); |
457
|
|
|
|
458
|
1 |
|
yield $key => $value; |
459
|
1 |
|
} |
460
|
1 |
|
}; |
461
|
|
|
|
462
|
1 |
|
return new Collection($generatorFactory); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Returns an item with $key key from $collection. If that key is not present, throws ItemNotFound. |
467
|
|
|
* |
468
|
|
|
* @param array|Traversable $collection |
469
|
|
|
* @param mixed $key |
470
|
|
|
* @return mixed |
471
|
|
|
*/ |
472
|
|
|
function get($collection, $key) |
473
|
|
|
{ |
474
|
20 |
|
foreach ($collection as $valueKey => $value) { |
475
|
18 |
|
if ($key === $valueKey) { |
476
|
17 |
|
return $value; |
477
|
|
|
} |
478
|
16 |
|
} |
479
|
|
|
|
480
|
9 |
|
throw new ItemNotFound; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
/** |
484
|
|
|
* Returns an item with $key key from $collection. If that key is not present, returns $default. |
485
|
|
|
* |
486
|
|
|
* @param array|Traversable $collection |
487
|
|
|
* @param mixed $key |
488
|
|
|
* @param mixed $default value returned if key is not found |
489
|
|
|
* @return mixed |
490
|
|
|
*/ |
491
|
|
|
function getOrDefault($collection, $key, $default) |
492
|
|
|
{ |
493
|
|
|
try { |
494
|
3 |
|
return get($collection, $key); |
495
|
3 |
|
} catch (ItemNotFound $e) { |
496
|
3 |
|
return $default; |
497
|
|
|
} |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Returns the first item from $collection for which $function returns true. If item like that is not present, returns |
502
|
|
|
* $default. |
503
|
|
|
* |
504
|
|
|
* @param array|Traversable $collection |
505
|
|
|
* @param callable $function ($value, $key) |
506
|
|
|
* @param mixed $default |
507
|
|
|
* @return mixed |
508
|
|
|
*/ |
509
|
|
|
function find($collection, callable $function, $default = null) |
510
|
|
|
{ |
511
|
1 |
|
foreach ($collection as $key => $value) { |
512
|
1 |
|
if ($function($value, $key)) { |
513
|
1 |
|
return $value; |
514
|
|
|
} |
515
|
1 |
|
} |
516
|
|
|
|
517
|
1 |
|
return $default; |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* Returns a lazy collection by changing keys of $collection for each item to the result of $function for |
522
|
|
|
* that item. |
523
|
|
|
* |
524
|
|
|
* @param array|Traversable $collection |
525
|
|
|
* @param callable $function ($value, $key) |
526
|
|
|
* @return Collection |
527
|
|
|
*/ |
528
|
|
View Code Duplication |
function indexBy($collection, callable $function) |
|
|
|
|
529
|
|
|
{ |
530
|
|
|
$generatorFactory = function () use ($collection, $function) { |
531
|
5 |
|
foreach ($collection as $key => $value) { |
532
|
4 |
|
yield $function($value, $key) => $value; |
533
|
4 |
|
} |
534
|
5 |
|
}; |
535
|
|
|
|
536
|
5 |
|
return new Collection($generatorFactory); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Returns a non-lazy collection of items whose keys are the return values of $function and values are the number of |
541
|
|
|
* items in this collection for which the $function returned this value. |
542
|
|
|
* |
543
|
|
|
* @param array|Traversable $collection |
544
|
|
|
* @param callable $function ($value, $key) |
545
|
|
|
* @return Collection |
546
|
|
|
*/ |
547
|
|
|
function countBy($collection, callable $function) |
548
|
|
|
{ |
549
|
2 |
|
return map( |
550
|
2 |
|
groupBy($collection, $function), |
551
|
|
|
'\DusanKasan\Knapsack\size' |
552
|
2 |
|
); |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Returns true if $function returns true for every item in $collection |
557
|
|
|
* |
558
|
|
|
* @param array|Traversable $collection |
559
|
|
|
* @param callable $function ($value, $key) |
560
|
|
|
* @return bool |
561
|
|
|
*/ |
562
|
|
|
function every($collection, callable $function) |
563
|
|
|
{ |
564
|
1 |
|
foreach ($collection as $key => $value) { |
565
|
1 |
|
if (!$function($value, $key)) { |
566
|
1 |
|
return false; |
567
|
|
|
} |
568
|
1 |
|
} |
569
|
|
|
|
570
|
1 |
|
return true; |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
/** |
574
|
|
|
* Returns true if $function returns true for at least one item in $collection. |
575
|
|
|
* |
576
|
|
|
* @param array|Traversable $collection |
577
|
|
|
* @param callable $function ($value, $key) |
578
|
|
|
* @return bool |
579
|
|
|
*/ |
580
|
|
|
function some($collection, callable $function) |
581
|
|
|
{ |
582
|
1 |
|
foreach ($collection as $key => $value) { |
583
|
1 |
|
if ($function($value, $key)) { |
584
|
1 |
|
return true; |
585
|
|
|
} |
586
|
1 |
|
} |
587
|
|
|
|
588
|
1 |
|
return false; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
/** |
592
|
|
|
* Returns true if $needle is found in $collection values. |
593
|
|
|
* |
594
|
|
|
* @param $collection |
595
|
|
|
* @param mixed $needle |
596
|
|
|
* @return bool |
597
|
|
|
*/ |
598
|
|
|
function contains($collection, $needle) |
599
|
|
|
{ |
600
|
1 |
|
foreach ($collection as $key => $value) { |
601
|
1 |
|
if ($value === $needle) { |
602
|
1 |
|
return true; |
603
|
|
|
} |
604
|
1 |
|
} |
605
|
|
|
|
606
|
1 |
|
return false; |
607
|
|
|
} |
608
|
|
|
|
609
|
|
|
/** |
610
|
|
|
* Reduce that walks from right to the left. |
611
|
|
|
* |
612
|
|
|
* @param array|Traversable $collection |
613
|
|
|
* @param callable $function |
614
|
|
|
* @param mixed $startValue |
615
|
|
|
* @return mixed |
616
|
|
|
*/ |
617
|
|
|
function reduceRight($collection, callable $function, $startValue) |
618
|
|
|
{ |
619
|
1 |
|
return reduce(reverse($collection), $function, $startValue); |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
/** |
623
|
|
|
* Returns a lazy collection of first $numberOfItems items of $collection. |
624
|
|
|
* |
625
|
|
|
* @param array|Traversable $collection |
626
|
|
|
* @param int $numberOfItems |
627
|
|
|
* @return Collection |
628
|
|
|
*/ |
629
|
|
|
function take($collection, $numberOfItems) |
630
|
|
|
{ |
631
|
12 |
|
return slice($collection, 0, $numberOfItems); |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* Returns a lazy collection of all but first $numberOfItems items of $collection. |
636
|
|
|
* |
637
|
|
|
* @param array|Traversable $collection |
638
|
|
|
* @param int $numberOfItems |
639
|
|
|
* @return Collection |
640
|
|
|
*/ |
641
|
|
|
function drop($collection, $numberOfItems) |
642
|
|
|
{ |
643
|
4 |
|
return slice($collection, $numberOfItems); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Returns a lazy collection of values, where first value is $value and all subsequent values are computed by applying |
648
|
|
|
* $function to the last value in the collection. By default this produces an infinite collection. However you can |
649
|
|
|
* end the collection by throwing a NoMoreItems exception. |
650
|
|
|
* |
651
|
|
|
* @param mixed $value |
652
|
|
|
* @param callable $function ($value, $key) |
653
|
|
|
* @return Collection |
654
|
|
|
*/ |
655
|
|
|
function iterate($value, callable $function) |
656
|
|
|
{ |
657
|
4 |
|
$duplicated = duplicate($value); |
658
|
|
|
$generatorFactory = function () use ($duplicated, $function) { |
659
|
4 |
|
$value = $duplicated; |
660
|
|
|
|
661
|
4 |
|
yield $value; |
662
|
|
|
|
663
|
4 |
|
while (true) { |
664
|
|
|
try { |
665
|
4 |
|
$value = $function($value); |
666
|
4 |
|
yield $value; |
667
|
4 |
|
} catch (NoMoreItems $e) { |
668
|
2 |
|
break; |
669
|
|
|
} |
670
|
4 |
|
} |
671
|
4 |
|
}; |
672
|
|
|
|
673
|
4 |
|
return new Collection($generatorFactory); |
674
|
|
|
} |
675
|
|
|
|
676
|
|
|
/** |
677
|
|
|
* Returns a lazy collection of items from $collection for which $function returned true. |
678
|
|
|
* |
679
|
|
|
* @param array|Traversable $collection |
680
|
|
|
* @param callable $function ($value, $key) |
681
|
|
|
* @return Collection |
682
|
|
|
*/ |
683
|
|
|
function reject($collection, callable $function) |
684
|
|
|
{ |
685
|
2 |
|
return filter( |
686
|
2 |
|
$collection, |
687
|
|
|
function($value, $key) use ($function) { |
688
|
2 |
|
return !$function($value, $key); |
689
|
|
|
} |
690
|
2 |
|
); |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
/** |
694
|
|
|
* Returns a lazy collection of items in $collection without the last $numberOfItems items. |
695
|
|
|
* |
696
|
|
|
* @param array|Traversable $collection |
697
|
|
|
* @param int $numberOfItems |
698
|
|
|
* @return Collection |
699
|
|
|
*/ |
700
|
|
|
function dropLast($collection, $numberOfItems = 1) |
701
|
|
|
{ |
702
|
|
|
$generatorFactory = function () use ($collection, $numberOfItems) { |
703
|
1 |
|
$buffer = []; |
704
|
|
|
|
705
|
1 |
|
foreach ($collection as $key => $value) { |
706
|
1 |
|
$buffer[] = [$key, $value]; |
707
|
|
|
|
708
|
1 |
|
if (count($buffer) > $numberOfItems) { |
709
|
1 |
|
$val = array_shift($buffer); |
710
|
1 |
|
yield $val[0] => $val[1]; |
711
|
1 |
|
} |
712
|
1 |
|
} |
713
|
1 |
|
}; |
714
|
|
|
|
715
|
1 |
|
return new Collection($generatorFactory); |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
/** |
719
|
|
|
* Returns a lazy collection of items from $collection separated by $separator. |
720
|
|
|
* |
721
|
|
|
* @param array|Traversable $collection |
722
|
|
|
* @param mixed $separator |
723
|
|
|
* @return Collection |
724
|
|
|
*/ |
725
|
|
|
function interpose($collection, $separator) |
726
|
|
|
{ |
727
|
|
|
$generatorFactory = function () use ($collection, $separator) { |
728
|
1 |
|
foreach (take($collection, 1) as $key => $value) { |
729
|
1 |
|
yield $key => $value; |
730
|
1 |
|
} |
731
|
|
|
|
732
|
1 |
|
foreach (drop($collection, 1) as $key => $value) { |
733
|
1 |
|
yield $separator; |
734
|
1 |
|
yield $key => $value; |
735
|
1 |
|
} |
736
|
1 |
|
}; |
737
|
|
|
|
738
|
1 |
|
return new Collection($generatorFactory); |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
/** |
742
|
|
|
* Returns a lazy collection of first item from first collection, first item from second, second from first and |
743
|
|
|
* so on. Accepts any number of collections. |
744
|
|
|
* |
745
|
|
|
* @param array|Traversable ...$collections |
746
|
|
|
* @return Collection |
747
|
|
|
*/ |
748
|
|
|
function interleave(...$collections) |
749
|
|
|
{ |
750
|
|
|
$generatorFactory = function () use ($collections) { |
751
|
1 |
|
$normalizedCollection = array_map( |
752
|
|
|
function ($collection) { |
753
|
1 |
|
$traversable = new Collection($collection); |
754
|
1 |
|
$traversable->rewind(); |
755
|
1 |
|
return $traversable; |
756
|
1 |
|
}, |
757
|
|
|
$collections |
758
|
1 |
|
); |
759
|
|
|
|
760
|
|
|
do { |
761
|
1 |
|
$valid = false; |
762
|
1 |
|
foreach ($normalizedCollection as $collection) { |
763
|
1 |
|
if ($collection->valid()) { |
764
|
1 |
|
yield $collection->key() => $collection->current(); |
765
|
1 |
|
$collection->next(); |
766
|
1 |
|
$valid = true; |
767
|
1 |
|
} |
768
|
1 |
|
} |
769
|
1 |
|
} while ($valid); |
770
|
1 |
|
}; |
771
|
|
|
|
772
|
1 |
|
return new Collection($generatorFactory); |
773
|
|
|
} |
774
|
|
|
|
775
|
|
|
/** |
776
|
|
|
* Returns a lazy collection of items in $collection with $value added as first element. If $key is not provided |
777
|
|
|
* it will be next integer index. |
778
|
|
|
* |
779
|
|
|
* @param array|Traversable $collection |
780
|
|
|
* @param mixed $value |
781
|
|
|
* @param mixed|null $key |
782
|
|
|
* @return Collection |
783
|
|
|
*/ |
784
|
|
View Code Duplication |
function prepend($collection, $value, $key = null) |
|
|
|
|
785
|
|
|
{ |
786
|
|
|
$generatorFactory = function () use ($collection, $value, $key) { |
787
|
2 |
|
if ($key === null) { |
788
|
1 |
|
yield $value; |
789
|
1 |
|
} else { |
790
|
1 |
|
yield $key => $value; |
791
|
|
|
} |
792
|
|
|
|
793
|
2 |
|
foreach ($collection as $key => $value) { |
794
|
2 |
|
yield $key => $value; |
795
|
2 |
|
} |
796
|
2 |
|
}; |
797
|
|
|
|
798
|
2 |
|
return new Collection($generatorFactory); |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* Returns a lazy collection of items in $collection with $value added as last element. If $key is not provided |
803
|
|
|
* it will be next integer index. |
804
|
|
|
* |
805
|
|
|
* @param array|Traversable $collection |
806
|
|
|
* @param mixed $value |
807
|
|
|
* @param mixed|null $key |
808
|
|
|
* @return Collection |
809
|
|
|
*/ |
810
|
|
View Code Duplication |
function append($collection, $value, $key = null) |
|
|
|
|
811
|
|
|
{ |
812
|
|
|
$generatorFactory = function () use ($collection, $value, $key) { |
813
|
11 |
|
foreach ($collection as $k => $v) { |
814
|
11 |
|
yield $k => $v; |
815
|
11 |
|
} |
816
|
|
|
|
817
|
11 |
|
if ($key === null) { |
818
|
9 |
|
yield $value; |
819
|
9 |
|
} else { |
820
|
2 |
|
yield $key => $value; |
821
|
|
|
} |
822
|
11 |
|
}; |
823
|
|
|
|
824
|
11 |
|
return new Collection($generatorFactory); |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
/** |
828
|
|
|
* Returns a lazy collection by removing items from $collection until first item for which $function returns false. |
829
|
|
|
* |
830
|
|
|
* @param array|Traversable $collection |
831
|
|
|
* @param callable $function ($value, $key) |
832
|
|
|
* @return Collection |
833
|
|
|
*/ |
834
|
|
View Code Duplication |
function dropWhile($collection, callable $function) |
|
|
|
|
835
|
|
|
{ |
836
|
|
|
$generatorFactory = function () use ($collection, $function) { |
837
|
2 |
|
$shouldDrop = true; |
838
|
2 |
|
foreach ($collection as $key => $value) { |
839
|
2 |
|
if ($shouldDrop) { |
840
|
2 |
|
$shouldDrop = $function($value, $key); |
841
|
2 |
|
} |
842
|
|
|
|
843
|
2 |
|
if (!$shouldDrop) { |
844
|
2 |
|
yield $key => $value; |
845
|
2 |
|
} |
846
|
2 |
|
} |
847
|
2 |
|
}; |
848
|
|
|
|
849
|
2 |
|
return new Collection($generatorFactory); |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Returns a lazy collection of items from $collection until first item for which $function returns false. |
854
|
|
|
* |
855
|
|
|
* @param array|Traversable $collection |
856
|
|
|
* @param callable $function ($value, $key) |
857
|
|
|
* @return Collection |
858
|
|
|
*/ |
859
|
|
View Code Duplication |
function takeWhile($collection, callable $function) |
|
|
|
|
860
|
|
|
{ |
861
|
|
|
$generatorFactory = function () use ($collection, $function) { |
862
|
2 |
|
$shouldTake = true; |
863
|
2 |
|
foreach ($collection as $key => $value) { |
864
|
2 |
|
if ($shouldTake) { |
865
|
2 |
|
$shouldTake = $function($value, $key); |
866
|
2 |
|
} |
867
|
|
|
|
868
|
2 |
|
if ($shouldTake) { |
869
|
2 |
|
yield $key => $value; |
870
|
2 |
|
} |
871
|
2 |
|
} |
872
|
2 |
|
}; |
873
|
|
|
|
874
|
2 |
|
return new Collection($generatorFactory); |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
/** |
878
|
|
|
* Returns a lazy collection. A result of calling map and flatten(1) |
879
|
|
|
* |
880
|
|
|
* @param array|Traversable $collection |
881
|
|
|
* @param callable $function ($value, $key) |
882
|
|
|
* @return Collection |
883
|
|
|
*/ |
884
|
|
|
function mapcat($collection, callable $function) |
885
|
|
|
{ |
886
|
1 |
|
return flatten(map($collection, $function), 1); |
887
|
|
|
} |
888
|
|
|
|
889
|
|
|
/** |
890
|
|
|
* Returns a lazy collection [take($collection, $position), drop($collection, $position)] |
891
|
|
|
* |
892
|
|
|
* @param array|Traversable $collection |
893
|
|
|
* @param int $position |
894
|
|
|
* @return Collection |
895
|
|
|
*/ |
896
|
|
|
function splitAt($collection, $position) |
897
|
|
|
{ |
898
|
|
|
$generatorFactory = function () use ($collection, $position) { |
899
|
1 |
|
yield take($collection, $position); |
900
|
1 |
|
yield drop($collection, $position); |
901
|
1 |
|
}; |
902
|
|
|
|
903
|
1 |
|
return new Collection($generatorFactory); |
904
|
|
|
} |
905
|
|
|
|
906
|
|
|
/** |
907
|
|
|
* Returns a lazy collection [takeWhile($collection, $function), dropWhile($collection, $function)] |
908
|
|
|
* |
909
|
|
|
* @param array|Traversable $collection |
910
|
|
|
* @param callable $function ($value, $key) |
911
|
|
|
* @return Collection |
912
|
|
|
*/ |
913
|
|
|
function splitWith($collection, callable $function) |
914
|
|
|
{ |
915
|
|
|
$generatorFactory = function () use ($collection, $function) { |
916
|
1 |
|
yield takeWhile($collection, $function); |
917
|
1 |
|
yield dropWhile($collection, $function); |
918
|
1 |
|
}; |
919
|
|
|
|
920
|
1 |
|
return new Collection($generatorFactory); |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
/** |
924
|
|
|
* Returns a lazy collection with items from $collection but values that are found in keys of $replacementMap |
925
|
|
|
* are replaced by their values. |
926
|
|
|
* |
927
|
|
|
* @param array|Traversable $collection |
928
|
|
|
* @param array|Traversable $replacementMap |
929
|
|
|
* @return Collection |
930
|
|
|
*/ |
931
|
|
View Code Duplication |
function replace($collection, $replacementMap) |
|
|
|
|
932
|
|
|
{ |
933
|
|
|
$generatorFactory = function () use ($collection, $replacementMap) { |
934
|
1 |
|
foreach ($collection as $key => $value) { |
935
|
1 |
|
$newValue = getOrDefault($replacementMap, $value, $value); |
936
|
1 |
|
yield $key => $newValue; |
937
|
1 |
|
} |
938
|
1 |
|
}; |
939
|
|
|
|
940
|
1 |
|
return new Collection($generatorFactory); |
941
|
|
|
} |
942
|
|
|
|
943
|
|
|
/** |
944
|
|
|
* Returns a lazy collection of reduction steps. |
945
|
|
|
* |
946
|
|
|
* @param array|Traversable $collection |
947
|
|
|
* @param callable $function |
948
|
|
|
* @param mixed $startValue |
949
|
|
|
* @return Collection |
950
|
|
|
*/ |
951
|
|
View Code Duplication |
function reductions($collection, callable $function, $startValue) |
|
|
|
|
952
|
|
|
{ |
953
|
|
|
$generatorFactory = function () use ($collection, $function, $startValue) { |
954
|
1 |
|
$tmp = duplicate($startValue); |
955
|
|
|
|
956
|
1 |
|
yield $tmp; |
957
|
1 |
|
foreach ($collection as $key => $value) { |
958
|
1 |
|
$tmp = $function($tmp, $value, $key); |
959
|
1 |
|
yield $tmp; |
960
|
1 |
|
} |
961
|
1 |
|
}; |
962
|
|
|
|
963
|
1 |
|
return new Collection($generatorFactory); |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
/** |
967
|
|
|
* Returns a lazy collection of every nth ($step) item in $collection. |
968
|
|
|
* |
969
|
|
|
* @param array|Traversable $collection |
970
|
|
|
* @param int $step |
971
|
|
|
* @return Collection |
972
|
|
|
*/ |
973
|
|
|
function takeNth($collection, $step) |
974
|
|
|
{ |
975
|
|
|
$generatorFactory = function () use ($collection, $step) { |
976
|
1 |
|
$index = 0; |
977
|
1 |
|
foreach ($collection as $key => $value) { |
978
|
1 |
|
if ($index % $step == 0) { |
979
|
1 |
|
yield $key => $value; |
980
|
1 |
|
} |
981
|
|
|
|
982
|
1 |
|
$index++; |
983
|
1 |
|
} |
984
|
1 |
|
}; |
985
|
|
|
|
986
|
1 |
|
return new Collection($generatorFactory); |
987
|
|
|
} |
988
|
|
|
|
989
|
|
|
/** |
990
|
|
|
* Returns a lazy collection of collections of $numberOfItems items each, at $step step |
991
|
|
|
* apart. If $step is not supplied, defaults to $numberOfItems, i.e. the partitions |
992
|
|
|
* do not overlap. If a $padding collection is supplied, use its elements as |
993
|
|
|
* necessary to complete last partition up to $numberOfItems items. In case there are |
994
|
|
|
* not enough padding elements, return a partition with less than $numberOfItems items. |
995
|
|
|
* |
996
|
|
|
* @param array|Traversable $collection |
997
|
|
|
* @param $numberOfItems |
998
|
|
|
* @param int $step |
999
|
|
|
* @param array|Traversable $padding |
1000
|
|
|
* @return Collection |
1001
|
|
|
*/ |
1002
|
|
|
function partition($collection, $numberOfItems, $step = -1, $padding = []) |
1003
|
|
|
{ |
1004
|
|
|
$generatorFactory = function () use ($collection, $numberOfItems, $step, $padding) { |
1005
|
1 |
|
$buffer = []; |
1006
|
1 |
|
$itemsToSkip = 0; |
1007
|
1 |
|
$tmpStep = $step ?: $numberOfItems; |
1008
|
|
|
|
1009
|
1 |
|
foreach ($collection as $key => $value) { |
1010
|
1 |
|
if (count($buffer) == $numberOfItems) { |
1011
|
1 |
|
yield dereferenceKeyValue($buffer); |
1012
|
|
|
|
1013
|
1 |
|
$buffer = array_slice($buffer, $tmpStep); |
1014
|
1 |
|
$itemsToSkip = $tmpStep - $numberOfItems; |
1015
|
1 |
|
} |
1016
|
|
|
|
1017
|
1 |
|
if ($itemsToSkip <= 0) { |
1018
|
1 |
|
$buffer[] = [$key, $value]; |
1019
|
1 |
|
} else { |
1020
|
1 |
|
$itemsToSkip--; |
1021
|
|
|
} |
1022
|
1 |
|
} |
1023
|
|
|
|
1024
|
1 |
|
yield take( |
1025
|
1 |
|
concat(dereferenceKeyValue($buffer), $padding), |
1026
|
|
|
$numberOfItems |
1027
|
1 |
|
); |
1028
|
1 |
|
}; |
1029
|
|
|
|
1030
|
1 |
|
return new Collection($generatorFactory); |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
|
|
/** |
1034
|
|
|
* Returns a lazy collection created by partitioning $collection each time $function returned a different value. |
1035
|
|
|
* |
1036
|
|
|
* @param array|Traversable $collection |
1037
|
|
|
* @param callable $function |
1038
|
|
|
* @return Collection |
1039
|
|
|
*/ |
1040
|
|
|
function partitionBy($collection, callable $function) |
1041
|
|
|
{ |
1042
|
|
|
$generatorFactory = function () use ($collection, $function) { |
1043
|
1 |
|
$result = null; |
1044
|
1 |
|
$buffer = []; |
1045
|
|
|
|
1046
|
1 |
|
foreach ($collection as $key => $value) { |
1047
|
1 |
|
$newResult = $function($value, $key); |
1048
|
|
|
|
1049
|
1 |
|
if (!empty($buffer) && $result != $newResult) { |
1050
|
1 |
|
yield dereferenceKeyValue($buffer); |
1051
|
1 |
|
$buffer = []; |
1052
|
1 |
|
} |
1053
|
|
|
|
1054
|
1 |
|
$result = $newResult; |
1055
|
1 |
|
$buffer[] = [$key, $value]; |
1056
|
1 |
|
} |
1057
|
|
|
|
1058
|
1 |
|
if (!empty($buffer)) { |
1059
|
1 |
|
yield dereferenceKeyValue($buffer); |
1060
|
1 |
|
} |
1061
|
1 |
|
}; |
1062
|
|
|
|
1063
|
1 |
|
return new Collection($generatorFactory); |
1064
|
|
|
} |
1065
|
|
|
|
1066
|
|
|
/** |
1067
|
|
|
* Returns a lazy collection of $value repeated $times times. If $times is not provided the collection is infinite. |
1068
|
|
|
* |
1069
|
|
|
* @param mixed $value |
1070
|
|
|
* @param int $times |
1071
|
|
|
* @return Collection |
1072
|
|
|
*/ |
1073
|
|
|
function repeat($value, $times = -1) |
1074
|
|
|
{ |
1075
|
|
|
$generatorFactory = function () use ($value, $times) { |
1076
|
3 |
|
$tmpTimes = $times; |
1077
|
|
|
|
1078
|
3 |
|
while ($tmpTimes != 0) { |
1079
|
3 |
|
yield $value; |
1080
|
|
|
|
1081
|
3 |
|
$tmpTimes = $tmpTimes < 0 ? -1 : $tmpTimes - 1; |
1082
|
3 |
|
} |
1083
|
3 |
|
}; |
1084
|
|
|
|
1085
|
3 |
|
return new Collection($generatorFactory); |
1086
|
|
|
} |
1087
|
|
|
|
1088
|
|
|
/** |
1089
|
|
|
* Returns a lazy collection of numbers starting at $start, incremented by $step until $end is reached. |
1090
|
|
|
* |
1091
|
|
|
* @param int $start |
1092
|
|
|
* @param int|null $end |
1093
|
|
|
* @param int $step |
1094
|
|
|
* @return Collection |
1095
|
|
|
*/ |
1096
|
|
|
function range($start = 0, $end = null, $step = 1) |
1097
|
|
|
{ |
1098
|
|
|
$generatorFactory = function () use ($start, $end, $step) { |
1099
|
2 |
|
return iterate( |
1100
|
2 |
|
$start, |
1101
|
|
|
function($value) use ($step, $end) { |
1102
|
2 |
|
$result = $value + $step; |
1103
|
|
|
|
1104
|
2 |
|
if ($end !== null && $result > $end) { |
1105
|
1 |
|
throw new NoMoreItems; |
1106
|
|
|
} |
1107
|
|
|
|
1108
|
2 |
|
return $result; |
1109
|
|
|
} |
1110
|
2 |
|
); |
1111
|
2 |
|
}; |
1112
|
|
|
|
1113
|
2 |
|
return new Collection($generatorFactory); |
1114
|
|
|
} |
1115
|
|
|
|
1116
|
|
|
/** |
1117
|
|
|
* Returns true if $input is array or Traversable object. |
1118
|
|
|
* |
1119
|
|
|
* @param mixed $input |
1120
|
|
|
* @return bool |
1121
|
|
|
*/ |
1122
|
|
|
function isCollection($input) |
1123
|
|
|
{ |
1124
|
19 |
|
return is_array($input) || $input instanceof Traversable; |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
/** |
1128
|
|
|
* Returns duplicated/cloned $input that has no relation to the original one. Used for making sure there are no side |
1129
|
|
|
* effect in functions. |
1130
|
|
|
* |
1131
|
|
|
* @param $input |
1132
|
|
|
* @return mixed |
1133
|
|
|
*/ |
1134
|
|
|
function duplicate($input) |
1135
|
|
|
{ |
1136
|
8 |
|
if (is_array($input)) { |
1137
|
1 |
|
return toArray( |
1138
|
1 |
|
map( |
1139
|
1 |
|
$input, |
1140
|
|
|
function($i) { |
1141
|
1 |
|
return duplicate($i); |
1142
|
|
|
} |
1143
|
1 |
|
) |
1144
|
1 |
|
); |
1145
|
8 |
|
} elseif (is_object($input)) { |
1146
|
2 |
|
return clone $input; |
1147
|
|
|
} else { |
1148
|
8 |
|
return $input; |
1149
|
|
|
} |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
/** |
1153
|
|
|
* Transforms [[$key, $value], [$key2, $value2]] into [$key => $value, $key2 => $value2]. Used as a helper |
1154
|
|
|
* |
1155
|
|
|
* @param array|Traversable $collection |
1156
|
|
|
* @return Collection |
1157
|
|
|
*/ |
1158
|
|
|
function dereferenceKeyValue($collection) |
1159
|
|
|
{ |
1160
|
|
|
$generatorFactory = function () use ($collection) { |
1161
|
6 |
|
foreach ($collection as $value) { |
1162
|
6 |
|
yield $value[0] => $value[1]; |
1163
|
6 |
|
} |
1164
|
7 |
|
}; |
1165
|
|
|
|
1166
|
7 |
|
return new Collection($generatorFactory); |
1167
|
|
|
} |
1168
|
|
|
|
1169
|
|
|
/** |
1170
|
|
|
* Realizes collection - turns lazy collection into non-lazy one by iterating over it and storing the key/values. |
1171
|
|
|
* |
1172
|
|
|
* @param array|Traversable $collection |
1173
|
|
|
* @return Collection |
1174
|
|
|
*/ |
1175
|
|
|
function realize($collection) |
1176
|
|
|
{ |
1177
|
1 |
|
return dereferenceKeyValue( |
1178
|
1 |
|
toArray( |
1179
|
1 |
|
map( |
1180
|
1 |
|
$collection, |
1181
|
|
|
function ($value, $key) { |
1182
|
1 |
|
return [$key, $value]; |
1183
|
|
|
} |
1184
|
1 |
|
) |
1185
|
1 |
|
) |
1186
|
1 |
|
); |
1187
|
|
|
} |
1188
|
|
|
|
1189
|
|
|
/** |
1190
|
|
|
* Returns the second item of $collection or throws ItemNotFound if $collection is empty or has 1 item. |
1191
|
|
|
* |
1192
|
|
|
* @param array|Traversable $collection |
1193
|
|
|
* @return mixed |
1194
|
|
|
*/ |
1195
|
|
|
function second($collection) |
1196
|
|
|
{ |
1197
|
6 |
|
return get(values($collection), 1); |
1198
|
|
|
} |
1199
|
|
|
|
1200
|
|
|
|
1201
|
|
|
/** |
1202
|
|
|
* Combines $keys and $values into a lazy collection. The resulting collection has length equal to the size of smaller |
1203
|
|
|
* argument. |
1204
|
|
|
* |
1205
|
|
|
* @param array|Traversable $keys |
1206
|
|
|
* @param array|Traversable $values |
1207
|
|
|
* @return Collection |
1208
|
|
|
*/ |
1209
|
|
|
function combine($keys, $values) |
1210
|
|
|
{ |
1211
|
|
|
$generatorFactory = function () use ($keys, $values) { |
1212
|
1 |
|
$keyCollection = new Collection($keys); |
1213
|
1 |
|
$valueCollection = new Collection($values); |
1214
|
1 |
|
$valueCollection->rewind(); |
1215
|
|
|
|
1216
|
1 |
|
foreach ($keyCollection as $key) { |
1217
|
1 |
|
if (!$valueCollection->valid()) { |
1218
|
1 |
|
break; |
1219
|
|
|
} |
1220
|
|
|
|
1221
|
1 |
|
yield $key => $valueCollection->current(); |
1222
|
1 |
|
$valueCollection->next(); |
1223
|
1 |
|
} |
1224
|
1 |
|
}; |
1225
|
|
|
|
1226
|
1 |
|
return new Collection($generatorFactory); |
1227
|
|
|
} |
1228
|
|
|
|
1229
|
|
|
/** |
1230
|
|
|
* Returns a lazy collection without the items associated to any of the keys from $keys. |
1231
|
|
|
* |
1232
|
|
|
* @param array|Traversable $collection |
1233
|
|
|
* @param array|Traversable $keys |
1234
|
|
|
* @return Collection |
1235
|
|
|
*/ |
1236
|
|
|
function except($collection, $keys) |
1237
|
|
|
{ |
1238
|
1 |
|
$keys = toArray(values($keys)); |
1239
|
|
|
|
1240
|
1 |
|
return reject( |
1241
|
1 |
|
$collection, |
1242
|
|
|
function ($value, $key) use ($keys) { |
1243
|
1 |
|
return in_array($key, $keys); |
1244
|
|
|
} |
1245
|
1 |
|
); |
1246
|
|
|
} |
1247
|
|
|
|
1248
|
|
|
/** |
1249
|
|
|
* Returns a lazy collection of items associated to any of the keys from $keys. |
1250
|
|
|
* |
1251
|
|
|
* @param array|Traversable $collection |
1252
|
|
|
* @param array|Traversable $keys |
1253
|
|
|
* @return Collection |
1254
|
|
|
*/ |
1255
|
|
|
function only($collection, $keys) |
1256
|
|
|
{ |
1257
|
2 |
|
$keys = toArray(values($keys)); |
1258
|
|
|
|
1259
|
2 |
|
return filter( |
1260
|
2 |
|
$collection, |
1261
|
|
|
function ($value, $key) use ($keys) { |
1262
|
2 |
|
return in_array($key, $keys, true); |
1263
|
|
|
} |
1264
|
2 |
|
); |
1265
|
|
|
} |
1266
|
|
|
|
1267
|
|
|
/** |
1268
|
|
|
* Returns a lazy collection of items that are in $collection but are not in any of the other arguments. Note that the |
1269
|
|
|
* ...$collections are iterated non-lazily. |
1270
|
|
|
* |
1271
|
|
|
* @param array|Traversable $collection |
1272
|
|
|
* @param array|Traversable ...$collections |
1273
|
|
|
* @return Collection |
1274
|
|
|
*/ |
1275
|
|
View Code Duplication |
function diff($collection, ...$collections) |
|
|
|
|
1276
|
|
|
{ |
1277
|
1 |
|
$valuesToCompare = toArray(values(concat(...$collections))); |
1278
|
|
|
$generatorFactory = function () use ($collection, $valuesToCompare) { |
1279
|
1 |
|
foreach ($collection as $key => $value) { |
1280
|
1 |
|
if (!in_array($value, $valuesToCompare)) { |
1281
|
1 |
|
yield $key => $value; |
1282
|
1 |
|
} |
1283
|
1 |
|
} |
1284
|
1 |
|
}; |
1285
|
|
|
|
1286
|
1 |
|
return new Collection($generatorFactory); |
1287
|
|
|
} |
1288
|
|
|
|
1289
|
|
|
/** |
1290
|
|
|
* Returns a lazy collection of items that are in $collection and all the other arguments, indexed by the keys from the |
1291
|
|
|
* first collection. Note that the ...$collections are iterated non-lazily. |
1292
|
|
|
* |
1293
|
|
|
* @param array|Traversable $collection |
1294
|
|
|
* @param array|Traversable ...$collections |
1295
|
|
|
* @return Collection |
1296
|
|
|
*/ |
1297
|
|
View Code Duplication |
function intersect($collection, ...$collections) |
|
|
|
|
1298
|
|
|
{ |
1299
|
1 |
|
$valuesToCompare = toArray(values(concat(...$collections))); |
1300
|
|
|
$generatorFactory = function () use ($collection, $valuesToCompare) { |
1301
|
1 |
|
foreach ($collection as $key => $value) { |
1302
|
1 |
|
if (in_array($value, $valuesToCompare)) { |
1303
|
1 |
|
yield $key => $value; |
1304
|
1 |
|
} |
1305
|
1 |
|
} |
1306
|
1 |
|
}; |
1307
|
|
|
|
1308
|
1 |
|
return new Collection($generatorFactory); |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
|
|
/** |
1312
|
|
|
* Returns a lazy collection where keys and values are flipped. |
1313
|
|
|
* |
1314
|
|
|
* @param array|Traversable $collection |
1315
|
|
|
* @return Collection |
1316
|
|
|
*/ |
1317
|
|
|
function flip($collection) |
1318
|
|
|
{ |
1319
|
|
|
$generatorFactory = function () use ($collection) { |
1320
|
1 |
|
foreach ($collection as $key => $value) { |
1321
|
1 |
|
yield $value => $key; |
1322
|
1 |
|
} |
1323
|
1 |
|
}; |
1324
|
|
|
|
1325
|
1 |
|
return new Collection($generatorFactory); |
1326
|
|
|
} |
1327
|
|
|
|
1328
|
|
|
/** |
1329
|
|
|
* Checks for the existence of an item with key $key in $collection. |
1330
|
|
|
* |
1331
|
|
|
* @param array|Traversable $collection |
1332
|
|
|
* @param mixed $key |
1333
|
|
|
* @return bool |
1334
|
|
|
*/ |
1335
|
|
|
function has($collection, $key) |
1336
|
|
|
{ |
1337
|
|
|
try { |
1338
|
2 |
|
get($collection, $key); |
1339
|
2 |
|
return true; |
1340
|
2 |
|
} catch (ItemNotFound $e) { |
1341
|
2 |
|
return false; |
1342
|
|
|
} |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
/** |
1346
|
|
|
* Returns a lazy collection of non-lazy collections of items from nth position from each passed collection. Stops when |
1347
|
|
|
* any of the collections don't have an item at the nth position. |
1348
|
|
|
* |
1349
|
|
|
* @param array|Traversable[] ...$collections |
1350
|
|
|
* @return Collection |
1351
|
|
|
*/ |
1352
|
|
|
function zip(...$collections) |
1353
|
|
|
{ |
1354
|
1 |
|
$normalizedCollections = []; |
1355
|
1 |
|
foreach ($collections as $collection) { |
1356
|
1 |
|
$traversable = new Collection($collection); |
1357
|
1 |
|
$traversable->rewind(); |
1358
|
1 |
|
$normalizedCollections[] = $traversable; |
1359
|
1 |
|
} |
1360
|
|
|
|
1361
|
|
|
$generatorFactory = function () use ($normalizedCollections) { |
1362
|
1 |
|
while (true) { |
1363
|
1 |
|
$isMissingItems = false; |
1364
|
1 |
|
$zippedItem = new Collection([]); |
1365
|
|
|
|
1366
|
1 |
|
foreach ($normalizedCollections as $collection) { |
1367
|
1 |
|
if (!$collection->valid()) { |
1368
|
1 |
|
$isMissingItems = true; |
1369
|
1 |
|
break; |
1370
|
|
|
} |
1371
|
|
|
|
1372
|
1 |
|
$zippedItem = append($zippedItem, $collection->current(), $collection->key()); |
1373
|
1 |
|
$collection->next(); |
1374
|
1 |
|
} |
1375
|
|
|
|
1376
|
1 |
|
if (!$isMissingItems) { |
1377
|
1 |
|
yield $zippedItem; |
1378
|
1 |
|
} else { |
1379
|
1 |
|
break; |
1380
|
|
|
} |
1381
|
1 |
|
} |
1382
|
1 |
|
}; |
1383
|
|
|
|
1384
|
1 |
|
return new Collection($generatorFactory); |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
|
|
/** |
1388
|
|
|
* Returns a lazy collection of data extracted from $collection items by dot separated key path. Supports the * |
1389
|
|
|
* wildcard. If a key contains \ or * it must be escaped using \ character. |
1390
|
|
|
* |
1391
|
|
|
* @param array|Traversable $collection |
1392
|
|
|
* @param mixed $keyPath |
1393
|
|
|
* @return Collection |
1394
|
|
|
*/ |
1395
|
|
|
function extract($collection, $keyPath) |
1396
|
|
|
{ |
1397
|
1 |
|
preg_match_all('/(.*[^\\\])(?:\.|$)/U', $keyPath, $matches); |
1398
|
1 |
|
$pathParts = $matches[1]; |
1399
|
|
|
|
1400
|
|
|
$extractor = function ($coll) use ($pathParts) { |
1401
|
1 |
|
foreach ($pathParts as $pathPart) { |
1402
|
1 |
|
$coll = flatten(filter($coll, '\DusanKasan\Knapsack\isCollection'), 1); |
1403
|
|
|
|
1404
|
1 |
|
if ($pathPart != '*') { |
1405
|
1 |
|
$pathPart = str_replace(['\.', '\*'], ['.', '*'], $pathPart); |
1406
|
1 |
|
$coll = values(only($coll, [$pathPart])); |
1407
|
1 |
|
} |
1408
|
1 |
|
} |
1409
|
|
|
|
1410
|
1 |
|
return $coll; |
1411
|
1 |
|
}; |
1412
|
|
|
|
1413
|
|
|
$generatorFactory = function () use ($collection, $extractor) { |
1414
|
1 |
|
foreach ($collection as $item) { |
1415
|
1 |
|
foreach ($extractor([$item]) as $extracted) { |
1416
|
1 |
|
yield $extracted; |
1417
|
1 |
|
} |
1418
|
1 |
|
} |
1419
|
1 |
|
}; |
1420
|
|
|
|
1421
|
|
|
|
1422
|
1 |
|
return new Collection($generatorFactory); |
1423
|
|
|
} |
1424
|
|
|
|
1425
|
|
|
/** |
1426
|
|
|
* Checks whether $collection has exactly $size items. |
1427
|
|
|
* |
1428
|
|
|
* @param array|Traversable $collection |
1429
|
|
|
* @param int $size |
1430
|
|
|
* @return bool |
1431
|
|
|
*/ |
1432
|
|
View Code Duplication |
function sizeIs($collection, $size) |
|
|
|
|
1433
|
|
|
{ |
1434
|
1 |
|
$itemsTempCount = 0; |
1435
|
|
|
|
1436
|
1 |
|
foreach ($collection as $key => $value) { |
1437
|
1 |
|
$itemsTempCount++; |
1438
|
|
|
|
1439
|
1 |
|
if ($itemsTempCount > $size) { |
1440
|
1 |
|
return false; |
1441
|
|
|
} |
1442
|
1 |
|
} |
1443
|
|
|
|
1444
|
1 |
|
return $itemsTempCount == $size; |
1445
|
|
|
} |
1446
|
|
|
|
1447
|
|
|
/** |
1448
|
|
|
* Checks whether $collection has less than $size items. |
1449
|
|
|
* |
1450
|
|
|
* @param array|Traversable $collection |
1451
|
|
|
* @param int $size |
1452
|
|
|
* @return bool |
1453
|
|
|
*/ |
1454
|
|
View Code Duplication |
function sizeIsLessThan($collection, $size) |
|
|
|
|
1455
|
|
|
{ |
1456
|
1 |
|
$itemsTempCount = 0; |
1457
|
|
|
|
1458
|
1 |
|
foreach ($collection as $key => $value) { |
1459
|
1 |
|
$itemsTempCount++; |
1460
|
|
|
|
1461
|
1 |
|
if ($itemsTempCount > $size) { |
1462
|
1 |
|
return false; |
1463
|
|
|
} |
1464
|
1 |
|
} |
1465
|
|
|
|
1466
|
1 |
|
return $itemsTempCount < $size; |
1467
|
|
|
} |
1468
|
|
|
|
1469
|
|
|
/** |
1470
|
|
|
* Checks whether $collection has more than $size items. |
1471
|
|
|
* |
1472
|
|
|
* @param array|Traversable $collection |
1473
|
|
|
* @param int $size |
1474
|
|
|
* @return bool |
1475
|
|
|
*/ |
1476
|
|
View Code Duplication |
function sizeIsGreaterThan($collection, $size) |
|
|
|
|
1477
|
|
|
{ |
1478
|
1 |
|
$itemsTempCount = 0; |
1479
|
|
|
|
1480
|
1 |
|
foreach ($collection as $key => $value) { |
1481
|
1 |
|
$itemsTempCount++; |
1482
|
|
|
|
1483
|
1 |
|
if ($itemsTempCount > $size) { |
1484
|
1 |
|
return true; |
1485
|
|
|
} |
1486
|
1 |
|
} |
1487
|
|
|
|
1488
|
1 |
|
return $itemsTempCount > $size; |
1489
|
|
|
} |
1490
|
|
|
|
1491
|
|
|
/** |
1492
|
|
|
* Checks whether $collection has between $fromSize to $toSize items. $toSize can be |
1493
|
|
|
* smaller than $fromSize. |
1494
|
|
|
* |
1495
|
|
|
* @param array|Traversable $collection |
1496
|
|
|
* @param int $fromSize |
1497
|
|
|
* @param int $toSize |
1498
|
|
|
* @return bool |
1499
|
|
|
*/ |
1500
|
|
|
function sizeIsBetween($collection, $fromSize, $toSize) |
1501
|
|
|
{ |
1502
|
1 |
|
if ($fromSize > $toSize) { |
1503
|
1 |
|
$tmp = $toSize; |
1504
|
1 |
|
$toSize = $fromSize; |
1505
|
1 |
|
$fromSize = $tmp; |
1506
|
1 |
|
} |
1507
|
|
|
|
1508
|
1 |
|
$itemsTempCount = 0; |
1509
|
1 |
|
foreach ($collection as $key => $value) { |
1510
|
1 |
|
$itemsTempCount++; |
1511
|
|
|
|
1512
|
1 |
|
if ($itemsTempCount > $toSize) { |
1513
|
1 |
|
return false; |
1514
|
|
|
} |
1515
|
1 |
|
} |
1516
|
|
|
|
1517
|
1 |
|
return $fromSize < $itemsTempCount && $itemsTempCount < $toSize; |
1518
|
|
|
} |
1519
|
|
|
|
1520
|
|
|
/** |
1521
|
|
|
* Returns a sum of all values in the $collection. |
1522
|
|
|
* |
1523
|
|
|
* @param array|Traversable $collection |
1524
|
|
|
* @return int|float |
1525
|
|
|
*/ |
1526
|
|
|
function sum($collection) |
1527
|
|
|
{ |
1528
|
1 |
|
$result = 0; |
1529
|
|
|
|
1530
|
1 |
|
foreach ($collection as $value) { |
1531
|
1 |
|
$result += $value; |
1532
|
1 |
|
} |
1533
|
|
|
|
1534
|
1 |
|
return $result; |
1535
|
|
|
} |
1536
|
|
|
|
1537
|
|
|
/** |
1538
|
|
|
* Returns average of values from $collection. |
1539
|
|
|
* |
1540
|
|
|
* @param array|Traversable $collection |
1541
|
|
|
* @return int|float |
1542
|
|
|
*/ |
1543
|
|
|
function average($collection) |
1544
|
|
|
{ |
1545
|
2 |
|
$sum = 0; |
1546
|
2 |
|
$count = 0; |
1547
|
|
|
|
1548
|
2 |
|
foreach ($collection as $value) { |
1549
|
1 |
|
$sum += $value; |
1550
|
1 |
|
$count++; |
1551
|
2 |
|
} |
1552
|
|
|
|
1553
|
2 |
|
return $count ? $sum/$count : 0; |
1554
|
|
|
} |
1555
|
|
|
|
1556
|
|
|
/** |
1557
|
|
|
* Returns maximal value from $collection. |
1558
|
|
|
* |
1559
|
|
|
* @param array|Traversable $collection |
1560
|
|
|
* @return mixed |
1561
|
|
|
*/ |
1562
|
|
|
function max($collection) |
1563
|
|
|
{ |
1564
|
2 |
|
$result = null; |
1565
|
|
|
|
1566
|
2 |
|
foreach ($collection as $value) { |
1567
|
1 |
|
$result = $value > $result ? $value : $result; |
1568
|
2 |
|
} |
1569
|
|
|
|
1570
|
2 |
|
return $result; |
1571
|
|
|
} |
1572
|
|
|
|
1573
|
|
|
/** |
1574
|
|
|
* Returns minimal value from $collection. |
1575
|
|
|
* |
1576
|
|
|
* @param array|Traversable $collection |
1577
|
|
|
* @return mixed |
1578
|
|
|
*/ |
1579
|
|
|
function min($collection) |
1580
|
|
|
{ |
1581
|
2 |
|
$result = null; |
1582
|
2 |
|
$hasItem = false; |
1583
|
|
|
|
1584
|
2 |
|
foreach ($collection as $value) { |
1585
|
1 |
|
if (!$hasItem) { |
1586
|
1 |
|
$hasItem = true; |
1587
|
1 |
|
$result = $value; |
1588
|
1 |
|
} |
1589
|
|
|
|
1590
|
1 |
|
$result = $value < $result ? $value : $result; |
1591
|
2 |
|
} |
1592
|
|
|
|
1593
|
2 |
|
return $result; |
1594
|
|
|
} |
1595
|
|
|
|
1596
|
|
|
/** |
1597
|
|
|
* Returns a string by concatenating the $collection values into a string. |
1598
|
|
|
* |
1599
|
|
|
* @param array|Traversable $collection |
1600
|
|
|
* @return string |
1601
|
|
|
*/ |
1602
|
|
|
function toString($collection) |
1603
|
|
|
{ |
1604
|
1 |
|
$result = ''; |
1605
|
|
|
|
1606
|
1 |
|
foreach ($collection as $value) { |
1607
|
1 |
|
$result .= (string) $value; |
1608
|
1 |
|
} |
1609
|
|
|
|
1610
|
1 |
|
return $result; |
1611
|
|
|
} |
1612
|
|
|
|
1613
|
|
|
|
1614
|
|
|
/** |
1615
|
|
|
* Returns a lazy collection with items from $collection, but items with keys that are found in keys of $replacementMap |
1616
|
|
|
* are replaced by their values. |
1617
|
|
|
* |
1618
|
|
|
* @param array|Traversable $collection |
1619
|
|
|
* @param array|Traversable $replacementMap |
1620
|
|
|
* @return Collection |
1621
|
|
|
*/ |
1622
|
|
View Code Duplication |
function replaceByKeys($collection, $replacementMap) |
|
|
|
|
1623
|
|
|
{ |
1624
|
|
|
$generatorFactory = function () use ($collection, $replacementMap) { |
1625
|
1 |
|
foreach ($collection as $key => $value) { |
1626
|
1 |
|
$newValue = getOrDefault($replacementMap, $key, $value); |
1627
|
1 |
|
yield $key => $newValue; |
1628
|
1 |
|
} |
1629
|
1 |
|
}; |
1630
|
|
|
|
1631
|
1 |
|
return new Collection($generatorFactory); |
1632
|
|
|
} |
1633
|
|
|
|
1634
|
|
|
/** |
1635
|
|
|
* Dumps a variable into scalar or array (recursively). |
1636
|
|
|
* |
1637
|
|
|
* - scalars are returned as they are, |
1638
|
|
|
* - array of class name => properties (name => value) is returned for objects, |
1639
|
|
|
* - arrays or Traversables are returned as arrays, |
1640
|
|
|
* - for anything else result of calling gettype($input) is returned |
1641
|
|
|
* |
1642
|
|
|
* If specified, $maxItemsPerCollection will only leave specified number of items in collection, |
1643
|
|
|
* appending a new element at end '>>>' if original collection was longer. |
1644
|
|
|
* |
1645
|
|
|
* If specified, $maxDepth will only leave specified n levels of nesting, replacing elements |
1646
|
|
|
* with '^^^' once the maximum nesting level was reached. |
1647
|
|
|
* |
1648
|
|
|
* If a collection with duplicate keys is encountered, the duplicate keys (except the first one) |
1649
|
|
|
* will be change into a format originalKey//duplicateCounter where duplicateCounter starts from |
1650
|
|
|
* 1 at the first duplicate. So [0 => 1, 0 => 2] will become [0 => 1, '0//1' => 2] |
1651
|
|
|
* |
1652
|
|
|
* @param mixed $input |
1653
|
|
|
* @param int|null $maxItemsPerCollection |
1654
|
|
|
* @param int|null $maxDepth |
1655
|
|
|
* @return array|mixed |
1656
|
|
|
*/ |
1657
|
|
|
function dump($input, $maxItemsPerCollection = null, $maxDepth = null) |
1658
|
|
|
{ |
1659
|
1 |
|
if (is_scalar($input)) { |
1660
|
1 |
|
return $input; |
1661
|
|
|
} |
1662
|
|
|
|
1663
|
1 |
|
if (isCollection($input)) { |
1664
|
1 |
|
if ($maxDepth === 0) { |
1665
|
1 |
|
return '^^^'; |
1666
|
|
|
} |
1667
|
|
|
|
1668
|
1 |
|
$normalizedProperties = []; |
1669
|
1 |
|
foreach ($input as $key => $value) { |
|
|
|
|
1670
|
1 |
|
if ($maxItemsPerCollection && count($normalizedProperties) >= $maxItemsPerCollection) { |
|
|
|
|
1671
|
1 |
|
$normalizedProperties[] = '>>>'; |
1672
|
1 |
|
break; |
1673
|
|
|
} |
1674
|
|
|
|
1675
|
1 |
|
for ($affix = 0; true; $affix++) { |
1676
|
1 |
|
$betterKey = $affix ? "$key//$affix" : $key; |
1677
|
1 |
|
if (!array_key_exists($betterKey, $normalizedProperties)) { |
1678
|
1 |
|
$normalizedProperties[$betterKey] = dump( |
1679
|
1 |
|
$value, |
1680
|
1 |
|
$maxItemsPerCollection, |
1681
|
1 |
|
$maxDepth>0 ? $maxDepth-1 : null |
1682
|
1 |
|
); |
1683
|
|
|
|
1684
|
1 |
|
break; |
1685
|
|
|
} |
1686
|
1 |
|
} |
1687
|
1 |
|
} |
1688
|
|
|
|
1689
|
1 |
|
return $normalizedProperties; |
1690
|
|
|
} |
1691
|
|
|
|
1692
|
1 |
|
if (is_object($input)) { |
1693
|
1 |
|
if ($maxDepth === 0) { |
1694
|
|
|
return '^^^'; |
1695
|
|
|
} |
1696
|
|
|
|
1697
|
1 |
|
$reflection = new \ReflectionObject($input); |
1698
|
1 |
|
$normalizedProperties = []; |
1699
|
1 |
|
foreach ($reflection->getProperties() as $property) { |
1700
|
1 |
|
$normalizedProperties[$property->getName()] = $property->getValue($input); |
1701
|
1 |
|
} |
1702
|
1 |
|
return [get_class($input) => dump($normalizedProperties, null, $maxDepth>0 ? $maxDepth-1 : null)]; |
1703
|
|
|
} |
1704
|
|
|
|
1705
|
|
|
return gettype($input); |
1706
|
|
|
} |
1707
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.