Completed
Pull Request — master (#10)
by Luke
10:48 queued 26s
created

Collection::getColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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