Completed
Pull Request — master (#10)
by Luke
09:27
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 100
    public function __construct(array $items = [])
44
    {
45 100
        $this->items = $items;
46 100
        $this->rewind();
47 100
    }
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 37
    public static function factory($items = null)
58
    {
59 37
        if (is_null($items)) {
60 10
            $items = [];
61 10
        }
62 37
        return new Collection(to_array($items));
63
    }
64
65
    /**
66
     * Get collection as an array
67
     *
68
     * @return array
69
     */
70 50
    public function toArray()
71
    {
72 50
        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 50
    public function has($key)
83
    {
84 50
        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 14
    public function set($key, $value, $overwrite = true)
251
    {
252 14
        if ($overwrite || !$this->has($key)) {
253 14
            $this->items[$key] = $value;
254 14
        }
255
256 14
        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 1
    public function values()
363
    {
364 1
        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 with order reversed
382
     *
383
     * @return Collection
384
     */
385 6
    public function reverse()
386
    {
387 6
        return static::factory(array_reverse($this->items));
388
    }
389
390
    /**
391
     * Get a collection with keys and values flipped.
392
     *
393
     * Returns a new collection containing the keys as values and the values as keys.
394
     *
395
     * @return Collection
396
     */
397 1
    public function flip()
398
    {
399 1
        $collection = static::factory();
400 1
        foreach ($this as $key => $val) {
401 1
            $collection->set($val, $key);
402 1
        }
403 1
        return $collection;
404
    }
405
406
    /**
407
     * Shuffle (randomize) the order of this collection's values (in-place)
408
     *
409
     * @return Collection
410
     */
411 1
    public function shuffle()
412
    {
413 1
        shuffle($this->items);
414 1
        return $this;
415
    }
416
417
    /**
418
     * Get a random value from the collection
419
     * 
420
     * @return mixed
421
     */
422 1
    public function random()
423
    {
424 1
        return $this->getValueAt(rand(1, $this->count()));
425
    }
426
427
    /**
428
     * Sort the collection's values (in-place)
429
     *
430
     * Sorts the collection by value using the provided algorithm (which can be either the name of a native php function
431
     * or a callable).
432
     *
433
     * @param callable $alg The sorting algorithm (defaults to strcmp)
434
     *
435
     * @return self
436
     */
437 2
    public function sort(callable $alg = null)
438
    {
439 2
        if (is_null($alg)) {
440
            // case-sensitive string comparison is the default sorting mechanism
441 1
            $alg = 'strcmp';
442 1
        }
443 2
        uasort($this->items, $alg);
444
445 2
        return $this;
446
    }
447
448
    /**
449
     * Sort the collection's keys (in-place)
450
     *
451
     * Sorts the collection by key using the provided algorithm (which can be either the name of a native php function
452
     * or a callable).
453
     *
454
     * @param callable $alg The sorting algorithm (defaults to strcmp)
455
     *
456
     * @return self
457
     */
458 2
    public function ksort(callable $alg = null)
459
    {
460 2
        if (is_null($alg)) {
461
            // case-sensitive string comparison is the default sorting mechanism
462 1
            $alg = 'strcmp';
463 1
        }
464 2
        uksort($this->items, $alg);
465
466 2
        return $this;
467
    }
468
469
    /**
470
     * Append items to collection without regard to key
471
     *
472
     * @param array|Traversable $items A list of values to append to the collection
473
     *
474
     * @return self
475
     */
476 2
    public function append($items)
477
    {
478 2
        if (!is_traversable($items)) {
479 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
480
        }
481
482 1
        foreach ($items as $val) {
483 1
            $this->add($val);
484 1
        }
485
486 1
        return $this;
487
    }
488
489
    /**
490
     * Return first item or first item where callback returns true
491
     *
492
     * Returns the first item in the collection or, if passed a callback, the first item that causes the callback to
493
     * return true.
494
     *
495
     * @param callable|null $callback A callback to compare items with
496
     *
497
     * @return mixed|null
498
     */
499 8
    public function first(callable $callback = null)
500
    {
501 8
        $index = 0;
502 8
        foreach ($this as $key => $val) {
503 8
            if (is_null($callback) || $callback($val, $key, $index++)) {
504 8
                return $val;
505
            }
506 6
        }
507
508
        return null;
509
    }
510
511
    /**
512
     * Return last item or last item where callback returns true
513
     *
514
     * Returns the last item in the collection or, if passed a callback, the last item that causes the callback to
515
     * return true.
516
     *
517
     * @param callable|null $callback A callback to compare items with
518
     *
519
     * @return mixed|null
520
     */
521 3
    public function last(callable $callback = null)
522
    {
523 3
        return $this->reverse()->first($callback);
524
    }
525
526
    /**
527
     * Map collection
528
     *
529
     * Create a new collection using the results of a callback function on each item in this collection.
530
     *
531
     * @param callable $callback A callback to generate the new values
532
     *
533
     * @return Collection
534
     */
535 3
    public function map(callable $callback)
536
    {
537 3
        $collection = static::factory();
538
539 3
        $index = 0;
540 3
        foreach ($this as $key => $val) {
541 3
            $collection->set($key, $callback($val, $key, $index++));
542 3
        }
543
544 3
        return $collection;
545
    }
546
547
    /**
548
     * Combine collection with another traversable/collection
549
     *
550
     * Using this collection's keys, and the incoming collection's values, a new collection is created and returned.
551
     *
552
     * @param array|Traversable $items The values to combine with this collection's keys
553
     *
554
     * @return Collection
555
     */
556 5
    public function combine($items)
557
    {
558 5
        if (!is_traversable($items)) {
559 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
560
        }
561
562 4
        $items = to_array($items);
563 4
        if (count($items) != count($this->items)) {
564 1
            throw new RuntimeException("Invalid input for " . __METHOD__ . ", number of items does not match");
565
        }
566
567 3
        return static::factory(array_combine($this->items, $items));
568
    }
569
570
    /**
571
     * Get a new collection with only distinct values
572
     *
573
     * @return Collection
574
     */
575 1
    public function distinct()
576
    {
577 1
        $collection = static::factory();
578 1
        foreach ($this as $key => $val) {
579 1
            if (!$collection->contains($val)) {
580 1
                $collection->set($key, $val);
581 1
            }
582 1
        }
583
584 1
        return $collection;
585
    }
586
587
    /**
588
     * Remove all duplicate values from collection (in-place)
589
     *
590
     * @return Collection
591
     */
592 1
    public function deduplicate()
593
    {
594 1
        $this->items = array_unique($this->items);
595
596 1
        return $this;
597
    }
598
599
    /**
600
     * Return a new collection with only filtered keys/values
601
     *
602
     * The callback accepts value, key, index and should return true if the item should be added to the returned
603
     * collection
604
     *
605
     * @param callable $callback The callback used to filter with
606
     *
607
     * @return Collection
608
     */
609 2
    public function filter(callable $callback = null)
610
    {
611 2
        $collection = static::factory();
612 2
        $index = 0;
613 2
        foreach ($this as $key => $value) {
614 2
            if (is_null($callback)) {
615 1
                if ($value) {
616 1
                    $collection->set($key, $value);
617 1
                }
618 1
            } else {
619 1
                if ($callback($value, $key, $index++)) {
620 1
                    $collection->set($key, $value);
621 1
                }
622
            }
623 2
        }
624
625 2
        return $collection;
626
    }
627
628
    /**
629
     * Fold collection into a single value (a.k.a. reduce)
630
     *
631
     * Loop through collection calling a callback function and passing the result to the next iteration, eventually
632
     * returning a single value.
633
     *
634
     * @param callable $callback The callback used to "fold" or "reduce" the collection into a single value
635
     * @param mixed $initial The (optional) initial value to pass to the callback
636
     *
637
     * @return null
638
     */
639 1
    public function fold(callable $callback, $initial = null)
640
    {
641 1
        $index = 0;
642 1
        $folded = $initial;
643 1
        foreach ($this as $key => $val) {
644 1
            $folded = $callback($folded, $val, $key, $index++);
645 1
        }
646
647 1
        return $folded;
648
    }
649
650
    /**
651
     * Return a merge of this collection and $items
652
     *
653
     * Returns a new collection with a merge of this collection and $items. Values from $items will overwrite values in
654
     * the current collection.
655
     *
656
     * @param array|Traversable $items The items to merge with the collection
657
     *
658
     * @return Collection
659
     */
660 3
    public function merge($items)
661
    {
662 3
        if (!is_traversable($items)) {
663 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
664
        }
665
666 2
        $collection = clone $this;
667 2
        foreach ($items as $key => $val) {
668 2
            $collection->set($key, $val);
669 2
        }
670
671 2
        return $collection;
672
    }
673
674
    /**
675
     * Create a new collection with a union of this collection and $items
676
     *
677
     * This method is similar to merge, except that existing values will not be overwritten.
678
     *
679
     * @param $items
680
     */
681 2
    public function union($items)
682
    {
683 2
        if (!is_traversable($items)) {
684 1
            throw new RuntimeException("Invalid input type for " . __METHOD__ . ", must be array or Traversable");
685
        }
686
687 1
        $collection = clone $this;
688 1
        foreach ($items as $key => $val) {
689 1
            $collection->set($key, $val, false);
690 1
        }
691
692 1
        return $collection;
693
    }
694
695
    /**
696
     * Call callback for each item in collection, passively
697
     *
698
     * If at any point the callback returns false, iteration stops.
699
     *
700
     * @param callable $callback The callback to use on each item in the collection
701
     *
702
     * @return self
703
     */
704 2
    public function each(callable $callback)
705
    {
706 2
        $index = 0;
707 2
        foreach ($this as $key => $val) {
708 2
            if ($callback($val, $key, $index++) === false) {
709 1
                break;
710
            }
711 2
        }
712
713 2
        return $this;
714
    }
715
716
    /**
717
     * Assert callback returns $expected value for each item in collection.
718
     *
719
     * This method will loop over each item in the collection, passing them to the callback. If the callback doesn't
720
     * return $expected value for every item in the collection, it will return false.
721
     *
722
     * @param callable $callback Assertion callback
723
     * @param bool $expected Expected value from callback
724
     *
725
     * @return bool
726
     */
727 2
    public function assert(callable $callback, $expected = true)
728
    {
729 2
        $index = 0;
730 2
        foreach ($this as $key => $val) {
731 2
            if ($callback($val, $key, $index++) !== $expected) {
732 2
                return false;
733
            }
734 2
        }
735
736 2
        return true;
737
    }
738
739
    /**
740
     * Pipe collection through a callback
741
     *
742
     * Simply passes the collection as an argument to the given callback.
743
     *
744
     * @param callable $callback The callback function
745
     *
746
     * @return mixed
747
     */
748 1
    public function pipe(callable $callback)
749
    {
750 1
        return $callback($this);
751
    }
752
753
    /**
754
     * Get new collection in chunks of $size
755
     *
756
     * Creates a new collection of arrays of $size length. The remainder items will be placed at the end.
757
     *
758
     * @param int $size The size of the arrays you want returned
759
     *
760
     * @return Collection
761
     */
762 2
    public function chunk($size)
763
    {
764 2
        return static::factory(array_chunk($this->items, $size, true));
765
    }
766
767
    /**
768
     * Get a new collection of $count chunks
769
     *
770
     * Returns a collection of $count number of equally-sized arrays, placing remainders at the end.
771
     *
772
     * @param int $count The number of arrays you want returned
773
     *
774
     * @return Collection
775
     */
776 1
    public function split($count = 1)
777
    {
778 1
        return $this->chunk(ceil($this->count() / $count));
779
    }
780
781
    /**
782
     * Get a slice of this collection.
783
     *
784
     * Returns a collection with a slice of this collection's items, starting at $offset and continuing until $length
785
     *
786
     * @param int $offset The offset at which you want the slice to begin
787
     * @param int|null $length The length of the slice (number of items)
788
     *
789
     * @return Collection
790
     */
791 1
    public function slice($offset, $length = null)
792
    {
793 1
        return static::factory(array_slice($this->items, $offset, $length, true));
794
    }
795
796
    /**
797
     * Get collection with only differing items
798
     *
799
     * Returns a collection containing only the items not present in *both* this collection and $items.
800
     *
801
     * @param array|Traversable $items The items to compare with
802
     *
803
     * @return Collection
804
     */
805 1
    public function diff($items)
806
    {
807 1
        return static::factory(array_diff($this->items, to_array($items)));
808
    }
809
810
    /**
811
     * Get collection with only differing items (by key)
812
     *
813
     * Returns a collection containing only the values whose keys are 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 2
    public function kdiff($items)
820
    {
821 2
        return static::factory(array_diff_key($this->items, to_array($items)));
822
    }
823
824
    /**
825
     * Get collection with only intersecting items
826
     *
827
     * Returns a collection containing only the values present in *both* this collection and $items
828
     *
829
     * @param array|Traversable $items The items to compare with
830
     *
831
     * @return Collection
832
     */
833 1
    public function intersect($items)
834
    {
835 1
        return static::factory(array_intersect($this->items, to_array($items)));
836
    }
837
838
    /**
839
     * Get collection with only intersecting items (by key)
840
     *
841
     * Returns a collection containing only the values whose keys are 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 kintersect($items)
848
    {
849 1
        return static::factory(array_intersect_key($this->items, to_array($items)));
850
    }
851
852
    /**
853
     * Remove last item in collection and return it
854
     *
855
     * @return mixed
856
     */
857 1
    public function pop()
858
    {
859 1
        return array_pop($this->items);
860
    }
861
862
    /**
863
     * Remove first item in collection and return it
864
     *
865
     * If the collection is numerically indexed, this method will re-index it from 0 after returning the item.
866
     *
867
     * @return mixed
868
     */
869 2
    public function shift()
870
    {
871 2
        return array_shift($this->items);
872
    }
873
874
    /**
875
     * Add item to the end of the collection
876
     *
877
     * @note This method is no different than add() but I included it for consistency's sake since I have the others
878
     *
879
     * @param mixed $item The item to add to the collection
880
     *
881
     * @return self
882
     */
883 1
    public function push($item)
884
    {
885 1
        return $this->add($item);
886
    }
887
888
    /**
889
     * Add item to the beginning of the collection
890
     *
891
     * The collection will be re-indexed if it has numeric keys.
892
     *
893
     * @param mixed $item The item to add to the collection
894
     *
895
     * @return self
896
     */
897 5
    public function unshift($item)
898
    {
899 5
        array_unshift($this->items, $item);
900
901 5
        return $this;
902
    }
903
904
    /**
905
     * Get new collection padded to specified $size with $value
906
     *
907
     * Using $value, pad the collection to specified $size. If $size is smaller or equal to the size of the collection,
908
     * then no padding takes place. If $size is positive, padding is added to the end, while if negative, padding will
909
     * be added to the beginning.
910
     *
911
     * @param int $size The number of items collection should have
912
     * @param mixed $value The value to pad with
913
     *
914
     * @return Collection
915
     */
916 3
    public function pad($size, $value = null)
917
    {
918 3
        $collection = clone $this;
919 3
        while ($collection->count() < abs($size)) {
920 3
            if ($size > 0) {
921 3
                $collection->add($value);
922 3
            } else {
923 3
                $collection->unshift($value);
924
            }
925 3
        }
926
927 3
        return $collection;
928
    }
929
930
    /**
931
     * Partition collection into two collections using a callback
932
     *
933
     * Iterates over each element in the collection with a callback. Items where callback returns true are placed in one
934
     * collection and the rest in another. Finally, the two collections are placed in an array and returned for easy use
935
     * with the list() function. ( list($a, $b) = $col->partition(function($val, $key, $index) {}) )
936
     *
937
     * @param callable $callback The comparison callback
938
     *
939
     * @return Collection[]
940
     */
941 2
    public function partition(callable $callback)
942
    {
943 2
        $pass = static::factory();
944 2
        $fail = static::factory();
945
946 2
        $index = 0;
947 2
        foreach ($this as $key => $val) {
948 1
            if ($callback($val, $key, $index++)) {
949 1
                $pass->set($key, $val);
950 1
            } else {
951 1
                $fail->set($key, $val);
952
            }
953 2
        }
954
955 2
        return [$pass, $fail];
956
    }
957
958
    /**
959
     * Get column values by key
960
     *
961
     * This method expects the collection's data to be tabular in nature (two-dimensional and for the rows to have
962
     * consistently named keys). If the data is not structured this way, it will do the best it can but it is not meant
963
     * for unstructured, non-tabular data so don't expect consistent results.
964
     *
965
     * @param string|int $column The key of the column you want to get
966
     *
967
     * @return Collection
968
     */
969 3
    public function getColumn($column)
970
    {
971 3
        return static::factory(array_column($this->items, $column));
972
    }
973
974
    /**
975
     * Is collection tabular?
976
     *
977
     * Returns true if the data in the collection is tabular in nature, meaning it is at least two-dimensional and each
978
     * row contains the same number of values with the same keys.
979
     *
980
     * @return bool
981
     */
982 1
    public function isTabular()
983
    {
984 1
        $first = $this->first();
985 1
        return $this->assert(function($row) use ($first) {
986 1
            if (!is_traversable(($first)) || !is_traversable($row)) {
987 1
                return false;
988
            }
989 1
            return Collection::factory($row)
990 1
                ->kdiff($first)
991 1
                ->isEmpty();
992 1
        });
993
    }
994
995
    /** ++++                  ++++ **/
996
    /** ++ Interface Compliance ++ **/
997
    /** ++++                  ++++ **/
998
999
    /**
1000
     * JSON serialize
1001
     *
1002
     * @return array
1003
     */
1004 1
    public function jsonSerialize()
1005
    {
1006 1
        return $this->toArray();
1007
    }
1008
1009
    /** ++++                  ++++ **/
1010
    /** ++ Array Access Methods ++ **/
1011
    /** ++++                  ++++ **/
1012
1013
    /**
1014
     * Does offset exist?
1015
     *
1016
     * @param mixed $offset
1017
     *
1018
     * @return bool
1019
     */
1020 1
    public function offsetExists($offset)
1021
    {
1022 1
        return $this->has($offset);
1023
    }
1024
1025
    /**
1026
     * Get item at offset
1027
     *
1028
     * @param mixed $offset
1029
     *
1030
     * @return mixed
1031
     */
1032 2
    public function offsetGet($offset)
1033
    {
1034 2
        if (!$this->has($offset)) {
1035 1
            throw new RuntimeException("Unknown offset: {$offset}");
1036
        }
1037
1038 1
        return $this->get($offset);
1039
    }
1040
1041
    /**
1042
     * Unset item at offset
1043
     *
1044
     * @param mixed $offset
1045
     *
1046
     * @return void
1047
     */
1048 1
    public function offsetUnset($offset)
1049
    {
1050 1
        $this->delete($offset);
1051 1
    }
1052
1053
    /**
1054
     * Set item at offset
1055
     *
1056
     * @param mixed $offset
1057
     * @param mixed $value
1058
     *
1059
     * @return self
1060
     */
1061 1
    public function offsetSet($offset, $value)
1062
    {
1063 1
        if (!isset($offset)) {
1064 1
            $this->add($value);
1065 1
        }
1066
1067 1
        $this->set($offset, $value);
1068 1
    }
1069
1070
    /** ++++                  ++++ **/
1071
    /** ++   Iterator Methods   ++ **/
1072
    /** ++++                  ++++ **/
1073
1074
    /**
1075
     * @ignore
1076
     */
1077 38
    public function current()
1078
    {
1079 38
        return current($this->items);
1080
    }
1081
1082
    /**
1083
     * @ignore
1084
     */
1085 38
    public function key()
1086
    {
1087 38
        return key($this->items);
1088
    }
1089
1090
    /**
1091
     * @ignore
1092
     */
1093 37
    public function next()
1094
    {
1095 37
        return next($this->items);
1096
    }
1097
1098
    /**
1099
     * @ignore
1100
     */
1101 100
    public function rewind()
1102
    {
1103 100
        reset($this->items);
1104 100
    }
1105
1106
    /**
1107
     * @ignore
1108
     */
1109 39
    public function valid()
1110
    {
1111 39
        return $this->has(key($this->items));
1112
    }
1113
1114
    /** ++++                  ++++ **/
1115
    /** ++   Countable Method   ++ **/
1116
    /** ++++                  ++++ **/
1117
1118
    /**
1119
     * Get number of items in the collection
1120
     *
1121
     * @return int
1122
     */
1123 11
    public function count()
1124
    {
1125 11
        return count($this->items);
1126
    }
1127
}