1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
namespace Narrowspark\Collection; |
4
|
|
|
|
5
|
|
|
use ArrayAccess; |
6
|
|
|
use ArrayIterator; |
7
|
|
|
use BadMethodCallException; |
8
|
|
|
use CachingIterator; |
9
|
|
|
use Closure; |
10
|
|
|
use Countable; |
11
|
|
|
use InvalidArgumentException; |
12
|
|
|
use Iterator; |
13
|
|
|
use IteratorAggregate; |
14
|
|
|
use JsonSerializable; |
15
|
|
|
use Narrowspark\Arr\Arr; |
16
|
|
|
use Serializable; |
17
|
|
|
use Traversable; |
18
|
|
|
|
19
|
|
|
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Serializable |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* The registered string extensions. |
23
|
|
|
* |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
protected static $extensions = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* The items contained in the collection. |
30
|
|
|
* |
31
|
|
|
* @var array |
32
|
|
|
*/ |
33
|
|
|
protected $items = []; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Create a new collection. |
37
|
|
|
* |
38
|
|
|
* @param callable|\Closure|array|Traversable\Iterator|self|IteratorAggregate|JsonSerializable $items |
39
|
|
|
*/ |
40
|
154 |
|
public function __construct($items = []) |
41
|
|
|
{ |
42
|
154 |
|
$this->items = $this->getArrayableItems($items); |
43
|
154 |
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Convert the collection to its string representation. |
47
|
|
|
* |
48
|
|
|
* @return string |
49
|
|
|
*/ |
50
|
1 |
|
public function __toString() |
51
|
|
|
{ |
52
|
1 |
|
return $this->toJson(); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Dynamically handle calls to the class. |
57
|
|
|
* |
58
|
|
|
* @param string $method |
59
|
|
|
* @param array $parameters |
60
|
|
|
* |
61
|
|
|
* @throws \BadMethodCallException |
62
|
|
|
* |
63
|
|
|
* @return mixed |
64
|
|
|
*/ |
65
|
1 |
View Code Duplication |
public static function __callStatic($method, $parameters) |
|
|
|
|
66
|
|
|
{ |
67
|
1 |
|
if (! static::hasExtensions($method)) { |
68
|
|
|
throw new BadMethodCallException("Method {$method} does not exist."); |
|
|
|
|
69
|
|
|
} |
70
|
|
|
|
71
|
1 |
|
if (static::$extensions[$method] instanceof Closure) { |
72
|
1 |
|
return call_user_func_array(Closure::bind(static::$extensions[$method], null, static::class), $parameters); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
return call_user_func_array(static::$extensions[$method], $parameters); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Dynamically handle calls to the class. |
80
|
|
|
* |
81
|
|
|
* @param string $method |
82
|
|
|
* @param array $parameters |
83
|
|
|
* |
84
|
|
|
* @throws \BadMethodCallException |
85
|
|
|
* |
86
|
|
|
* @return mixed |
87
|
|
|
*/ |
88
|
2 |
View Code Duplication |
public function __call($method, $parameters) |
|
|
|
|
89
|
|
|
{ |
90
|
2 |
|
if (! static::hasExtensions($method)) { |
91
|
|
|
throw new BadMethodCallException("Method {$method} does not exist."); |
|
|
|
|
92
|
|
|
} |
93
|
|
|
|
94
|
2 |
|
if (static::$extensions[$method] instanceof Closure) { |
95
|
2 |
|
return call_user_func_array(static::$extensions[$method]->bindTo($this, static::class), $parameters); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return call_user_func_array(static::$extensions[$method], $parameters); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Static alias of normal constructor. |
103
|
|
|
* |
104
|
|
|
* @param callable|\Closure|array|Traversable\Iterator|self|IteratorAggregate|JsonSerializable $items |
105
|
|
|
* |
106
|
|
|
* @return $this |
107
|
|
|
*/ |
108
|
5 |
|
public static function from($items = []): Collection |
109
|
|
|
{ |
110
|
5 |
|
return new self($items); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Get the average value of a given key. |
115
|
|
|
* |
116
|
|
|
* @param callable|string|null $callback |
117
|
|
|
* |
118
|
|
|
* @return int|float|null |
119
|
|
|
*/ |
120
|
4 |
|
public function average($callback = null) |
121
|
|
|
{ |
122
|
4 |
|
if ($count = $this->count()) { |
123
|
4 |
|
return $this->sum($callback) / $count; |
124
|
|
|
} |
125
|
1 |
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Get the median of a given key. |
129
|
|
|
* |
130
|
|
|
* @param null $key |
131
|
|
|
* |
132
|
|
|
* @return mixed|null |
133
|
|
|
*/ |
134
|
5 |
|
public function median($key = null) |
135
|
|
|
{ |
136
|
5 |
|
$count = $this->count(); |
137
|
|
|
|
138
|
5 |
|
if ($count == 0) { |
139
|
1 |
|
return; |
140
|
|
|
} |
141
|
|
|
|
142
|
4 |
|
$collection = isset($key) ? $this->pluck($key) : $this; |
143
|
4 |
|
$values = $collection->sort()->values(); |
144
|
|
|
|
145
|
4 |
|
$middle = (int) ($count / 2); |
146
|
|
|
|
147
|
4 |
|
if ($count % 2) { |
148
|
1 |
|
return $values->get($middle); |
149
|
|
|
} |
150
|
|
|
|
151
|
3 |
|
return (new static([ |
152
|
3 |
|
$values->get($middle - 1), $values->get($middle), |
153
|
3 |
|
]))->average(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Get the mode of a given key. |
158
|
|
|
* |
159
|
|
|
* @param string|int|null $key |
160
|
|
|
* |
161
|
|
|
* @return array|null |
162
|
|
|
*/ |
163
|
4 |
|
public function mode($key = null) |
164
|
|
|
{ |
165
|
4 |
|
$count = $this->count(); |
166
|
|
|
|
167
|
4 |
|
if ($count == 0) { |
168
|
1 |
|
return; |
169
|
|
|
} |
170
|
|
|
|
171
|
3 |
|
$collection = isset($key) ? $this->pluck($key) : $this; |
172
|
3 |
|
$counts = new self(); |
173
|
|
|
|
174
|
|
|
$collection->each(function ($value) use ($counts) { |
175
|
3 |
|
$counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; |
176
|
3 |
|
}); |
177
|
|
|
|
178
|
3 |
|
$sorted = $counts->sort(); |
179
|
3 |
|
$highestValue = $sorted->last(); |
180
|
|
|
|
181
|
|
|
return $sorted->filter(function ($key, $value) use ($highestValue) { |
182
|
3 |
|
return $value == $highestValue; |
183
|
3 |
|
})->sort()->keys()->all(); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Get the first item from the collection. |
188
|
|
|
* |
189
|
|
|
* @param callable|null $callback |
190
|
|
|
* @param mixed $default |
191
|
|
|
* |
192
|
|
|
* @return mixed |
193
|
|
|
*/ |
194
|
9 |
|
public function first(callable $callback = null, $default = null) |
195
|
|
|
{ |
196
|
9 |
|
return Arr::first($this->items, $callback, $default); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Get a flattened array of the items in the collection. |
201
|
|
|
* |
202
|
|
|
* @param int|float|string $depth |
203
|
|
|
* |
204
|
|
|
* @return static |
205
|
|
|
*/ |
206
|
3 |
|
public function flatten($depth = INF): Collection |
207
|
|
|
{ |
208
|
3 |
|
return new static(self::flattenCallback($this->items, $depth)); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Flip the items in the collection. |
213
|
|
|
* |
214
|
|
|
* @return static |
215
|
|
|
*/ |
216
|
1 |
|
public function flip(): Collection |
217
|
|
|
{ |
218
|
1 |
|
return new static(array_flip($this->items)); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Get all items except for those with the specified keys. |
223
|
|
|
* |
224
|
|
|
* @param mixed $keys |
225
|
|
|
* |
226
|
|
|
* @return static |
227
|
|
|
*/ |
228
|
1 |
|
public function except($keys): Collection |
229
|
|
|
{ |
230
|
1 |
|
$keys = is_array($keys) ? $keys : func_get_args(); |
231
|
|
|
|
232
|
1 |
|
return new static(Arr::except($this->items, $keys)); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Run a filter over each of the items. |
237
|
|
|
* |
238
|
|
|
* @param callable|null $callback |
239
|
|
|
* |
240
|
|
|
* @return static |
241
|
|
|
*/ |
242
|
13 |
|
public function filter(callable $callback = null): Collection |
243
|
|
|
{ |
244
|
13 |
|
if ($callback) { |
245
|
13 |
|
return new static(Arr::where($this->items, $callback)); |
246
|
|
|
} |
247
|
|
|
|
248
|
1 |
|
return new static(array_filter($this->items)); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Get all of the items in the collection. |
253
|
|
|
* |
254
|
|
|
* @return array |
255
|
|
|
*/ |
256
|
84 |
|
public function all(): array |
257
|
|
|
{ |
258
|
84 |
|
return $this->items; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Merge the collection with the given items. |
263
|
|
|
* |
264
|
|
|
* @param callable|Closure|array|Traversable\Iterator|self|IteratorAggregate|JsonSerializable $items |
265
|
|
|
* |
266
|
|
|
* @return static |
267
|
|
|
*/ |
268
|
3 |
|
public function merge($items): Collection |
269
|
|
|
{ |
270
|
3 |
|
return new static(array_merge($this->items, $this->getArrayableItems($items))); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Returns a new collection with $value added as last element. If $key is not provided |
275
|
|
|
* it will be next integer index. |
276
|
|
|
* |
277
|
|
|
* @param mixed $value |
278
|
|
|
* @param mixed $key |
279
|
|
|
* |
280
|
|
|
* @return static |
281
|
|
|
*/ |
282
|
1 |
|
public function append($value, $key = null): Collection |
283
|
|
|
{ |
284
|
1 |
|
$items = $this->items; |
285
|
|
|
|
286
|
1 |
|
if ($key === null) { |
287
|
1 |
|
$items[] = $value; |
288
|
|
|
} else { |
289
|
|
|
$items[$key] = $value; |
290
|
|
|
} |
291
|
|
|
|
292
|
1 |
|
return new static($items); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Reset the keys on the underlying array. |
297
|
|
|
* |
298
|
|
|
* @return static |
299
|
|
|
*/ |
300
|
24 |
|
public function values(): Collection |
301
|
|
|
{ |
302
|
24 |
|
return new static(array_values($this->items)); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Determine if an item exists in the collection. |
307
|
|
|
* |
308
|
|
|
* @param mixed $key |
309
|
|
|
* @param mixed $value |
310
|
|
|
* |
311
|
|
|
* @return bool |
312
|
|
|
*/ |
313
|
2 |
View Code Duplication |
public function contains($key, $value = null): bool |
|
|
|
|
314
|
|
|
{ |
315
|
2 |
|
if (func_num_args() == 2) { |
316
|
|
|
return $this->contains(function ($itemKey, $item) use ($key, $value) { |
317
|
1 |
|
return self::dataGet($item, $key) == $value; |
318
|
1 |
|
}); |
319
|
|
|
} |
320
|
|
|
|
321
|
2 |
|
if ($this->useAsCallable($key)) { |
322
|
2 |
|
return ! is_null($this->first($key)); |
323
|
|
|
} |
324
|
|
|
|
325
|
1 |
|
return in_array($key, $this->items); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* Determine if an item exists in the collection using strict comparison. |
330
|
|
|
* |
331
|
|
|
* @param mixed $key |
332
|
|
|
* @param mixed $value |
333
|
|
|
* |
334
|
|
|
* @return bool |
335
|
|
|
*/ |
336
|
1 |
View Code Duplication |
public function containsStrict($key, $value = null): bool |
|
|
|
|
337
|
|
|
{ |
338
|
1 |
|
if (func_num_args() == 2) { |
339
|
|
|
return $this->contains(function ($itemKey, $item) use ($key, $value) { |
340
|
1 |
|
return self::dataGet($item, $key) === $value; |
341
|
1 |
|
}); |
342
|
|
|
} |
343
|
|
|
|
344
|
1 |
|
if ($this->useAsCallable($key)) { |
345
|
1 |
|
return ! is_null($this->first($key)); |
346
|
|
|
} |
347
|
|
|
|
348
|
1 |
|
return in_array($key, $this->items, true); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Get the items in the collection that are not present in the given items. |
353
|
|
|
* |
354
|
|
|
* @param mixed $items |
355
|
|
|
* |
356
|
|
|
* @return static |
357
|
|
|
*/ |
358
|
2 |
|
public function diff($items): Collection |
359
|
|
|
{ |
360
|
2 |
|
return new static(array_diff($this->items, $this->getArrayableItems($items))); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Get the items in the collection whose keys are not present in the given items. |
365
|
|
|
* |
366
|
|
|
* @param mixed $items |
367
|
|
|
* |
368
|
|
|
* @return static |
369
|
|
|
*/ |
370
|
1 |
|
public function diffKeys($items): Collection |
371
|
|
|
{ |
372
|
1 |
|
return new static(array_diff_key($this->items, $this->getArrayableItems($items))); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Execute a callback over each item. |
377
|
|
|
* |
378
|
|
|
* @param callable $callback |
379
|
|
|
* |
380
|
|
|
* @return $this |
381
|
|
|
*/ |
382
|
4 |
|
public function each(callable $callback): Collection |
383
|
|
|
{ |
384
|
4 |
|
foreach ($this->items as $key => $item) { |
385
|
4 |
|
if ($callback($item, $key) === false) { |
386
|
4 |
|
break; |
387
|
|
|
} |
388
|
|
|
} |
389
|
|
|
|
390
|
4 |
|
return $this; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Create a new collection consisting of every n-th element. |
395
|
|
|
* |
396
|
|
|
* @param int $step |
397
|
|
|
* @param int $offset |
398
|
|
|
* |
399
|
|
|
* @return static |
400
|
|
|
*/ |
401
|
1 |
|
public function every($step, $offset = 0): Collection |
402
|
|
|
{ |
403
|
1 |
|
$new = []; |
404
|
1 |
|
$position = 0; |
405
|
|
|
|
406
|
1 |
|
foreach ($this->items as $item) { |
407
|
1 |
|
if ($position % $step === $offset) { |
408
|
1 |
|
$new[] = $item; |
409
|
|
|
} |
410
|
|
|
|
411
|
1 |
|
++$position; |
412
|
|
|
} |
413
|
|
|
|
414
|
1 |
|
return new static($new); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Filter items by the given key value pair. |
419
|
|
|
* |
420
|
|
|
* @param string $key |
421
|
|
|
* @param mixed $operator |
422
|
|
|
* @param mixed $value |
423
|
|
|
* |
424
|
|
|
* @return static |
425
|
|
|
*/ |
426
|
2 |
|
public function where(string $key, $operator, $value = null): Collection |
427
|
|
|
{ |
428
|
2 |
|
if (func_num_args() === 2) { |
429
|
1 |
|
$value = $operator; |
430
|
1 |
|
$operator = '='; |
431
|
|
|
} |
432
|
|
|
|
433
|
2 |
|
return $this->filter($this->operatorForWhere($key, $operator, $value)); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* Filter items by the given key value pair using strict comparison. |
438
|
|
|
* |
439
|
|
|
* @param string $key |
440
|
|
|
* @param mixed $value |
441
|
|
|
* |
442
|
|
|
* @return static |
443
|
|
|
*/ |
444
|
1 |
|
public function whereStrict(string $key, $value): Collection |
445
|
|
|
{ |
446
|
1 |
|
return $this->where($key, '===', $value); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Filter items by the given key value pair. |
451
|
|
|
* |
452
|
|
|
* @param string $key |
453
|
|
|
* @param mixed $values |
454
|
|
|
* @param bool $strict |
455
|
|
|
* |
456
|
|
|
* @return static |
457
|
|
|
*/ |
458
|
2 |
|
public function whereIn(string $key, $values, bool $strict = false) |
459
|
|
|
{ |
460
|
2 |
|
$values = $this->getArrayableItems($values); |
461
|
|
|
|
462
|
|
|
return $this->filter(function ($itemKey, $item) use ($key, $values, $strict) { |
463
|
2 |
|
return in_array(self::dataGet($item, $key), $values, $strict); |
464
|
2 |
|
}); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* Filter items by the given key value pair using strict comparison. |
469
|
|
|
* |
470
|
|
|
* @param string $key |
471
|
|
|
* @param mixed $values |
472
|
|
|
* |
473
|
|
|
* @return static |
474
|
|
|
*/ |
475
|
1 |
|
public function whereInStrict(string $key, $values): Collection |
476
|
|
|
{ |
477
|
1 |
|
return $this->whereIn($key, $values, true); |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
/** |
481
|
|
|
* Remove an item from the collection by key. |
482
|
|
|
* |
483
|
|
|
* @param string|array $keys |
484
|
|
|
* |
485
|
|
|
* @return $this |
486
|
|
|
*/ |
487
|
2 |
|
public function forget($keys): Collection |
488
|
|
|
{ |
489
|
2 |
|
foreach ((array) $keys as $key) { |
490
|
2 |
|
$this->offsetUnset($key); |
491
|
|
|
} |
492
|
|
|
|
493
|
2 |
|
return $this; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* Get an item from the collection by key. |
498
|
|
|
* |
499
|
|
|
* @param mixed $key |
500
|
|
|
* @param mixed $default |
501
|
|
|
* |
502
|
|
|
* @return mixed |
503
|
|
|
*/ |
504
|
4 |
|
public function get($key, $default = null) |
505
|
|
|
{ |
506
|
4 |
|
if ($this->offsetExists($key)) { |
507
|
4 |
|
return $this->items[$key]; |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
return value($default); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Group an associative array by a field or using a callback. |
515
|
|
|
* |
516
|
|
|
* @param callable|string $groupBy |
517
|
|
|
* @param bool $preserveKeys |
518
|
|
|
* |
519
|
|
|
* @return static |
520
|
|
|
*/ |
521
|
6 |
|
public function groupBy($groupBy, bool $preserveKeys = false): Collection |
522
|
|
|
{ |
523
|
6 |
|
$groupBy = $this->valueRetriever($groupBy); |
524
|
6 |
|
$results = []; |
525
|
|
|
|
526
|
6 |
|
foreach ($this->items as $key => $value) { |
527
|
6 |
|
$groupKeys = $groupBy($value, $key); |
528
|
|
|
|
529
|
6 |
|
if (! is_array($groupKeys)) { |
530
|
4 |
|
$groupKeys = [$groupKeys]; |
531
|
|
|
} |
532
|
|
|
|
533
|
6 |
|
foreach ($groupKeys as $groupKey) { |
534
|
6 |
|
if (! array_key_exists($groupKey, $results)) { |
535
|
6 |
|
$results[$groupKey] = new static(); |
536
|
|
|
} |
537
|
|
|
|
538
|
6 |
|
$results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
6 |
|
return new static($results); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Key an associative array by a field or using a callback. |
547
|
|
|
* |
548
|
|
|
* @param callable|string $keyBy |
549
|
|
|
* |
550
|
|
|
* @return static |
551
|
|
|
*/ |
552
|
2 |
|
public function keyBy($keyBy): Collection |
553
|
|
|
{ |
554
|
2 |
|
$keyBy = $this->valueRetriever($keyBy); |
555
|
2 |
|
$results = []; |
556
|
2 |
|
foreach ($this->items as $key => $item) { |
557
|
2 |
|
$results[$keyBy($item, $key)] = $item; |
558
|
|
|
} |
559
|
|
|
|
560
|
2 |
|
return new static($results); |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
/** |
564
|
|
|
* Determine if an item exists in the collection by key. |
565
|
|
|
* |
566
|
|
|
* @param mixed $key |
567
|
|
|
* |
568
|
|
|
* @return bool |
569
|
|
|
*/ |
570
|
|
|
public function has($key): bool |
571
|
|
|
{ |
572
|
|
|
return $this->offsetExists($key); |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
/** |
576
|
|
|
* Concatenate values of a given key as a string. |
577
|
|
|
* |
578
|
|
|
* @param string $value |
579
|
|
|
* @param string|null $glue |
580
|
|
|
* |
581
|
|
|
* @return string |
582
|
|
|
*/ |
583
|
1 |
|
public function implode(string $value, string $glue = null): string |
584
|
|
|
{ |
585
|
1 |
|
$first = $this->first(); |
586
|
|
|
|
587
|
1 |
|
if (is_array($first) || is_object($first)) { |
588
|
1 |
|
return implode($glue, $this->pluck($value)->all()); |
589
|
|
|
} |
590
|
|
|
|
591
|
1 |
|
return implode($value, $this->items); |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
/** |
595
|
|
|
* Intersect the collection with the given items. |
596
|
|
|
* |
597
|
|
|
* @param mixed $items |
598
|
|
|
* |
599
|
|
|
* @return static |
600
|
|
|
*/ |
601
|
2 |
|
public function intersect($items): Collection |
602
|
|
|
{ |
603
|
2 |
|
return new static(array_intersect($this->items, $this->getArrayableItems($items))); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
/** |
607
|
|
|
* Determine if the collection is empty or not. |
608
|
|
|
* |
609
|
|
|
* @return bool |
610
|
|
|
*/ |
611
|
5 |
|
public function isEmpty(): bool |
612
|
|
|
{ |
613
|
5 |
|
return empty($this->items); |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* Get the last item from the collection. |
618
|
|
|
* |
619
|
|
|
* @param callable|null $callback |
620
|
|
|
* @param mixed $default |
621
|
|
|
* |
622
|
|
|
* @return mixed |
623
|
|
|
*/ |
624
|
7 |
|
public function last(callable $callback = null, $default = null) |
625
|
|
|
{ |
626
|
7 |
|
return Arr::last($this->items, $callback, $default); |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
/** |
630
|
|
|
* Get the values of a given key. |
631
|
|
|
* |
632
|
|
|
* @param string $value |
633
|
|
|
* @param string|null $key |
634
|
|
|
* |
635
|
|
|
* @return static |
636
|
|
|
*/ |
637
|
8 |
|
public function pluck(string $value, $key = null): Collection |
638
|
|
|
{ |
639
|
8 |
|
$results = []; |
640
|
|
|
|
641
|
8 |
|
list($value, $key) = static::explodePluckParameters($value, $key); |
642
|
|
|
|
643
|
8 |
|
foreach ($this->items as $item) { |
644
|
8 |
|
$itemValue = self::dataGet($item, $value); |
645
|
|
|
|
646
|
8 |
|
if (is_null($key)) { |
647
|
8 |
|
$results[] = $itemValue; |
648
|
|
|
} else { |
649
|
2 |
|
$itemKey = self::dataGet($item, $key); |
650
|
8 |
|
$results[$itemKey] = $itemValue; |
651
|
|
|
} |
652
|
|
|
} |
653
|
|
|
|
654
|
8 |
|
return new static($results); |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
/** |
658
|
|
|
* Run a map over each of the items. |
659
|
|
|
* |
660
|
|
|
* @param callable $callback |
661
|
|
|
* |
662
|
|
|
* @return static |
663
|
|
|
*/ |
664
|
8 |
|
public function map(callable $callback): Collection |
665
|
|
|
{ |
666
|
8 |
|
$keys = array_keys($this->items); |
667
|
8 |
|
$items = array_map($callback, $this->items, $keys); |
668
|
|
|
|
669
|
8 |
|
return new static(array_combine($keys, $items)); |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
/** |
673
|
|
|
* Run an associative map over each of the items. |
674
|
|
|
* |
675
|
|
|
* The callback should return an associative array with a single key/value pair. |
676
|
|
|
* |
677
|
|
|
* @param callable $callback |
678
|
|
|
* |
679
|
|
|
* @return static |
680
|
|
|
*/ |
681
|
1 |
|
public function mapWithKeys(callable $callback): Collection |
682
|
|
|
{ |
683
|
1 |
|
return $this->flatMap($callback); |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
/** |
687
|
|
|
* Map a collection and flatten the result by a single level. |
688
|
|
|
* |
689
|
|
|
* @param callable $callback |
690
|
|
|
* |
691
|
|
|
* @return static |
692
|
|
|
*/ |
693
|
2 |
|
public function flatMap(callable $callback): Collection |
694
|
|
|
{ |
695
|
2 |
|
return $this->map($callback)->collapse(); |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
/** |
699
|
|
|
* Get the max value of a given key. |
700
|
|
|
* |
701
|
|
|
* @param callable|string|null $callback |
702
|
|
|
* |
703
|
|
|
* @return mixed |
704
|
|
|
*/ |
705
|
1 |
View Code Duplication |
public function max($callback = null) |
|
|
|
|
706
|
|
|
{ |
707
|
1 |
|
$callback = $this->valueRetriever($callback); |
708
|
|
|
|
709
|
|
|
return $this->reduce(function ($result, $item) use ($callback) { |
710
|
1 |
|
$value = $callback($item); |
711
|
|
|
|
712
|
1 |
|
return is_null($result) || $value > $result ? $value : $result; |
713
|
1 |
|
}); |
714
|
|
|
} |
715
|
|
|
|
716
|
|
|
/** |
717
|
|
|
* Create a collection by using this collection for keys and another for its values. |
718
|
|
|
* |
719
|
|
|
* @param mixed $values |
720
|
|
|
* |
721
|
|
|
* @return static |
722
|
|
|
*/ |
723
|
2 |
|
public function combine($values): Collection |
724
|
|
|
{ |
725
|
2 |
|
return new static(array_combine($this->all(), $this->getArrayableItems($values))); |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
/** |
729
|
|
|
* Union the collection with the given items. |
730
|
|
|
* |
731
|
|
|
* @param mixed $items |
732
|
|
|
* |
733
|
|
|
* @return static |
734
|
|
|
*/ |
735
|
3 |
|
public function union($items): Collection |
736
|
|
|
{ |
737
|
3 |
|
return new static($this->items + $this->getArrayableItems($items)); |
738
|
|
|
} |
739
|
|
|
|
740
|
|
|
/** |
741
|
|
|
* Get the min value of a given key. |
742
|
|
|
* |
743
|
|
|
* @param callable|string|null $callback |
744
|
|
|
* |
745
|
|
|
* @return mixed |
746
|
|
|
*/ |
747
|
1 |
View Code Duplication |
public function min($callback = null) |
|
|
|
|
748
|
|
|
{ |
749
|
1 |
|
$callback = $this->valueRetriever($callback); |
750
|
|
|
|
751
|
|
|
return $this->reduce(function ($result, $item) use ($callback) { |
752
|
1 |
|
$value = $callback($item); |
753
|
|
|
|
754
|
1 |
|
return is_null($result) || $value < $result ? $value : $result; |
755
|
1 |
|
}); |
756
|
|
|
} |
757
|
|
|
|
758
|
|
|
/** |
759
|
|
|
* Get the items with the specified keys. |
760
|
|
|
* |
761
|
|
|
* @param mixed $keys |
762
|
|
|
* |
763
|
|
|
* @return static |
764
|
|
|
*/ |
765
|
1 |
|
public function only($keys): Collection |
766
|
|
|
{ |
767
|
1 |
|
$keys = is_array($keys) ? $keys : func_get_args(); |
768
|
|
|
|
769
|
1 |
|
return new static(Arr::only($this->items, $keys)); |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* "Paginate" the collection by slicing it into a smaller collection. |
774
|
|
|
* |
775
|
|
|
* @param int $page |
776
|
|
|
* @param int $perPage |
777
|
|
|
* |
778
|
|
|
* @return static |
779
|
|
|
*/ |
780
|
1 |
|
public function forPage(int $page, int $perPage): Collection |
781
|
|
|
{ |
782
|
1 |
|
return $this->slice(($page - 1) * $perPage, $perPage); |
783
|
|
|
} |
784
|
|
|
|
785
|
|
|
/** |
786
|
|
|
* Pass the collection to the given callback and return the result. |
787
|
|
|
* |
788
|
|
|
* @param callable $callback |
789
|
|
|
* |
790
|
|
|
* @return mixed |
791
|
|
|
*/ |
792
|
1 |
|
public function pipe(callable $callback) |
793
|
|
|
{ |
794
|
1 |
|
return $callback($this); |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
/** |
798
|
|
|
* Get and remove the last item from the collection. |
799
|
|
|
* |
800
|
|
|
* @return mixed |
801
|
|
|
*/ |
802
|
1 |
|
public function pop() |
803
|
|
|
{ |
804
|
1 |
|
return array_pop($this->items); |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
/** |
808
|
|
|
* Push an item onto the beginning of the collection. |
809
|
|
|
* |
810
|
|
|
* @param mixed $value |
811
|
|
|
* @param mixed $key |
812
|
|
|
* |
813
|
|
|
* @return $this |
814
|
|
|
*/ |
815
|
1 |
|
public function prepend($value, $key = null): Collection |
816
|
|
|
{ |
817
|
1 |
|
$this->items = Arr::prepend($this->items, $value, $key); |
818
|
|
|
|
819
|
1 |
|
return $this; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
/** |
823
|
|
|
* Push an item onto the end of the collection. |
824
|
|
|
* |
825
|
|
|
* @param mixed $value |
826
|
|
|
* |
827
|
|
|
* @return $this |
828
|
|
|
*/ |
829
|
|
|
public function push($value): Collection |
830
|
|
|
{ |
831
|
|
|
$this->offsetSet(null, $value); |
832
|
|
|
|
833
|
|
|
return $this; |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
/** |
837
|
|
|
* Get and remove an item from the collection. |
838
|
|
|
* |
839
|
|
|
* @param mixed $key |
840
|
|
|
* @param mixed $default |
841
|
|
|
* |
842
|
|
|
* @return mixed |
843
|
|
|
*/ |
844
|
3 |
|
public function pull($key, $default = null) |
845
|
|
|
{ |
846
|
3 |
|
return Arr::pull($this->items, $key, $default); |
847
|
|
|
} |
848
|
|
|
|
849
|
|
|
/** |
850
|
|
|
* Put an item in the collection by key. |
851
|
|
|
* |
852
|
|
|
* @param mixed $key |
853
|
|
|
* @param mixed $value |
854
|
|
|
* |
855
|
|
|
* @return $this |
856
|
|
|
*/ |
857
|
|
|
public function put($key, $value): Collection |
858
|
|
|
{ |
859
|
|
|
$this->offsetSet($key, $value); |
860
|
|
|
|
861
|
|
|
return $this; |
862
|
|
|
} |
863
|
|
|
|
864
|
|
|
/** |
865
|
|
|
* Get one or more items randomly from the collection. |
866
|
|
|
* |
867
|
|
|
* @param int $amount |
868
|
|
|
* |
869
|
|
|
* @throws \InvalidArgumentException |
870
|
|
|
* |
871
|
|
|
* @return mixed |
872
|
|
|
*/ |
873
|
3 |
|
public function random($amount = 1) |
874
|
|
|
{ |
875
|
3 |
|
if ($amount > ($count = $this->count())) { |
876
|
2 |
|
throw new InvalidArgumentException( |
877
|
2 |
|
sprintf('You requested %s items, but there are only %s items in the collection', $amount, $count) |
878
|
|
|
); |
879
|
|
|
} |
880
|
|
|
|
881
|
1 |
|
$keys = array_rand($this->items, $amount); |
882
|
|
|
|
883
|
1 |
|
if ($amount == 1) { |
884
|
1 |
|
return $this->items[$keys]; |
885
|
|
|
} |
886
|
|
|
|
887
|
1 |
|
return new static(array_intersect_key($this->items, array_flip($keys))); |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
/** |
891
|
|
|
* Reduce the collection to a single value. |
892
|
|
|
* |
893
|
|
|
* @param callable $callback |
894
|
|
|
* @param mixed $initial |
895
|
|
|
* |
896
|
|
|
* @return mixed |
897
|
|
|
*/ |
898
|
6 |
|
public function reduce(callable $callback, $initial = null) |
899
|
|
|
{ |
900
|
6 |
|
return array_reduce($this->items, $callback, $initial); |
901
|
|
|
} |
902
|
|
|
|
903
|
|
|
/** |
904
|
|
|
* Create a collection of all elements that do not pass a given truth test. |
905
|
|
|
* |
906
|
|
|
* @param mixed $callback |
907
|
|
|
* |
908
|
|
|
* @return static |
909
|
|
|
*/ |
910
|
3 |
|
public function reject($callback): Collection |
911
|
|
|
{ |
912
|
3 |
|
if ($this->useAsCallable($callback)) { |
913
|
|
|
return $this->filter(function ($key, $value) use ($callback) { |
914
|
3 |
|
return ! $callback($value, $key); |
915
|
3 |
|
}); |
916
|
|
|
} |
917
|
|
|
|
918
|
|
|
return $this->filter(function ($key, $item) use ($callback) { |
919
|
1 |
|
return $item != $callback; |
920
|
1 |
|
}); |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
/** |
924
|
|
|
* Reverse items order. |
925
|
|
|
* |
926
|
|
|
* @return static |
927
|
|
|
*/ |
928
|
1 |
|
public function reverse(): Collection |
929
|
|
|
{ |
930
|
1 |
|
return new static(array_reverse($this->items, true)); |
931
|
|
|
} |
932
|
|
|
|
933
|
|
|
/** |
934
|
|
|
* Search the collection for a given value and return the corresponding key if successful. |
935
|
|
|
* |
936
|
|
|
* @param mixed $value |
937
|
|
|
* @param bool $strict |
938
|
|
|
* |
939
|
|
|
* @return false|int|string |
940
|
|
|
*/ |
941
|
2 |
|
public function search($value, $strict = false) |
942
|
|
|
{ |
943
|
2 |
|
if (! $this->useAsCallable($value)) { |
944
|
2 |
|
return array_search($value, $this->items, $strict); |
945
|
|
|
} |
946
|
2 |
|
foreach ($this->items as $key => $item) { |
947
|
2 |
|
if (call_user_func($value, $item, $key)) { |
948
|
2 |
|
return $key; |
949
|
|
|
} |
950
|
|
|
} |
951
|
|
|
|
952
|
1 |
|
return false; |
953
|
|
|
} |
954
|
|
|
|
955
|
|
|
/** |
956
|
|
|
* Get and remove the first item from the collection. |
957
|
|
|
* |
958
|
|
|
* @return mixed |
959
|
|
|
*/ |
960
|
1 |
|
public function shift() |
961
|
|
|
{ |
962
|
1 |
|
return array_shift($this->items); |
963
|
|
|
} |
964
|
|
|
|
965
|
|
|
/** |
966
|
|
|
* Shuffle the items in the collection. |
967
|
|
|
* |
968
|
|
|
* @param null|int $seed |
969
|
|
|
* |
970
|
|
|
* @return static |
971
|
|
|
*/ |
972
|
|
|
public function shuffle(int $seed = null): Collection |
973
|
|
|
{ |
974
|
|
|
$items = $this->items; |
975
|
|
|
if (is_null($seed)) { |
976
|
|
|
shuffle($items); |
977
|
|
|
} else { |
978
|
|
|
srand($seed); |
979
|
|
|
usort($items, function () { |
980
|
|
|
return rand(-1, 1); |
981
|
|
|
}); |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
return new static($items); |
985
|
|
|
} |
986
|
|
|
|
987
|
|
|
/** |
988
|
|
|
* Slice the underlying collection array. |
989
|
|
|
* |
990
|
|
|
* @param int $offset |
991
|
|
|
* @param null|int $length |
992
|
|
|
* |
993
|
|
|
* @return static |
994
|
|
|
*/ |
995
|
9 |
|
public function slice(int $offset, int $length = null): Collection |
996
|
|
|
{ |
997
|
9 |
|
return new static(array_slice($this->items, $offset, $length, true)); |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
/** |
1001
|
|
|
* Split a collection into a certain number of groups. |
1002
|
|
|
* |
1003
|
|
|
* @param int $numberOfGroups |
1004
|
|
|
* |
1005
|
|
|
* @return static |
1006
|
|
|
*/ |
1007
|
4 |
|
public function split(int $numberOfGroups): Collection |
1008
|
|
|
{ |
1009
|
4 |
|
if ($this->isEmpty()) { |
1010
|
1 |
|
return new static(); |
1011
|
|
|
} |
1012
|
|
|
|
1013
|
3 |
|
$groupSize = ceil($this->count() / $numberOfGroups); |
1014
|
|
|
|
1015
|
3 |
|
return $this->chunk($groupSize); |
1016
|
|
|
} |
1017
|
|
|
|
1018
|
|
|
/** |
1019
|
|
|
* Chunk the underlying collection array. |
1020
|
|
|
* |
1021
|
|
|
* @param int|float $size |
1022
|
|
|
* |
1023
|
|
|
* @return static |
1024
|
|
|
*/ |
1025
|
4 |
|
public function chunk($size): Collection |
1026
|
|
|
{ |
1027
|
4 |
|
$chunks = []; |
1028
|
|
|
|
1029
|
4 |
|
foreach (array_chunk($this->items, (int) $size, true) as $chunk) { |
1030
|
4 |
|
$chunks[] = new static($chunk); |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
4 |
|
return new static($chunks); |
1034
|
|
|
} |
1035
|
|
|
|
1036
|
|
|
/** |
1037
|
|
|
* Sort through each item with a callback. |
1038
|
|
|
* |
1039
|
|
|
* @see http://stackoverflow.com/questions/4353739/preserve-key-order-stable-sort-when-sorting-with-phps-uasort |
1040
|
|
|
* @see http://en.wikipedia.org/wiki/Schwartzian_transform |
1041
|
|
|
* |
1042
|
|
|
* @param callable|null $callback |
1043
|
|
|
* |
1044
|
|
|
* @return static |
1045
|
|
|
*/ |
1046
|
9 |
|
public function sort(callable $callback = null): Collection |
1047
|
|
|
{ |
1048
|
9 |
|
$items = $this->items; |
1049
|
|
|
|
1050
|
9 |
|
$callback |
1051
|
1 |
|
? uasort($items, $callback) |
1052
|
8 |
|
: asort($items); |
1053
|
|
|
|
1054
|
9 |
|
return new static($items); |
1055
|
|
|
} |
1056
|
|
|
|
1057
|
|
|
/** |
1058
|
|
|
* Sort an array in reverse order and maintain index association. |
1059
|
|
|
* |
1060
|
|
|
* @param int $option |
1061
|
|
|
* |
1062
|
|
|
* @return static |
1063
|
|
|
*/ |
1064
|
3 |
View Code Duplication |
public function arsort(int $option = SORT_REGULAR): Collection |
|
|
|
|
1065
|
|
|
{ |
1066
|
3 |
|
$index = 0; |
1067
|
3 |
|
$items = $this->items; |
1068
|
|
|
|
1069
|
3 |
|
foreach ($items as &$item) { |
1070
|
3 |
|
$item = [$index++, $item]; |
1071
|
|
|
} |
1072
|
|
|
|
1073
|
|
|
uasort($items, function ($a, $b) use ($option) { |
1074
|
3 |
|
if ($a[1] === $b[1]) { |
1075
|
1 |
|
return $a[0] - $b[0]; |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
3 |
|
$set = [-1 => $b[1], 1 => $a[1]]; |
1079
|
|
|
|
1080
|
3 |
|
asort($set, $option); |
1081
|
3 |
|
reset($set); |
1082
|
|
|
|
1083
|
3 |
|
return key($set); |
1084
|
3 |
|
}); |
1085
|
|
|
|
1086
|
3 |
|
foreach ($items as &$item) { |
1087
|
3 |
|
$item = $item[1]; |
1088
|
|
|
} |
1089
|
|
|
|
1090
|
3 |
|
return new static($items); |
1091
|
|
|
} |
1092
|
|
|
|
1093
|
|
|
/** |
1094
|
|
|
* Sort an array and maintain index association. |
1095
|
|
|
* |
1096
|
|
|
* @param int $option |
1097
|
|
|
* |
1098
|
|
|
* @return static |
1099
|
|
|
*/ |
1100
|
3 |
View Code Duplication |
public function asort(int $option = SORT_REGULAR): Collection |
|
|
|
|
1101
|
|
|
{ |
1102
|
3 |
|
$index = 0; |
1103
|
3 |
|
$items = $this->items; |
1104
|
|
|
|
1105
|
3 |
|
foreach ($items as &$item) { |
1106
|
3 |
|
$item = [$index++, $item]; |
1107
|
|
|
} |
1108
|
|
|
|
1109
|
|
|
uasort($items, function ($a, $b) use ($option) { |
1110
|
3 |
|
if ($a[1] === $b[1]) { |
1111
|
1 |
|
return $a[0] - $b[0]; |
1112
|
|
|
} |
1113
|
|
|
|
1114
|
3 |
|
$set = [-1 => $a[1], 1 => $b[1]]; |
1115
|
|
|
|
1116
|
3 |
|
asort($set, $option); |
1117
|
3 |
|
reset($set); |
1118
|
|
|
|
1119
|
3 |
|
return key($set); |
1120
|
3 |
|
}); |
1121
|
|
|
|
1122
|
3 |
|
foreach ($items as &$item) { |
1123
|
3 |
|
$item = $item[1]; |
1124
|
|
|
} |
1125
|
|
|
|
1126
|
3 |
|
return new static($items); |
1127
|
|
|
} |
1128
|
|
|
|
1129
|
|
|
/** |
1130
|
|
|
* Sort an array using a case insensitive "natural order" algorithm. |
1131
|
|
|
* |
1132
|
|
|
* @return static |
1133
|
|
|
*/ |
1134
|
1 |
View Code Duplication |
public function natcasesort(): Collection |
|
|
|
|
1135
|
|
|
{ |
1136
|
1 |
|
$index = 0; |
1137
|
1 |
|
$items = $this->items; |
1138
|
|
|
|
1139
|
1 |
|
foreach ($items as &$item) { |
1140
|
1 |
|
$item = [$index++, $item]; |
1141
|
|
|
} |
1142
|
|
|
|
1143
|
|
|
uasort($items, function ($a, $b) { |
1144
|
1 |
|
$result = strnatcasecmp($a[1], $b[1]); |
1145
|
|
|
|
1146
|
1 |
|
return $result === 0 ? $a[0] - $b[0] : $result; |
1147
|
1 |
|
}); |
1148
|
|
|
|
1149
|
1 |
|
foreach ($items as &$item) { |
1150
|
1 |
|
$item = $item[1]; |
1151
|
|
|
} |
1152
|
|
|
|
1153
|
1 |
|
return new static($items); |
1154
|
|
|
} |
1155
|
|
|
|
1156
|
|
|
/** |
1157
|
|
|
* Sort an array using a "natural order" algorithm. |
1158
|
|
|
* |
1159
|
|
|
* @return static |
1160
|
|
|
*/ |
1161
|
1 |
View Code Duplication |
public function natsort(): Collection |
|
|
|
|
1162
|
|
|
{ |
1163
|
1 |
|
$index = 0; |
1164
|
1 |
|
$items = $this->items; |
1165
|
|
|
|
1166
|
1 |
|
foreach ($items as &$item) { |
1167
|
1 |
|
$item = [$index++, $item]; |
1168
|
|
|
} |
1169
|
|
|
|
1170
|
|
|
uasort($items, function ($a, $b) { |
1171
|
1 |
|
$result = strnatcmp($a[1], $b[1]); |
1172
|
|
|
|
1173
|
1 |
|
return $result === 0 ? $a[0] - $b[0] : $result; |
1174
|
1 |
|
}); |
1175
|
|
|
|
1176
|
1 |
|
foreach ($items as &$item) { |
1177
|
1 |
|
$item = $item[1]; |
1178
|
|
|
} |
1179
|
|
|
|
1180
|
1 |
|
return new static($items); |
1181
|
|
|
} |
1182
|
|
|
|
1183
|
|
|
/** |
1184
|
|
|
* Sort an array with a user-defined comparison function and maintain index association. |
1185
|
|
|
* |
1186
|
|
|
* @param callable $callback |
1187
|
|
|
* |
1188
|
|
|
* @return static |
1189
|
|
|
*/ |
1190
|
1 |
View Code Duplication |
public function uasort(callable $callback): Collection |
|
|
|
|
1191
|
|
|
{ |
1192
|
1 |
|
$index = 0; |
1193
|
1 |
|
$items = $this->items; |
1194
|
|
|
|
1195
|
1 |
|
foreach ($items as &$item) { |
1196
|
1 |
|
$item = [$index++, $item]; |
1197
|
|
|
} |
1198
|
|
|
|
1199
|
|
|
uasort($items, function ($a, $b) use ($callback) { |
1200
|
1 |
|
$result = call_user_func($callback, $a[1], $b[1]); |
1201
|
|
|
|
1202
|
1 |
|
return $result === 0 ? $a[0] - $b[0] : $result; |
1203
|
1 |
|
}); |
1204
|
|
|
|
1205
|
1 |
|
foreach ($items as &$item) { |
1206
|
1 |
|
$item = $item[1]; |
1207
|
|
|
} |
1208
|
|
|
|
1209
|
1 |
|
return new static($items); |
1210
|
|
|
} |
1211
|
|
|
|
1212
|
|
|
/** |
1213
|
|
|
* Sort an array by keys using a user-defined comparison function. |
1214
|
|
|
* |
1215
|
|
|
* @param callable $callback |
1216
|
|
|
* |
1217
|
|
|
* @return static |
1218
|
|
|
*/ |
1219
|
1 |
|
public function uksort(callable $callback): Collection |
1220
|
|
|
{ |
1221
|
1 |
|
$items = $this->items; |
1222
|
1 |
|
$keys = array_combine(array_keys($items), range(1, count($items))); |
1223
|
|
|
|
1224
|
|
|
uksort($items, function ($a, $b) use ($callback, $keys) { |
1225
|
1 |
|
$result = call_user_func($callback, $a, $b); |
1226
|
|
|
|
1227
|
1 |
|
return $result === 0 ? $keys[$a] - $keys[$b] : $result; |
1228
|
1 |
|
}); |
1229
|
|
|
|
1230
|
1 |
|
return new static($items); |
1231
|
|
|
} |
1232
|
|
|
|
1233
|
|
|
/** |
1234
|
|
|
* Sort an array by values using a user-defined comparison function. |
1235
|
|
|
* |
1236
|
|
|
* @param callable $callback |
1237
|
|
|
* |
1238
|
|
|
* @return static |
1239
|
|
|
*/ |
1240
|
1 |
View Code Duplication |
public function usort(callable $callback): Collection |
|
|
|
|
1241
|
|
|
{ |
1242
|
1 |
|
$index = 0; |
1243
|
1 |
|
$items = $this->items; |
1244
|
|
|
|
1245
|
1 |
|
foreach ($items as &$item) { |
1246
|
1 |
|
$item = [$index++, $item]; |
1247
|
|
|
} |
1248
|
|
|
|
1249
|
|
|
usort($items, function ($a, $b) use ($callback) { |
1250
|
1 |
|
$result = call_user_func($callback, $a[1], $b[1]); |
1251
|
|
|
|
1252
|
1 |
|
return $result === 0 ? $a[0] - $b[0] : $result; |
1253
|
1 |
|
}); |
1254
|
|
|
|
1255
|
1 |
|
foreach ($items as &$item) { |
1256
|
1 |
|
$item = $item[1]; |
1257
|
|
|
} |
1258
|
|
|
|
1259
|
1 |
|
return new static($items); |
1260
|
|
|
} |
1261
|
|
|
|
1262
|
|
|
/** |
1263
|
|
|
* Sort the collection using the given callback. |
1264
|
|
|
* |
1265
|
|
|
* @param callable|string $callback |
1266
|
|
|
* @param int $options |
1267
|
|
|
* @param bool $descending |
1268
|
|
|
* |
1269
|
|
|
* @return static |
1270
|
|
|
*/ |
1271
|
3 |
|
public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): Collection |
1272
|
|
|
{ |
1273
|
3 |
|
$results = []; |
1274
|
3 |
|
$callback = $this->valueRetriever($callback); |
1275
|
|
|
|
1276
|
|
|
// First we will loop through the items and get the comparator from a callback |
1277
|
|
|
// function which we were given. Then, we will sort the returned values and |
1278
|
|
|
// and grab the corresponding values for the sorted keys from this array. |
1279
|
3 |
|
foreach ($this->items as $key => $value) { |
1280
|
3 |
|
$results[$key] = $callback($value, $key); |
1281
|
|
|
} |
1282
|
|
|
|
1283
|
3 |
|
$descending ? arsort($results, $options) : asort($results, $options); |
1284
|
|
|
|
1285
|
|
|
// Once we have sorted all of the keys in the array, we will loop through them |
1286
|
|
|
// and grab the corresponding model so we can set the underlying items list |
1287
|
|
|
// to the sorted version. Then we'll just return the collection instance. |
1288
|
3 |
|
foreach (array_keys($results) as $key) { |
1289
|
3 |
|
$results[$key] = $this->items[$key]; |
1290
|
|
|
} |
1291
|
|
|
|
1292
|
3 |
|
return new static($results); |
1293
|
|
|
} |
1294
|
|
|
|
1295
|
|
|
/** |
1296
|
|
|
* Sort the collection in descending order using the given callback. |
1297
|
|
|
* |
1298
|
|
|
* @param callable|string $callback |
1299
|
|
|
* @param int $options |
1300
|
|
|
* |
1301
|
|
|
* @return static |
1302
|
|
|
*/ |
1303
|
1 |
|
public function sortByDesc($callback, int $options = SORT_REGULAR): Collection |
1304
|
|
|
{ |
1305
|
1 |
|
return $this->sortBy($callback, $options, true); |
1306
|
|
|
} |
1307
|
|
|
|
1308
|
|
|
/** |
1309
|
|
|
* Splice a portion of the underlying collection array. |
1310
|
|
|
* |
1311
|
|
|
* @param int $offset |
1312
|
|
|
* @param int|null $length |
1313
|
|
|
* @param mixed $replacement |
1314
|
|
|
* |
1315
|
|
|
* @return static |
1316
|
|
|
*/ |
1317
|
1 |
|
public function splice(int $offset, $length = null, $replacement = []): Collection |
1318
|
|
|
{ |
1319
|
1 |
|
if (func_num_args() == 1) { |
1320
|
1 |
|
return new static(array_splice($this->items, $offset)); |
1321
|
|
|
} |
1322
|
|
|
|
1323
|
1 |
|
return new static(array_splice($this->items, $offset, $length, $replacement)); |
1324
|
|
|
} |
1325
|
|
|
|
1326
|
|
|
/** |
1327
|
|
|
* Get the sum of the given values. |
1328
|
|
|
* |
1329
|
|
|
* @param callable|string|null $callback |
1330
|
|
|
* |
1331
|
|
|
* @return mixed |
1332
|
|
|
*/ |
1333
|
8 |
|
public function sum($callback = null) |
1334
|
|
|
{ |
1335
|
8 |
|
if (is_null($callback)) { |
1336
|
6 |
|
return array_sum($this->items); |
1337
|
|
|
} |
1338
|
3 |
|
$callback = $this->valueRetriever($callback); |
1339
|
|
|
|
1340
|
|
|
return $this->reduce(function ($result, $item) use ($callback) { |
1341
|
2 |
|
return $result + $callback($item); |
1342
|
3 |
|
}, 0); |
1343
|
|
|
} |
1344
|
|
|
|
1345
|
|
|
/** |
1346
|
|
|
* Take the first or last {$limit} items. |
1347
|
|
|
* |
1348
|
|
|
* @param int $limit |
1349
|
|
|
* |
1350
|
|
|
* @return static |
1351
|
|
|
*/ |
1352
|
2 |
|
public function take(int $limit): Collection |
1353
|
|
|
{ |
1354
|
2 |
|
if ($limit < 0) { |
1355
|
1 |
|
return $this->slice($limit, abs($limit)); |
1356
|
|
|
} |
1357
|
|
|
|
1358
|
1 |
|
return $this->slice(0, $limit); |
1359
|
|
|
} |
1360
|
|
|
|
1361
|
|
|
/** |
1362
|
|
|
* Transform each item in the collection using a callback. |
1363
|
|
|
* |
1364
|
|
|
* @param callable $callback |
1365
|
|
|
* |
1366
|
|
|
* @return $this |
1367
|
|
|
*/ |
1368
|
1 |
|
public function transform(callable $callback): Collection |
1369
|
|
|
{ |
1370
|
1 |
|
$this->items = $this->map($callback)->all(); |
1371
|
|
|
|
1372
|
1 |
|
return $this; |
1373
|
|
|
} |
1374
|
|
|
|
1375
|
|
|
/** |
1376
|
|
|
* Return only unique items from the collection array. |
1377
|
|
|
* |
1378
|
|
|
* @param string|callable|null $key |
1379
|
|
|
* @param bool $strict |
1380
|
|
|
* |
1381
|
|
|
* @return static |
1382
|
|
|
*/ |
1383
|
4 |
|
public function unique($key = null, bool $strict = false): Collection |
1384
|
|
|
{ |
1385
|
4 |
|
if (is_null($key)) { |
1386
|
2 |
|
return new static(array_unique($this->items, SORT_REGULAR)); |
1387
|
|
|
} |
1388
|
|
|
|
1389
|
2 |
|
$key = $this->valueRetriever($key); |
1390
|
2 |
|
$exists = []; |
1391
|
|
|
|
1392
|
|
|
return $this->reject(function ($item) use ($key, $strict, &$exists) { |
1393
|
2 |
|
if (in_array($id = $key($item), $exists, $strict)) { |
1394
|
2 |
|
return true; |
1395
|
|
|
} |
1396
|
|
|
|
1397
|
2 |
|
$exists[] = $id; |
1398
|
2 |
|
}); |
1399
|
|
|
} |
1400
|
|
|
|
1401
|
|
|
/** |
1402
|
|
|
* Return only unique items from the collection array using strict comparison. |
1403
|
|
|
* |
1404
|
|
|
* @param string|callable|null $key |
1405
|
|
|
* |
1406
|
|
|
* @return static |
1407
|
|
|
*/ |
1408
|
1 |
|
public function uniqueStrict($key = null): Collection |
1409
|
|
|
{ |
1410
|
1 |
|
return $this->unique($key, true); |
1411
|
|
|
} |
1412
|
|
|
|
1413
|
|
|
/** |
1414
|
|
|
* Zip the collection together with one or more arrays. |
1415
|
|
|
* |
1416
|
|
|
* e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); |
1417
|
|
|
* => [[1, 4], [2, 5], [3, 6]] |
1418
|
|
|
* |
1419
|
|
|
* @param mixed ...$items |
1420
|
|
|
* |
1421
|
|
|
* @return static |
1422
|
|
|
*/ |
1423
|
1 |
|
public function zip($items): Collection |
1424
|
|
|
{ |
1425
|
|
|
$arrayableItems = array_map(function ($items) { |
1426
|
1 |
|
return $this->getArrayableItems($items); |
1427
|
1 |
|
}, func_get_args()); |
1428
|
|
|
$params = array_merge([function () { |
1429
|
1 |
|
return new static(func_get_args()); |
1430
|
1 |
|
}, $this->items], $arrayableItems); |
1431
|
|
|
|
1432
|
1 |
|
return new static(call_user_func_array('array_map', $params)); |
1433
|
|
|
} |
1434
|
|
|
|
1435
|
|
|
/** |
1436
|
|
|
* Collapse the collection of items into a single array. |
1437
|
|
|
* |
1438
|
|
|
* @return static |
1439
|
|
|
*/ |
1440
|
4 |
|
public function collapse(): Collection |
1441
|
|
|
{ |
1442
|
4 |
|
$results = []; |
1443
|
|
|
|
1444
|
4 |
|
foreach ($this->items as $values) { |
1445
|
4 |
|
if ($values instanceof self) { |
1446
|
1 |
|
$values = $values->all(); |
1447
|
3 |
|
} elseif (! is_array($values)) { |
1448
|
|
|
continue; |
1449
|
|
|
} |
1450
|
|
|
|
1451
|
4 |
|
$results = array_merge($results, $values); |
1452
|
|
|
} |
1453
|
|
|
|
1454
|
4 |
|
return new static($results); |
1455
|
|
|
} |
1456
|
|
|
|
1457
|
|
|
/** |
1458
|
|
|
* Get the keys of the collection items. |
1459
|
|
|
* |
1460
|
|
|
* @return static |
1461
|
|
|
*/ |
1462
|
4 |
|
public function keys(): Collection |
1463
|
|
|
{ |
1464
|
4 |
|
return new static(array_keys($this->items)); |
1465
|
|
|
} |
1466
|
|
|
|
1467
|
|
|
/** |
1468
|
|
|
* Get the instance as an array. |
1469
|
|
|
* |
1470
|
|
|
* @return array |
1471
|
|
|
*/ |
1472
|
24 |
|
public function toArray(): array |
1473
|
|
|
{ |
1474
|
|
|
return array_map(function ($value) { |
1475
|
23 |
|
if (method_exists($value, 'toArray')) { |
1476
|
7 |
|
return $value->toArray(); |
1477
|
|
|
} |
1478
|
|
|
|
1479
|
22 |
|
return $value; |
1480
|
24 |
|
}, $this->items); |
1481
|
|
|
} |
1482
|
|
|
|
1483
|
|
|
/** |
1484
|
|
|
* {@inheritdoc} |
1485
|
|
|
*/ |
1486
|
2 |
|
public function jsonSerialize() |
1487
|
|
|
{ |
1488
|
|
|
return array_map(function ($value) { |
1489
|
2 |
|
if ($value instanceof JsonSerializable) { |
|
|
|
|
1490
|
2 |
|
return $value->jsonSerialize(); |
1491
|
2 |
|
} elseif (method_exists($value, 'toJson')) { |
1492
|
1 |
|
return json_decode($value->toJson(), true); |
1493
|
2 |
|
} elseif (method_exists($value, 'toArray')) { |
1494
|
2 |
|
return $value->toArray(); |
1495
|
|
|
} |
1496
|
|
|
|
1497
|
1 |
|
return $value; |
1498
|
2 |
|
}, $this->items); |
1499
|
|
|
} |
1500
|
|
|
|
1501
|
|
|
/** |
1502
|
|
|
* Convert the object to its JSON representation. |
1503
|
|
|
* |
1504
|
|
|
* @param int $options |
1505
|
|
|
* |
1506
|
|
|
* @return string |
1507
|
|
|
*/ |
1508
|
2 |
|
public function toJson(int $options = 0): string |
1509
|
|
|
{ |
1510
|
2 |
|
return json_encode($this->jsonSerialize(), $options); |
1511
|
|
|
} |
1512
|
|
|
|
1513
|
|
|
/** |
1514
|
|
|
* Get an iterator for the items. |
1515
|
|
|
* |
1516
|
|
|
* @return \ArrayIterator |
1517
|
|
|
*/ |
1518
|
2 |
|
public function getIterator() |
1519
|
|
|
{ |
1520
|
2 |
|
return new ArrayIterator($this->items); |
1521
|
|
|
} |
1522
|
|
|
|
1523
|
|
|
/** |
1524
|
|
|
* Get a CachingIterator instance. |
1525
|
|
|
* |
1526
|
|
|
* @param int $flags |
1527
|
|
|
* |
1528
|
|
|
* @return \CachingIterator |
1529
|
|
|
*/ |
1530
|
1 |
|
public function getCachingIterator(int $flags = CachingIterator::CALL_TOSTRING): CachingIterator |
1531
|
|
|
{ |
1532
|
1 |
|
return new CachingIterator($this->getIterator(), $flags); |
1533
|
|
|
} |
1534
|
|
|
|
1535
|
|
|
/** |
1536
|
|
|
* Count the number of items in the collection. |
1537
|
|
|
* |
1538
|
|
|
* @return int |
1539
|
|
|
*/ |
1540
|
19 |
|
public function count() |
1541
|
|
|
{ |
1542
|
19 |
|
return count($this->items); |
1543
|
|
|
} |
1544
|
|
|
|
1545
|
|
|
/** |
1546
|
|
|
* {@inheritdoc} |
1547
|
|
|
*/ |
1548
|
|
|
public function serialize() |
1549
|
|
|
{ |
1550
|
|
|
return serialize($this->toJson()); |
1551
|
|
|
} |
1552
|
|
|
|
1553
|
|
|
/** |
1554
|
|
|
* Unserialize a string to a collection object. |
1555
|
|
|
* |
1556
|
|
|
* @param string $serialized |
1557
|
|
|
* |
1558
|
|
|
* @return static |
1559
|
|
|
*/ |
1560
|
|
|
public function unserialize($serialized) |
1561
|
|
|
{ |
1562
|
|
|
return new static(unserialize($serialized)); |
1563
|
|
|
} |
1564
|
|
|
|
1565
|
|
|
/** |
1566
|
|
|
* Determine if an item exists at an offset. |
1567
|
|
|
* |
1568
|
|
|
* @param mixed $key |
1569
|
|
|
* |
1570
|
|
|
* @return bool |
1571
|
|
|
*/ |
1572
|
11 |
|
public function offsetExists($key) |
1573
|
|
|
{ |
1574
|
11 |
|
return array_key_exists($key, $this->items); |
1575
|
|
|
} |
1576
|
|
|
|
1577
|
|
|
/** |
1578
|
|
|
* Get an item at a given offset. |
1579
|
|
|
* |
1580
|
|
|
* @param mixed $key |
1581
|
|
|
* |
1582
|
|
|
* @return mixed |
1583
|
|
|
*/ |
1584
|
10 |
|
public function offsetGet($key) |
1585
|
|
|
{ |
1586
|
10 |
|
return $this->items[$key]; |
1587
|
|
|
} |
1588
|
|
|
|
1589
|
|
|
/** |
1590
|
|
|
* Set the item at a given offset. |
1591
|
|
|
* |
1592
|
|
|
* @param mixed $key |
1593
|
|
|
* @param mixed $value |
1594
|
|
|
*/ |
1595
|
11 |
|
public function offsetSet($key, $value) |
1596
|
|
|
{ |
1597
|
11 |
|
if (is_null($key)) { |
1598
|
5 |
|
$this->items[] = $value; |
1599
|
|
|
} else { |
1600
|
8 |
|
$this->items[$key] = $value; |
1601
|
|
|
} |
1602
|
11 |
|
} |
1603
|
|
|
|
1604
|
|
|
/** |
1605
|
|
|
* Unset the item at a given offset. |
1606
|
|
|
* |
1607
|
|
|
* @param string $key |
1608
|
|
|
*/ |
1609
|
4 |
|
public function offsetUnset($key) |
1610
|
|
|
{ |
1611
|
4 |
|
unset($this->items[$key]); |
1612
|
4 |
|
} |
1613
|
|
|
|
1614
|
|
|
/** |
1615
|
|
|
* Register a custom extensions. |
1616
|
|
|
* |
1617
|
|
|
* @param string $name |
1618
|
|
|
* @param callable $callback |
1619
|
|
|
*/ |
1620
|
3 |
|
public static function extend(string $name, callable $callback) |
1621
|
|
|
{ |
1622
|
3 |
|
static::$extensions[$name] = $callback; |
1623
|
3 |
|
} |
1624
|
|
|
|
1625
|
|
|
/** |
1626
|
|
|
* Checks if extensions is registered. |
1627
|
|
|
* |
1628
|
|
|
* @param string $name |
1629
|
|
|
* |
1630
|
|
|
* @return bool |
1631
|
|
|
*/ |
1632
|
3 |
|
public static function hasExtensions(string $name): bool |
1633
|
|
|
{ |
1634
|
3 |
|
return isset(static::$extensions[$name]); |
1635
|
|
|
} |
1636
|
|
|
|
1637
|
|
|
/** |
1638
|
|
|
* Explode the "value" and "key" arguments passed to "pluck". |
1639
|
|
|
* |
1640
|
|
|
* @param string|array $value |
1641
|
|
|
* @param string|array|null $key |
1642
|
|
|
* |
1643
|
|
|
* @return array |
1644
|
|
|
*/ |
1645
|
8 |
|
protected static function explodePluckParameters($value, $key): array |
1646
|
|
|
{ |
1647
|
8 |
|
$value = is_string($value) ? explode('.', $value) : $value; |
1648
|
|
|
|
1649
|
8 |
|
$key = is_null($key) || is_array($key) ? $key : explode('.', $key); |
1650
|
|
|
|
1651
|
8 |
|
return [$value, $key]; |
1652
|
|
|
} |
1653
|
|
|
|
1654
|
|
|
/** |
1655
|
|
|
* Get an operator checker callback. |
1656
|
|
|
* |
1657
|
|
|
* @param string $key |
1658
|
|
|
* @param string $operator |
1659
|
|
|
* @param mixed $value |
1660
|
|
|
* |
1661
|
|
|
* @return \Closure |
1662
|
|
|
*/ |
1663
|
2 |
|
protected function operatorForWhere(string $key, $operator, $value) |
1664
|
|
|
{ |
1665
|
|
|
return function ($itemKey, $item) use ($key, $operator, $value) { |
1666
|
2 |
|
$retrieved = self::dataGet($item, $key); |
1667
|
|
|
|
1668
|
|
|
switch ($operator) { |
1669
|
|
|
default: |
1670
|
2 |
|
case '=': |
1671
|
2 |
|
case '==': |
1672
|
1 |
|
return $retrieved == $value; |
1673
|
2 |
|
case '!=': |
1674
|
2 |
|
case '<>': |
1675
|
1 |
|
return $retrieved != $value; |
1676
|
2 |
|
case '<': |
1677
|
1 |
|
return $retrieved < $value; |
1678
|
2 |
|
case '>': |
1679
|
1 |
|
return $retrieved > $value; |
1680
|
2 |
|
case '<=': |
1681
|
1 |
|
return $retrieved <= $value; |
1682
|
2 |
|
case '>=': |
1683
|
1 |
|
return $retrieved >= $value; |
1684
|
2 |
|
case '===': |
1685
|
2 |
|
return $retrieved === $value; |
1686
|
1 |
|
case '!==': |
1687
|
1 |
|
return $retrieved !== $value; |
1688
|
|
|
} |
1689
|
2 |
|
}; |
1690
|
|
|
} |
1691
|
|
|
|
1692
|
|
|
/** |
1693
|
|
|
* Get a value retrieving callback. |
1694
|
|
|
* |
1695
|
|
|
* @param mixed $value |
1696
|
|
|
* |
1697
|
|
|
* @return callable |
1698
|
|
|
*/ |
1699
|
18 |
|
protected function valueRetriever($value) |
1700
|
|
|
{ |
1701
|
18 |
|
if ($this->useAsCallable($value)) { |
1702
|
12 |
|
return $value; |
1703
|
|
|
} |
1704
|
|
|
|
1705
|
|
|
return function ($item) use ($value) { |
1706
|
11 |
|
return self::dataGet($item, $value); |
1707
|
12 |
|
}; |
1708
|
|
|
} |
1709
|
|
|
|
1710
|
|
|
/** |
1711
|
|
|
* Results array of items from Collection or Arrayable. |
1712
|
|
|
* |
1713
|
|
|
* @param callable|Closure|array|Traversable\Iterator|self|IteratorAggregate|JsonSerializable $items |
1714
|
|
|
* |
1715
|
|
|
* @return array |
1716
|
|
|
*/ |
1717
|
154 |
|
protected function getArrayableItems($items): array |
1718
|
|
|
{ |
1719
|
154 |
|
if (is_array($items)) { |
1720
|
148 |
|
return $items; |
1721
|
23 |
|
} elseif (is_callable($items)) { |
1722
|
|
|
return call_user_func($items); |
1723
|
23 |
|
} elseif ($items instanceof self) { |
1724
|
10 |
|
return $items->all(); |
1725
|
|
|
} elseif ($items instanceof Iterator) { |
1726
|
|
|
return $items; |
1727
|
|
|
} elseif ($items instanceof Traversable) { |
1728
|
2 |
|
return iterator_to_array($items); |
1729
|
|
|
} elseif ($items instanceof IteratorAggregate) { |
1730
|
|
|
return $items->getIterator(); |
1731
|
|
|
} elseif ($items instanceof JsonSerializable) { |
|
|
|
|
1732
|
1 |
|
return $items->jsonSerialize(); |
1733
|
12 |
|
} elseif (method_exists($items, 'toJson')) { |
1734
|
1 |
|
return json_decode($items->toJson(), true); |
1735
|
12 |
|
} elseif (method_exists($items, 'toArray')) { |
1736
|
1 |
|
return $items->toArray(); |
1737
|
|
|
} |
1738
|
|
|
|
1739
|
11 |
|
return (array) $items; |
1740
|
|
|
} |
1741
|
|
|
|
1742
|
|
|
/** |
1743
|
|
|
* Determine if the given value is callable, but not a string. |
1744
|
|
|
* |
1745
|
|
|
* @param mixed $value |
1746
|
|
|
* |
1747
|
|
|
* @return bool |
1748
|
|
|
*/ |
1749
|
23 |
|
protected function useAsCallable($value): bool |
1750
|
|
|
{ |
1751
|
23 |
|
return ! is_string($value) && is_callable($value); |
1752
|
|
|
} |
1753
|
|
|
|
1754
|
|
|
/** |
1755
|
|
|
* Get an item from an array or object using "dot" notation. |
1756
|
|
|
* |
1757
|
|
|
* @param mixed $target |
1758
|
|
|
* @param string|array $key |
1759
|
|
|
* @param mixed $default |
1760
|
|
|
* |
1761
|
|
|
* @return mixed |
1762
|
|
|
*/ |
1763
|
24 |
|
protected static function dataGet($target, $key, $default = null) |
1764
|
|
|
{ |
1765
|
24 |
|
if (is_null($key)) { |
1766
|
2 |
|
return $target; |
1767
|
|
|
} |
1768
|
|
|
|
1769
|
24 |
|
$key = is_array($key) ? $key : explode('.', $key); |
1770
|
|
|
|
1771
|
24 |
|
while (($segment = array_shift($key)) !== null) { |
1772
|
24 |
|
if ($segment === '*') { |
1773
|
|
|
if ($target instanceof self) { |
1774
|
|
|
$target = $target->all(); |
1775
|
|
|
} elseif (! is_array($target)) { |
1776
|
|
|
return Arr::value($default); |
1777
|
|
|
} |
1778
|
|
|
|
1779
|
|
|
$result = $this->pluck($target, $key); |
|
|
|
|
1780
|
|
|
|
1781
|
|
|
return in_array('*', $key) ? Arr::collapse($result) : $result; |
|
|
|
|
1782
|
|
|
} |
1783
|
|
|
|
1784
|
24 |
|
if (Arr::accessible($target) && Arr::exists($target, $segment)) { |
1785
|
19 |
|
$target = $target[$segment]; |
1786
|
10 |
|
} elseif (is_object($target) && isset($target->{$segment})) { |
1787
|
10 |
|
$target = $target->{$segment}; |
1788
|
|
|
} else { |
1789
|
|
|
return Arr::value($default); |
1790
|
|
|
} |
1791
|
|
|
} |
1792
|
|
|
|
1793
|
24 |
|
return $target; |
1794
|
|
|
} |
1795
|
|
|
|
1796
|
|
|
/** |
1797
|
|
|
* Flatten a multi-dimensional array into a single level. |
1798
|
|
|
* |
1799
|
|
|
* @param array|\Collection $array |
1800
|
|
|
* @param int|float|string $depth |
1801
|
|
|
* |
1802
|
|
|
* @return array |
1803
|
|
|
*/ |
1804
|
3 |
|
private function flattenCallback($array, $depth = INF): array |
1805
|
|
|
{ |
1806
|
3 |
|
$depth = (int) $depth; |
1807
|
|
|
|
1808
|
3 |
|
return array_reduce($array, function ($result, $item) use ($depth) { |
1809
|
3 |
|
$item = $item instanceof self ? $item->all() : $item; |
1810
|
|
|
|
1811
|
3 |
|
if (! is_array($item)) { |
1812
|
3 |
|
return array_merge($result, [$item]); |
1813
|
3 |
|
} elseif ($depth === 1) { |
1814
|
2 |
|
return array_merge($result, array_values($item)); |
1815
|
|
|
} |
1816
|
|
|
|
1817
|
3 |
|
return array_merge($result, self::flattenCallback($item, $depth - 1)); |
1818
|
3 |
|
}, []); |
1819
|
|
|
} |
1820
|
|
|
} |
1821
|
|
|
|
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.