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