Completed
Push — master ( 8fc95d...19af0c )
by Luke
05:31
created

Collection::partition()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 1
dl 0
loc 16
ccs 11
cts 11
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Noz\Collection;
3
4
use Countable;
5
use JsonSerializable;
6
use Iterator;
7
use ArrayAccess;
8
use RuntimeException;
9
use Traversable;
10
11
use function Noz\is_traversable,
12
             Noz\to_array;
13
14
/**
15
 * Nozavroni Collection
16
 *
17
 * Basically an array wrapper with a bunch of super useful methods for working with its items and/or create new collections from its items.
18
 *
19
 * @note None of the methods in this class have a $preserveKeys param. That is by design. I don't think it's necessary.
20
 *       Instead, keys are ALWAYS preserved and if you want to NOT preserve keys, simply call Collection::values().
21
 * @todo Scour Laravel's collection methods for ideas (for instance contains($val, $key) to check key as well as value)
22
 *       So I did and the following methods look interesting (think about implementing): tap, times, transform, zip?
23
 *       I also still like the idea of a pairs() method that returns a collection of the collection's key/val pairs as
24
 *       two-item arrays [key, val]. 
25
 */
26
class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable
27
{
28
    /** @var array */
29
    protected $items;
30
31
    /**
32
     * Collection constructor.
33
     *
34
     * @param array $items
35
     */
36 90
    public function __construct(array $items = [])
37
    {
38 90
        $this->items = $items;
39 90
        $this->rewind();
40 90
    }
41
42
    /**
43
     * Generate a collection from an array of items.
44
     * I created this method so that it's possible to extend a collection more easily.
45
     *
46
     * @param array $items
47
     *
48
     * @todo call to_array on $items and don't require input to be an array.
49
     *
50
     * @return Collection
51
     */
52 29
    public static function factory(array $items = [])
53
    {
54 29
        return new Collection($items);
55
    }
56
57
    /**
58
     * Get collection as an array
59
     *
60
     * @return array
61
     */
62 46
    public function toArray()
63
    {
64 46
        return $this->items;
65
    }
66
67
    /**
68
     * Determine if collection has a given key
69
     *
70
     * @param mixed $key The key to look for
71
     *
72
     * @return bool
73
     */
74 45
    public function has($key)
75
    {
76 45
        return isset($this->items[$key]) || array_key_exists($key, $this->items);
77
    }
78
79
    /**
80
     * Does collection have item at position?
81
     *
82
     * Determine if collection has an item at a particular position (indexed from one).
83
     * Position can be positive and start from the beginning or it can be negative and
84
     * start from the end.
85
     *
86
     * @param int $position
87
     *
88
     * @return bool
89
     */
90 2
    public function hasValueAt($position)
91
    {
92
        try {
93 2
            $this->getKeyAt($position);
94 2
            return true;
95 2
        } catch (RuntimeException $e) {
96 2
            return false;
97
        }
98
    }
99
100
    /**
101
     * Get key at given position
102
     *
103
     * Returns the key at the given position, starting from one. Position can be positive (start from beginning) or
104
     * negative (start from the end).
105
     *
106
     * If the position does not exist, a RuntimeException is thrown.
107
     *
108
     * @param int $position
109
     *
110
     * @return string
111
     *
112
     * @throws RuntimeException
113
     */
114 6
    public function getKeyAt($position)
115
    {
116 6
        $collection = $this;
117 6
        if ($position < 0) {
118 3
            $collection = $this->reverse();
119 3
        }
120 6
        $i = 1;
121 6
        foreach ($collection as $key => $val) {
122 6
            if (abs($position) == $i++) {
123 5
                return $key;
124
            }
125 6
        }
126 3
        throw new RuntimeException("No key at position {$position}");
127
    }
128
129
    /**
130
     * Get value at given position
131
     *
132
     * Returns the value at the given position, starting from one. Position can be positive (start from beginning) or
133
     * negative (start from the end).
134
     *
135
     * If the position does not exist, a RuntimeException is thrown.
136
     *
137
     * @param int $position
138
     *
139
     * @return mixed
140
     *
141
     * @throws RuntimeException
142
     */
143 2
    public function getValueAt($position)
144
    {
145 2
        return $this->get($this->getKeyAt($position));
146
    }
147
148
    /**
149
     * Get the key of the first item found matching $item
150
     *
151
     * @todo Perhaps this should allow a callback in place of $item?
152
     *
153
     * @param mixed $item
154
     *
155
     * @return mixed|null
156
     */
157 1
    public function keyOf($item)
158
    {
159 1
        foreach ($this as $key => $val) {
160 1
            if ($item === $val) {
161 1
                return $key;
162
            }
163 1
        }
164
165 1
        return null;
166
    }
167
168
    /**
169
     * Get the offset (index) of the first item found that matches $item
170
     *
171
     * @todo Perhaps this should allow a callback in place of $item?
172
     *
173
     * @param mixed $item
174
     *
175
     * @return int|null
176
     */
177 1
    public function indexOf($item)
178
    {
179 1
        $i = 0;
180 1
        foreach ($this as $key => $val) {
181 1
            if ($item === $val) {
182 1
                return $i;
183
            }
184 1
            $i++;
185 1
        }
186
187 1
        return null;
188
    }
189
190
    /**
191
     * Get item by key, with an optional default return value
192
     *
193
     * @param mixed $key
194
     * @param mixed $default
195
     *
196
     * @return mixed
197
     */
198 8
    public function get($key, $default = null)
199
    {
200 8
        if ($this->has($key)) {
201 8
            return $this->items[$key];
202
        }
203
204 3
        return $default;
205
    }
206
207
    /**
208
     * Add an item with no regard to key
209
     *
210
     * @param mixed $value
211
     *
212
     * @return $this
213
     */
214 8
    public function add($value)
215
    {
216 8
        $this->items[] = $value;
217
218 8
        return $this;
219
    }
220
221
    /**
222
     * Set an item at a given key
223
     *
224
     * @param mixed $key
225
     * @param mixed $value
226
     * @param bool  $overwrite If false, do not overwrite existing key
227
     *
228
     * @return $this
229
     */
230 14
    public function set($key, $value, $overwrite = true)
231
    {
232 14
        if ($overwrite || !$this->has($key)) {
233 14
            $this->items[$key] = $value;
234 14
        }
235
236 14
        return $this;
237
    }
238
239
    /**
240
     * Delete an item by key
241
     *
242
     * @param mixed $key
243
     *
244
     * @return $this
245
     */
246 3
    public function delete($key)
247
    {
248 3
        unset($this->items[$key]);
249
250 3
        return $this;
251
    }
252
253
    /**
254
     * Clear the collection of all its items.
255
     *
256
     * @return $this
257
     */
258 2
    public function clear()
259
    {
260 2
        $this->items = [];
261
262 2
        return $this;
263
    }
264
265
    /**
266
     * Determine if collection contains given value
267
     *
268
     * @param mixed|callable $val
269
     * @param mixed $key
270
     *
271
     * @return bool
272
     */
273 4
    public function contains($val, $key = null)
274
    {
275 4
        $index = 0;
276 4
        foreach ($this as $k => $v) {
277 4
            $matchkey = is_null($key) || $key === $k;
278 4
            if (is_callable($val)) {
279 1
                if ($val($v, $k, $index)) {
280 1
                    return $matchkey;
281
                }
282 1
            } else {
283 3
                if ($val === $v) {
284 3
                    return $matchkey;
285
                }
286
            }
287 4
            $index++;
288 4
        }
289 2
        return false;
290
    }
291
292
    /**
293
     * Fetch item from collection by key and remove it from collection
294
     *
295
     * @param mixed $key
296
     *
297
     * @return mixed
298
     */
299 1
    public function pull($key)
300
    {
301 1
        if ($this->has($key)) {
302 1
            $value = $this->get($key);
303 1
            $this->delete($key);
304 1
            return $value;
305
        }
306 1
    }
307
308
    /**
309
     * Join collection items using a delimiter
310
     *
311
     * @param string $delim
312
     *
313
     * @return string
314
     */
315 1
    public function join($delim = '')
316
    {
317 1
        return implode($delim, $this->items);
318
    }
319
320
    /**
321
     * Determine if collection has any items
322
     *
323
     * @return bool
324
     */
325 4
    public function isEmpty()
326
    {
327 4
        return $this->count() == 0;
328
    }
329
330
    /**
331
     * Get a collection of only this collection's values (without its keys)
332
     *
333
     * @return Collection
334
     */
335 1
    public function values()
336
    {
337 1
        return static::factory(array_values($this->items));
338
    }
339
340
    /**
341
     * Get a collection of only this collection's keys
342
     *
343
     * @return Collection
344
     */
345 1
    public function keys()
346
    {
347 1
        return static::factory(array_keys($this->items));
348
    }
349
350
    /**
351
     * Get a collection with order reversed
352
     *
353
     * @return Collection
354
     */
355 6
    public function reverse()
356
    {
357 6
        return static::factory(array_reverse($this->items));
358
    }
359
360
    /**
361
     * Get a collection with keys and values flipped
362
     *
363
     * @return Collection
364
     */
365 1
    public function flip()
366
    {
367 1
        $collection = static::factory();
368 1
        foreach ($this as $key => $val) {
369 1
            $collection->set($val, $key);
370 1
        }
371 1
        return $collection;
372
    }
373
374
    /**
375
     * Shuffle the order of this collection's values
376
     *
377
     * @return Collection
378
     */
379 1
    public function shuffle()
380
    {
381 1
        shuffle($this->items);
382 1
        return $this;
383
    }
384
385
    /**
386
     * Get a random value from the collection
387
     * 
388
     * @todo might be useful to add a $count param to specify you want $count random items...
389
     *
390
     * @return mixed
391
     */
392 1
    public function random()
393
    {
394 1
        return $this->getValueAt(rand(1, $this->count()));
395
    }
396
397
    /**
398
     * Sort the collection (using values)
399
     *
400
     * @param callable $alg
401
     *
402
     * @return $this
403
     */
404 2
    public function sort(callable $alg = null)
405
    {
406 2
        if (is_null($alg)) {
407
            // case-sensitive string comparison is the default sorting mechanism
408 1
            $alg = 'strcmp';
409 1
        }
410 2
        uasort($this->items, $alg);
411
412 2
        return $this;
413
    }
414
415
    /**
416
     * Sort the collection (using keys)
417
     *
418
     * @param callable $alg
419
     *
420
     * @return $this
421
     */
422 2
    public function ksort(callable $alg = null)
423
    {
424 2
        if (is_null($alg)) {
425
            // case-sensitive string comparison is the default sorting mechanism
426 1
            $alg = 'strcmp';
427 1
        }
428 2
        uksort($this->items, $alg);
429
430 2
        return $this;
431
    }
432
433
    /**
434
     * Append items to collection without regard to keys
435
     *
436
     * @param array|Traversable $items
437
     *
438
     * @return $this
439
     */
440 2
    public function append($items)
441
    {
442 2
        if (!is_traversable($items)) {
443 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
444
        }
445
446 1
        foreach ($items as $val) {
447 1
            $this->add($val);
448 1
        }
449
450 1
        return $this;
451
    }
452
453
    /**
454
     * Return first item or first item where callback returns true
455
     *
456
     * @param callable|null $callback
457
     *
458
     * @return mixed|null
459
     */
460 7
    public function first(callable $callback = null)
461
    {
462 7
        $index = 0;
463 7
        foreach ($this as $key => $val) {
464 7
            if (is_null($callback) || $callback($val, $key, $index++)) {
465 7
                return $val;
466
            }
467 6
        }
468
469
        return null;
470
    }
471
472
    /**
473
     * Return last item or last item where callback returns true
474
     *
475
     * @param callable|null $callback
476
     *
477
     * @return mixed|null
478
     */
479 3
    public function last(callable $callback = null)
480
    {
481 3
        return $this->reverse()->first($callback);
482
    }
483
484
    /**
485
     * Map collection
486
     *
487
     * Create a new collection using the results of a callback function on each item in this collection.
488
     *
489
     * @param callable $callback
490
     *
491
     * @return Collection
492
     */
493 3
    public function map(callable $callback)
494
    {
495 3
        $collection = static::factory();
496
497 3
        $index = 0;
498 3
        foreach ($this as $key => $val) {
499 3
            $collection->set($key, $callback($val, $key, $index++));
500 3
        }
501
502 3
        return $collection;
503
    }
504
505
    /**
506
     * Combine collection with another traversable/collection
507
     *
508
     * Using this collection's keys, and the incoming collection's values, a new collection is created and returned.
509
     *
510
     * @param array|Traversable $items
511
     *
512
     * @return Collection
513
     */
514 5
    public function combine($items)
515
    {
516 5
        if (!is_traversable($items)) {
517 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
518
        }
519
520 4
        $items = to_array($items);
521 4
        if (count($items) != count($this->items)) {
522 1
            throw new RuntimeException("Invalid input for " . __METHOD__ . ", number of items does not match");
523
        }
524
525 3
        return static::factory(array_combine($this->items, $items));
526
    }
527
528
    /**
529
     * Get a new collection with only distinct values
530
     *
531
     * @return Collection
532
     */
533 1
    public function distinct()
534
    {
535 1
        $collection = static::factory();
536 1
        foreach ($this as $key => $val) {
537 1
            if (!$collection->contains($val)) {
538 1
                $collection->set($key, $val);
539 1
            }
540 1
        }
541
542 1
        return $collection;
543
    }
544
545
    /**
546
     * Remove all duplicate values from collection in-place
547
     *
548
     * @return Collection
549
     */
550 1
    public function deduplicate()
551
    {
552 1
        $this->items = array_unique($this->items);
553
554 1
        return $this;
555
    }
556
557
    /**
558
     * Return a new collection with only filtered keys/values
559
     *
560
     * The callback accepts value, key, index and should return true if the item should be added to the returned
561
     * collection
562
     *
563
     * @param callable $callback
564
     *
565
     * @return Collection
566
     */
567 2
    public function filter(callable $callback = null)
568
    {
569 2
        $collection = static::factory();
570 2
        $index = 0;
571 2
        foreach ($this as $key => $value) {
572 2
            if (is_null($callback)) {
573 1
                if ($value) {
574 1
                    $collection->set($key, $value);
575 1
                }
576 1
            } else {
577 1
                if ($callback($value, $key, $index++)) {
578 1
                    $collection->set($key, $value);
579 1
                }
580
            }
581 2
        }
582
583 2
        return $collection;
584
    }
585
586
    /**
587
     * Fold collection into a single value
588
     *
589
     * Loop through collection calling a callback function and passing the result to the next iteration, eventually
590
     * returning a single value.
591
     *
592
     * @param callable $callback
593
     * @param mixed $initial
594
     *
595
     * @return null
596
     */
597 1
    public function fold(callable $callback, $initial = null)
598
    {
599 1
        $index = 0;
600 1
        $folded = $initial;
601 1
        foreach ($this as $key => $val) {
602 1
            $folded = $callback($folded, $val, $key, $index++);
603 1
        }
604
605 1
        return $folded;
606
    }
607
608
    /**
609
     * Return a merge of this collection and $items
610
     *
611
     * @param array|Traversable $items
612
     *
613
     * @return Collection
614
     */
615 3
    public function merge($items)
616
    {
617 3
        if (!is_traversable($items)) {
618 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
619
        }
620
621 2
        $collection = clone $this;
622 2
        foreach ($items as $key => $val) {
623 2
            $collection->set($key, $val);
624 2
        }
625
626 2
        return $collection;
627
    }
628
629
    /**
630
     * Create a new collection with a union of this collection and $items
631
     *
632
     * This method is similar to merge, except that existing items will not be overwritten.
633
     *
634
     * @param $items
635
     */
636 2
    public function union($items)
637
    {
638 2
        if (!is_traversable($items)) {
639 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
640
        }
641
642 1
        $collection = clone $this;
643 1
        foreach ($items as $key => $val) {
644 1
            $collection->set($key, $val, false);
645 1
        }
646
647 1
        return $collection;
648
    }
649
650
    /**
651
     * Call callback for each item in collection, passively
652
     * If at any point the callback returns false, iteration stops.
653
     *
654
     * @param callable $callback
655
     *
656
     * @return $this
657
     */
658 2
    public function each(callable $callback)
659
    {
660 2
        $index = 0;
661 2
        foreach ($this as $key => $val) {
662 2
            if ($callback($val, $key, $index++) === false) {
663 1
                break;
664
            }
665 2
        }
666
667 2
        return $this;
668
    }
669
670
    /**
671
     * Assert callback returns $expected value for each item in collection.
672
     *
673
     * @todo This can be used to easily make methods like all($callback) and none($callback).
674
     *
675
     * @param callable $callback
676
     * @param bool $expected
677
     *
678
     * @return bool
679
     */
680 1
    public function assert(callable $callback, $expected = true)
681
    {
682 1
        $index = 0;
683 1
        foreach ($this as $key => $val) {
684 1
            if ($callback($val, $key, $index++) !== $expected) {
685 1
                return false;
686
            }
687 1
        }
688
689 1
        return true;
690
    }
691
692
    /**
693
     * Pipe collection through a callback
694
     *
695
     * @param callable $callback
696
     *
697
     * @return mixed
698
     */
699 1
    public function pipe(callable $callback)
700
    {
701 1
        return $callback($this);
702
    }
703
704
    /**
705
     * Get new collection in chunks of $size
706
     *
707
     * Creates a new collection of arrays of $size length. The remainder items will be placed at the end.
708
     *
709
     * @param int $size
710
     *
711
     * @return Collection
712
     */
713 2
    public function chunk($size)
714
    {
715 2
        return static::factory(array_chunk($this->items, $size, true));
716
    }
717
718
    /**
719
     * Get a new collection of $count chunks
720
     *
721
     * @todo It might be useful to have a method that spreads remainder items more evenly so you don't end up with the
722
     *       last item containing only one or two items.
723
     *
724
     * @param int $count
725
     *
726
     * @return Collection
727
     */
728 1
    public function split($count = 1)
729
    {
730 1
        return $this->chunk(ceil($this->count() / $count));
731
    }
732
733
    /**
734
     * Get a slice of this collection.
735
     *
736
     * @param int $offset
737
     * @param int|null $length
738
     *
739
     * @return Collection
740
     */
741 1
    public function slice($offset, $length = null)
742
    {
743 1
        return static::factory(array_slice($this->items, $offset, $length, true));
744
    }
745
746
    /**
747
     * Get collection with only differing items
748
     *
749
     * @param array|Traversable $items
750
     *
751
     * @return Collection
752
     */
753 1
    public function diff($items)
754
    {
755 1
        return static::factory(array_diff($this->items, to_array($items)));
756
    }
757
758
    /**
759
     * Get collection with only differing items (by key)
760
     *
761
     * @param array|Traversable $items
762
     *
763
     * @return Collection
764
     */
765 1
    public function kdiff($items)
766
    {
767 1
        return static::factory(array_diff_key($this->items, to_array($items)));
768
    }
769
770
    /**
771
     * Get collection with only intersecting items
772
     *
773
     * @param array|Traversable $items
774
     *
775
     * @return Collection
776
     */
777 1
    public function intersect($items)
778
    {
779 1
        return static::factory(array_intersect($this->items, to_array($items)));
780
    }
781
782
    /**
783
     * Get collection with only intersecting items (by key)
784
     *
785
     * @param array|Traversable $items
786
     *
787
     * @return Collection
788
     */
789 1
    public function kintersect($items)
790
    {
791 1
        return static::factory(array_intersect_key($this->items, to_array($items)));
792
    }
793
794
    /**
795
     * Remove last item in collection and return it
796
     *
797
     * @return mixed
798
     */
799 1
    public function pop()
800
    {
801 1
        return array_pop($this->items);
802
    }
803
804
    /**
805
     * Remove first item in collection and return it (and re-index if numerically indexed)
806
     *
807
     * @return mixed
808
     */
809 2
    public function shift()
810
    {
811 2
        return array_shift($this->items);
812
    }
813
814
    /**
815
     * Add item to the end of the collection
816
     *
817
     * @note This method is no different than add() but I included it for consistency's sake since I have the others
818
     *
819
     * @param mixed $item
820
     *
821
     * @return $this
822
     */
823 1
    public function push($item)
824
    {
825 1
        return $this->add($item);
826
    }
827
828
    /**
829
     * Add item to the beginning of the collection (and re-index if a numerically indexed collection)
830
     *
831
     * @param mixed $item
832
     *
833
     * @return $this
834
     */
835 5
    public function unshift($item)
836
    {
837 5
        array_unshift($this->items, $item);
838
839 5
        return $this;
840
    }
841
842
    /**
843
     * Get new collection padded to specified $size with $value
844
     *
845
     * Using $value, pad the collection to specified $size. If $size is smaller or equal to the size of the collection,
846
     * then no padding takes place. If $size is positive, padding is added to the end, while if negative, padding will
847
     * be added to the beginning.
848
     *
849
     * @param int $size
850
     * @param mixed $value
851
     *
852
     * @return Collection
853
     */
854 3
    public function pad($size, $value = null)
855
    {
856 3
        $collection = clone $this;
857 3
        while ($collection->count() < abs($size)) {
858 3
            if ($size > 0) {
859 3
                $collection->add($value);
860 3
            } else {
861 3
                $collection->unshift($value);
862
            }
863 3
        }
864
865 3
        return $collection;
866
    }
867
868
    /**
869
     * Partition collection into two collections using a callback
870
     *
871
     * Iterates over each element in the collection with a callback. Items where callback returns true are placed in one
872
     * collection and the rest in another. Finally, the two collections are placed in an array and returned for easy use
873
     * with the list() function. ( list($a, $b) = $col->partition(function($val, $key, $index) {}) )
874
     *
875
     * @param callable $callback
876
     *
877
     * @return array<Collection>
878
     */
879 2
    public function partition(callable $callback)
880
    {
881 2
        $pass = static::factory();
882 2
        $fail = static::factory();
883
884 2
        $index = 0;
885 2
        foreach ($this as $key => $val) {
886 1
            if ($callback($val, $key, $index++)) {
887 1
                $pass->set($key, $val);
888 1
            } else {
889 1
                $fail->set($key, $val);
890
            }
891 2
        }
892
893 2
        return [$pass, $fail];
894
    }
895
896
    /** ++++                  ++++ **/
897
    /** ++ Interface Compliance ++ **/
898
    /** ++++                  ++++ **/
899
900
    /**
901
     * @return array
902
     */
903 1
    public function jsonSerialize()
904
    {
905 1
        return $this->toArray();
906
    }
907
908
    /** ++++                  ++++ **/
909
    /** ++ Array Access Methods ++ **/
910
    /** ++++                  ++++ **/
911
912
    /**
913
     * {@inheritDoc}
914
     */
915 1
    public function offsetExists($offset)
916
    {
917 1
        return $this->has($offset);
918
    }
919
920
    /**
921
     * {@inheritDoc}
922
     */
923 2
    public function offsetGet($offset)
924
    {
925 2
        if (!$this->has($offset)) {
926 1
            throw new RuntimeException("Unknown offset: {$offset}");
927
        }
928
929 1
        return $this->get($offset);
930
    }
931
932
    /**
933
     * {@inheritDoc}
934
     */
935 1
    public function offsetUnset($offset)
936
    {
937 1
        $this->delete($offset);
938 1
    }
939
940
    /**
941
     * {@inheritDoc}
942
     */
943 1
    public function offsetSet($offset, $value)
944
    {
945 1
        if (!isset($offset)) {
946 1
            $this->add($value);
947 1
        }
948
949 1
        $this->set($offset, $value);
950 1
    }
951
952
    /** ++++                  ++++ **/
953
    /** ++   Iterator Methods   ++ **/
954
    /** ++++                  ++++ **/
955
956
    /**
957
     * {@inheritDoc}
958
     */
959 33
    public function current()
960
    {
961 33
        return current($this->items);
962
    }
963
964
    /**
965
     * {@inheritDoc}
966
     */
967 33
    public function key()
968
    {
969 33
        return key($this->items);
970
    }
971
972
    /**
973
     * {@inheritDoc}
974
     */
975 32
    public function next()
976
    {
977 32
        return next($this->items);
978
    }
979
980
    /**
981
     * {@inheritDoc}
982
     *
983
     * @todo Should this return $this?
984
     */
985 90
    public function rewind()
986
    {
987 90
        reset($this->items);
988 90
    }
989
990
    /**
991
     * {@inheritDoc}
992
     */
993 34
    public function valid()
994
    {
995 34
        return $this->has(key($this->items));
996
    }
997
998
    /** ++++                  ++++ **/
999
    /** ++   Countable Method   ++ **/
1000
    /** ++++                  ++++ **/
1001
1002
    /**
1003
     * {@inheritDoc}
1004
     */
1005 10
    public function count()
1006
    {
1007 10
        return count($this->items);
1008
    }
1009
}