Completed
Push — master ( 5d933c...75f119 )
by Ivan
12:11
created

Collection::where()   D

Complexity

Conditions 27
Paths 1

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 27.0686

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 21
cts 22
cp 0.9545
rs 4.6752
c 0
b 0
f 0
cc 27
eloc 24
nc 1
nop 2
crap 27.0686

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace vakata\collection;
4
5
class Collection implements \Iterator, \ArrayAccess, \Serializable, \Countable
6
{
7
    protected $array = null;
8
    protected $stack = [];
9
    protected $iterator = null;
10
    protected $key = null;
11
    protected $val = null;
12
13 4
    protected static function rangeGenerator($low, $high, $step = 1)
14
    {
15 4
        $k = -1;
16 4
        for ($i = $low; $i <= $high; $i += $step) {
17 4
            yield ++$k => $i;
18
        }
19 4
    }
20
    /**
21
     * Create a collection based on a range generator
22
     * @param  int|float  $low  start value
23
     * @param  int|float  $high end value
24
     * @param  int|float  $step increment
25
     * @return Collection
26
     */
27 4
    public static function range($low, $high, $step = 1) : Collection
28
    {
29 4
        return new static(static::rangeGenerator($low, $high, $step));
30
    }
31
    /**
32
     * A static alias of the __constructor
33
     * @param  mixed  $input  Anything iterable
34
     * @return Collection
35
     */
36 51
    public static function from($input) : Collection
37
    {
38 51
        return new static($input);
39
    }
40
    /**
41
     * Create an instance
42
     * @param  mixed  $input  Anything iterable
43
     */
44 55
    public function __construct($input = [])
45
    {
46 55
        if (is_object($input)) {
47 28
            if ($input instanceof \Iterator) {
48 5
                $this->array = $input;
49 5
                $this->iterator = $input;
50 23
            } else if ($input instanceof self) {
51
                $this->array = $input->toArray();
52
            } else {
53 23
                $input = get_object_vars($input);
54
            }
55
        }
56 55
        if (is_array($input)) {
57 51
            $this->array = new \ArrayObject($input);
58 51
            $this->iterator = $this->array->getIterator();
59
        }
60 55
    }
61
    public function __clone()
62
    {
63
        $this->array = new \ArrayObject(iterator_to_array($this));
64
        $this->stack = [];
65
        $this->iterator = $this->array->getIterator();
66
    }
67 1
    public function __toString()
68
    {
69 1
        return implode(', ', $this->toArray());
70
    }
71 1
    public function serialize() {
72 1
        return serialize($this->toArray());
73
    }
74 1
    public function unserialize($array) {
75 1
        $this->array = new \ArrayObject(unserialize($array));
76 1
        $this->stack = [];
77 1
        $this->iterator = $this->array->getIterator();
78 1
    }
79
80
    /**
81
     * Applies all pending operations
82
     * @return $this
83
     */
84 44
    public function squash() : Collection
85
    {
86 44
        if (count($this->stack) || !($this->array instanceof \ArrayObject)) {
87 16
            $this->array = new \ArrayObject(iterator_to_array($this));
88 16
            $this->stack = [];
89 16
            $this->iterator = $this->array->getIterator();
90
        }
91 44
        return $this;
92
    }
93
    /**
94
     * Get an actual array from the collection
95
     * @return array
96
     */
97 43
    public function toArray() : array
98
    {
99 43
        return $this->squash()->array->getArrayCopy();
0 ignored issues
show
Bug introduced by
The method getArrayCopy does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
100
    }
101
    /**
102
     * Gets the first value in the collection or null if empty
103
     * @return mixed
104
     */
105 3
    public function value()
106
    {
107 3
        foreach ($this as $v) {
108 3
            return $v;
109
        }
110 1
        return null;
111
    }
112
113
    // iterator
114 27
    public function key()
115
    {
116 27
        return $this->key;
117
    }
118 32
    public function current()
119
    {
120 32
        return $this->val;
121
    }
122 32
    public function rewind()
123
    {
124 32
        return $this->iterator->rewind();
125
    }
126 29
    public function next()
127
    {
128 29
        return $this->iterator->next();
129
    }
130 32
    public function valid()
131
    {
132 32
        while ($this->iterator->valid()) {
133 32
            $this->val = $this->iterator->current();
134 32
            $this->key = $this->iterator->key();
135 32
            $con = false;
136 32
            foreach ($this->stack as $action) {
137 12
                if ($action[0] === 'filter') {
138 8
                    if (!call_user_func($action[1], $this->val, $this->key, $this)) {
139 8
                        $con = true;
140 8
                        break;
141
                    }
142
                }
143 12
                if ($action[0] === 'map') {
144 5
                    $this->val = call_user_func($action[1], $this->val, $this->key, $this);
145
                }
146 12
                if ($action[0] === 'mapKey') {
147 12
                    $this->key = call_user_func($action[1], $this->val, $this->key, $this);
148
                }
149
            }
150 32
            if ($con) {
151 8
                $this->iterator->next();
152 8
                continue;
153
            }
154 32
            return true;
155
        }
156 29
        return false;
157
    }
158
159
    // array access
160 1
    public function offsetGet($offset)
161
    {
162 1
        return $this->squash()->array->offsetGet($offset);
0 ignored issues
show
Bug introduced by
The method offsetGet does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
163
    }
164 1
    public function offsetExists($offset)
165
    {
166 1
        return $this->squash()->array->offsetExists($offset);
0 ignored issues
show
Bug introduced by
The method offsetExists does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
167
    }
168 1
    public function offsetUnset($offset)
169
    {
170 1
        return $this->squash()->array->offsetUnset($offset);
0 ignored issues
show
Bug introduced by
The method offsetUnset does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
171
    }
172 1
    public function offsetSet($offset, $value)
173
    {
174 1
        return $this->squash()->array->offsetSet($offset, $value);
0 ignored issues
show
Bug introduced by
The method offsetSet does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
175
    }
176
    public function add($value)
177
    {
178
        $this->squash()->array->append($value);
0 ignored issues
show
Bug introduced by
The method append does only exist in ArrayObject, but not in Iterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
179
        return $this;
180
    }
181
    public function append($value)
182
    {
183
        return $this->add($value);
184
    }
185
    public function remove($value)
186
    {
187
        return $this->filter(function ($v) use ($value) { return $v !== $value; })->squash();
188
    }
189
    /**
190
     * Get the collection length
191
     * @return int
192
     */
193 1
    public function count()
194
    {
195 1
        if (count($this->stack) || !($this->array instanceof \Countable)) {
196
            $this->squash();
197
        }
198 1
        return $this->array->count();
199
    }
200
201
    // mutators
202
    /**
203
     * Filter values from the collection based on a predicate. The callback will receive the value, key and collection
204
     * @param  callable $iterator the predicate
205
     * @return $this
206
     */
207 8
    public function filter(callable $iterator) : Collection
208
    {
209 8
        $this->stack[] = [ 'filter', $iterator ];
210 8
        return $this;
211
    }
212
    /**
213
     * Pass all values of the collection through a mutator callable, which will receive the value, key and collection
214
     * @param  callable $iterator the mutator
215
     * @return $this
216
     */
217 5
    public function map(callable $iterator) : Collection
218
    {
219 5
        $this->stack[] = [ 'map', $iterator ];
220 5
        return $this;
221
    }
222
    /**
223
     * Pass all values of the collection through a key mutator callable, which will receive the value, key and collection
224
     * @param  callable $iterator the mutator
225
     * @return $this
226
     */
227 1
    public function mapKey(callable $iterator) : Collection
228
    {
229 1
        $this->stack[] = [ 'mapKey', $iterator ];
230 1
        return $this;
231
    }
232
    /**
233
     * Clone the current collection and return it.
234
     * @return Collection
235
     */
236
    public function clone() : Collection
237
    {
238 1
        return new static($this->toArray());
239
    }
240
    /**
241
     * Remove all falsy values from the collection (uses filter internally).
242
     * @return $this
243
     */
244 1
    public function compact() : Collection
245
    {
246
        return $this->filter(function ($v) {
247 1
            return !!$v;
248 1
        });
249
    }
250
    /**
251
     * Exclude all listed values from the collection (uses filter internally).
252
     * @param  iterable $values the values to exclude
253
     * @return $this
254
     */
255 3
    public function difference($values) : Collection
256
    {
257 3
        if (!is_array($values)) {
258 1
            $values = iterator_to_array($values);
259
        }
260 3
        $keys = array_keys($values);
261 3
        $isAssoc = $keys !== array_keys($keys);
262
        return $this->filter(function ($v, $k) use ($values, $isAssoc) {
263 3
            return $isAssoc ? 
264 1
                ($index = array_search($v, $values)) === false || $index !== $k :
265 3
                !in_array($v, $values, true);
266 3
        });
267
    }
268
    /**
269
     * Append more values to the collection
270
     * @param  iterable $source the values to add
271
     * @return Collection
272
     */
273 2
    public function extend($source) : Collection
274
    {
275 2
        if (!is_array($source)) {
276 1
            $source = iterator_to_array($source);
277
        }
278 2
        return new static(array_merge($this->toArray(), $source));
279
    }
280
    /**
281
     * Append more values to the collection
282
     * @param  iterable $source the values to add
283
     * @return Collection
284
     */
285 1
    public function merge($source) : Collection
286
    {
287 1
        return $this->extend($source);
288
    }
289
    /**
290
     * Perform a shallow flatten of the collection
291
     * @return Collection
292
     */
293 1
    public function flatten() : Collection
294
    {
295 1
        $rslt = [];
296 1
        $temp = $this->toArray();
297 1
        foreach ($temp as $v) {
298 1
            $rslt = array_merge($rslt, is_array($v) ? $v : [$v]);
299
        }
300 1
        return new static($rslt);
301
    }
302
    /**
303
     * Group by a key (if a callable is used - return the value to group by)
304
     * @param  string|callable $iterator the key to group by
305
     * @return Collection
306
     */
307 1
    public function groupBy($iterator) : Collection
308
    {
309 1
        $rslt = [];
310 1
        $temp = $this->toArray();
311 1
        foreach ($temp as $k => $v) {
312 1
            $rslt[is_string($iterator) ? (is_object($v) ? $v->{$iterator} : $v[$iterator]) : call_user_func($iterator, $v, $k)][] = $v;
313
        }
314 1
        return new static($rslt);
315
    }
316
    /**
317
     * Get the first X items from the collection
318
     * @param  int $count the number of items to include (defaults to 1)
319
     * @return Collection
320
     */
321 1
    public function first(int $count = 1) : Collection
322
    {
323 1
        $i = 0;
324 1
        $new = [];
325 1
        foreach ($this as $k => $v) {
326 1
            if (++$i > $count) {
327 1
                break;
328
            }
329 1
            $new[$k] = $v;
330
        }
331 1
        return new static($new);
332
    }
333
    /**
334
     * Get the first X items from the collection
335
     * @param  int $count the number of items to include (defaults to 1)
336
     * @return Collection
337
     */
338 1
    public function head(int $count = 1) : Collection
339
    {
340 1
        return $this->first($count);
341
    }
342
    /**
343
     * Get the last X items from the collection
344
     * @param  int $count the number of items to include (defaults to 1)
345
     * @return Collection
346
     */
347 2
    public function last(int $count = 1) : Collection
348
    {
349 2
        $new = $this->toArray();
350 2
        return new static(array_slice($new, $count * -1));
351
    }
352
    /**
353
     * Get the first X items from the collection
354
     * @param  int $count the number of items to include (defaults to 1)
355
     * @return Collection
356
     */
357 1
    public function tail(int $count = 1) : Collection
358
    {
359 1
        return $this->last($count);
360
    }
361
    /**
362
     * Get all but the last X items from the collection
363
     * @param  int $count the number of items to exclude (defaults to 1)
364
     * @return Collection
365
     */
366 1
    public function initial(int $count = 1) : Collection
367
    {
368 1
        $new = $this->toArray();
369 1
        return new static(array_slice($new, 0, $count * -1));
370
    }
371
    /**
372
     * Get all but the first X items from the collection
373
     * @param  int $count the number of items to exclude (defaults to 1)
374
     * @return Collection
375
     */
376 1
    public function rest(int $count = 1) : Collection
377
    {
378 1
        $new = $this->toArray();
379 1
        return new static(array_slice($new, $count));
380
    }
381
    /**
382
     * Execute a callable for each item in the collection (does not modify the collection)
383
     * @param  callable $iterator the callable to execute
384
     * @return $this
385
     */
386 1
    public function each(callable $iterator) : Collection
387
    {
388 1
        foreach ($this as $k => $v) {
389 1
            call_user_func($iterator, $v, $k, $this);
390
        }
391 1
        return $this;
392
    }
393
    /**
394
     * Execute a callable for each item in the collection (does not modify the collection)
395
     * @param  callable $iterator the callable to execute
396
     * @return $this
397
     */
398 1
    public function invoke(callable $iterator) : Collection
399
    {
400 1
        return $this->each($iterator);
401
    }
402
    /**
403
     * Get all the collection keys
404
     * @return $this
405
     */
406
    public function keys() : Collection
407
    {
408
        return $this->map(function ($v, $k) { return $k; })->values();
409
    }
410
    /**
411
     * Pluck a value from each object (uses map internally)
412
     * @param  string|int $key the key to extract
413
     * @return $this
414
     */
415 2
    public function pluck($key) : Collection
416
    {
417
        return $this->map(function ($v) use ($key) {
418 2
            return is_object($v) ?
419 1
                (isset($v->{$key}) ? $v->{$key} : null) :
420 2
                (isset($v[$key]) ? $v[$key] : null);
421 2
        });
422
    }
423
    /**
424
     * Intersect the collection with another iterable (uses filter internally)
425
     * @param  interable $values the data to intersect with
426
     * @return $this
427
     */
428 1
    public function intersection($values) : Collection
429
    {
430 1
        if (!is_array($values)) {
431 1
            $values = iterator_to_array($values);
432
        }
433 1
        $keys = array_keys($values);
434 1
        $isAssoc = $keys !== array_keys($keys);
435
        return $this->filter(function ($v, $k) use ($values, $isAssoc) {
436 1
            return $isAssoc ? 
437 1
                array_search($v, $values) === $k :
438 1
                in_array($v, $values, true);
439 1
        });
440
    }
441
    /**
442
     * Reject values on a given predicate (opposite of filter)
443
     * @param  callable $iterator the predicate
444
     * @return $this
445
     */
446 1
    public function reject(callable $iterator) : Collection
447
    {
448
        return $this->filter(function ($v, $k, $array) use ($iterator) {
449 1
            return !call_user_func($iterator, $v, $k, $array);
450 1
        });
451
    }
452
    /**
453
     * Shuffle the values in the collection
454
     * @return Collection
455
     */
456 1
    public function shuffle() : Collection
457
    {
458 1
        $temp = $this->toArray();
459 1
        $keys = array_keys($temp);
460 1
        shuffle($keys);
461 1
        $rslt = [];
462 1
        foreach ($keys as $key) {
463 1
            $rslt[$key] = $temp[$key];
464
        }
465 1
        return new static($rslt);
466
    }
467
    /**
468
     * Sort the collection using a standard sorting function
469
     * @param  callable $iterator the sort function (must return -1, 0 or 1)
470
     * @return Collection
471
     */
472 1
    public function sortBy(callable $iterator) : Collection
473
    {
474 1
        $this->squash();
475 1
        $this->array->uasort($iterator);
476 1
        return $this;
477
    }
478
    /**
479
     * Inspect the whole collection (as an array) mid-chain
480
     * @param  callable $iterator the callable to execute
481
     * @return $this
482
     */
483 1
    public function tap(callable $iterator) : Collection
484
    {
485 1
        call_user_func($iterator, $this->toArray());
486 1
        return $this;
487
    }
488
    /**
489
     * Modify the whole collection (as an array) mid-chain
490
     * @param  callable $iterator the callable to execute
491
     * @return Collection
492
     */
493 1
    public function thru(callable $iterator) : Collection
494
    {
495 1
        $temp = $this->toArray();
496 1
        $rslt = call_user_func($iterator, $temp);
497 1
        return new static($rslt);
498
    }
499
    /**
500
     * Leave only unique items in the collection
501
     * @return Collection
502
     */
503 3
    public function unique() : Collection
504
    {
505 3
        $temp = $this->toArray();
506 3
        $rslt = [];
507 3
        foreach ($temp as $k => $v) {
508 3
            if (!in_array($v, $rslt, true)) {
509 3
                $rslt[$k] = $v;
510
            }
511
        }
512 3
        return new static($rslt);
513
    }
514
    /**
515
     * Get only the values of the collection
516
     * @return Collection
517
     */
518 3
    public function values() : Collection
519
    {
520 3
        return new static(array_values($this->toArray()));
521
    }
522
    /**
523
     * Filter items from the collection using key => value pairs
524
     * @param  array   $properties the key => value to check for in each item
525
     * @param  boolean $strict     should the comparison be strict
526
     * @return $this
527
     */
528
    public function where(array $properties, $strict = true) : Collection
529
    {
530 1
        return $this->filter(function ($v) use ($properties, $strict) {
531 1
            foreach ($properties as $key => $value) {
532 1
                $vv = is_object($v) ? (isset($v->{$key}) ? $v->{$key} : null) : (isset($v[$key]) ? $v[$key] : null);
533 1
                if (is_array($value)) {
534 1
                    if (isset($value['beg']) && isset($value['end'])) {
535 1
                        if ($vv < $value['beg'] || $vv > $value['end']) {
536 1
                            return false;
537
                        }
538 1
                    } elseif (isset($value['lt']) || isset($value['gt']) || isset($value['lte']) || isset($value['gte'])) {
539 1
                        if (isset($value['lt']) && $vv >= $value['lt']) {
540 1
                            return false;
541
                        }
542 1
                        if (isset($value['gt']) && $vv <= $value['gt']) {
543
                            return false;
544
                        }
545 1
                        if (isset($value['lte']) && $vv > $value['lte']) {
546 1
                            return false;
547
                        }
548 1
                        if (isset($value['gte']) && $vv < $value['gte']) {
549 1
                            return false;
550
                        }
551
                    } else {
552 1
                        if (!in_array($vv, $value, $strict)) {
553 1
                            return false;
554
                        }
555
                    }
556
                } else {
557 1
                    if (($strict && $vv !== $value) || (!$strict && $vv != $value)) {
558 1
                        return false;
559
                    }
560
                }
561
            }
562 1
            return true;
563 1
        });
564
    }
565
    /**
566
     * Exclude all listed values from the collection (uses filter internally).
567
     * @param  iterable $values the values to exclude
568
     * @return $this
569
     */
570 2
    public function without($values) : Collection
571
    {
572 2
        return $this->difference($values);
573
    }
574
    /**
575
     * Combine all the values from the collection with a key
576
     * @param  iterable $keys the keys to use
577
     * @return Collection
578
     */
579 2
    public function zip($keys) : Collection
580
    {
581 2
        if (!is_array($keys)) {
582 1
            $keys = iterator_to_array($keys);
583
        }
584 2
        return new static(array_combine($keys, $this->toArray()));
585
    }
586
    /**
587
     * Reverse the collection order
588
     * @return Collection
589
     */
590 2
    public function reverse() : Collection
591
    {
592 2
        return new static(array_reverse($this->toArray()));
593
    }
594
595
    // accessors
596
    /**
597
     * Do all of the items in the collection match a given criteria
598
     * @param  callable $iterator the criteria - should return true / false
599
     * @return bool
600
     */
601 1
    public function all(callable $iterator) : bool
602
    {
603 1
        foreach ($this as $k => $v) {
604 1
            if (!call_user_func($iterator, $v, $k, $this)) {
605 1
                return false;
606
            }
607
        }
608 1
        return true;
609
    }
610
    /**
611
     * Do any of the items in the collection match a given criteria
612
     * @param  callable $iterator the criteria - should return true / false
613
     * @return bool
614
     */
615 1
    public function any(callable $iterator) : bool
616
    {
617 1
        foreach ($this as $k => $v) {
618 1
            if (call_user_func($iterator, $v, $k, $this)) {
619 1
                return true;
620
            }
621
        }
622 1
        return false;
623
    }
624
    /**
625
     * Does the collection contain a given value
626
     * @param  mixed $needle the value to check for
627
     * @return bool
628
     */
629 1
    public function contains($needle) : bool
630
    {
631 1
        foreach ($this as $k => $v) {
632 1
            if ($v === $needle) {
633 1
                return true;
634
            }
635
        }
636 1
        return false;
637
    }
638
    /**
639
     * Get the first element matching a given criteria (or null)
640
     * @param  callable $iterator the filter criteria
641
     * @return mixed
642
     */
643 1
    public function find(callable $iterator)
644
    {
645 1
        foreach ($this as $k => $v) {
646 1
            if (call_user_func($iterator, $v, $k, $this)) {
647 1
                return $v;
648
            }
649
        }
650 1
        return null;
651
    }
652
    /**
653
     * Get all the elements matching a given criteria (with the option to limit the number of results)
654
     * @param  callable $iterator the search criteria
655
     * @param  int|null $limit    optional limit to the number of results (default to null - no limit)
656
     * @return Collection
657
     */
658 1
    public function findAll(callable $iterator, int $limit = null) : Collection
659
    {
660 1
        $res = [];
661 1
        foreach ($this as $k => $v) {
662 1
            if (call_user_func($iterator, $v, $k, $this)) {
663 1
                $res[] = $v;
664
            }
665 1
            if ((int)$limit > 0 && count($res) >= $limit) {
666 1
                break;
667
            }
668
        }
669 1
        return new static($res);
670
    }
671
    /**
672
     * Get the key corresponding to a value (or false)
673
     * @param  mixed  $needle the value to search for
674
     * @return mixed
675
     */
676 1
    public function indexOf($needle)
677
    {
678 1
        return array_search($needle, $this->toArray(), true);
679
    }
680
    /**
681
     * Get the last key corresponding to a value (or false)
682
     * @param  mixed  $needle the value to search for
683
     * @return mixed
684
     */
685 1
    public function lastIndexOf($needle)
686
    {
687 1
        $res = null;
688 1
        foreach ($this as $k => $v) {
689 1
            if ($v === $needle) {
690 1
                $res = $k;
691
            }
692
        }
693 1
        return $res;
694
    }
695
    /**
696
     * Get the number of elements in the collection
697
     * @return int
698
     */
699 1
    public function size() : int
700
    {
701 1
        return $this->count();
702
    }
703
    /**
704
     * Get the minimal item in the collection
705
     * @return mixed
706
     */
707 1
    public function min()
708
    {
709 1
        $min = null;
710 1
        $first = false;
711 1
        foreach ($this as $v) {
712 1
            if (!$first || $v < $min) {
713 1
                $min = $v;
714 1
                $first = true;
715
            }
716
        }
717 1
        return $min;
718
    }
719
    /**
720
     * Get the maximum item in the collection
721
     * @return mixed
722
     */
723 1
    public function max()
724
    {
725 1
        $max = null;
726 1
        $first = false;
727 1
        foreach ($this as $v) {
728 1
            if (!$first || $v > $max) {
729 1
                $max = $v;
730 1
                $first = true;
731
            }
732
        }
733 1
        return $max;
734
    }
735
    /**
736
     * Does the collection contain a given key
737
     * @param  string|int  $key the key to check
738
     * @return bool
739
     */
740 1
    public function has($key) : bool
741
    {
742 1
        return $this->offsetExists($key);
743
    }
744
    /**
745
     * Reduce the collection to a single value
746
     * @param  callable $iterator the reducer (will recieve the carried value, the value, the key and the collection)
747
     * @param  mixed    $initial  the initial value
748
     * @return mixed the final value
749
     */
750 2
    public function reduce(callable $iterator, $initial = null)
751
    {
752 2
        foreach ($this as $k => $v) {
753 2
            $initial = $iterator($initial, $v, $k, $this);
754
        }
755 2
        return $initial;
756
    }
757
    /**
758
     * Reduce the collection to a single value, starting from the last element
759
     * @param  callable $iterator the reducer (will recieve the carried value, the value, the key and the collection)
760
     * @param  mixed    $initial  the initial value
761
     * @return mixed the final value
762
     */
763 1
    public function reduceRight(callable $iterator, $initial = null)
764
    {
765 1
        return $this->reverse()->reduce($iterator, $initial);
766
    }
767
}
768