Completed
Pull Request — master (#49)
by Luke
02:03
created

Collection::isNumeric()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 4
nop 0
dl 0
loc 13
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/*
3
 * Nozavroni/Collections
4
 * Just another collections library for PHP5.6+.
5
 *
6
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
7
 * @author    Luke Visinoni <[email protected]>
8
 * @license   https://github.com/nozavroni/collections/blob/master/LICENSE The MIT License (MIT)
9
 */
10
namespace Noz\Collection;
11
12
use ArrayAccess;
13
use ArrayIterator;
14
use Countable;
15
use InvalidArgumentException;
16
use Iterator;
17
use Noz\Contracts\ArrayableInterface;
18
use Noz\Contracts\CollectionInterface;
19
use OutOfBoundsException;
20
use Traversable;
21
22
use Noz\Traits\IsArrayable;
23
use Noz\Traits\IsImmutable;
24
25
use function
26
    Noz\is_traversable,
27
    Noz\typeof,
28
    Noz\collect;
29
30
31
/**
32
 * Class Collection.
33
 *
34
 * This is the abstract class that all other collection classes are based on.
35
 * Although it's possible to use a completely custom Collection class by simply
36
 * implementing the "Collectable" interface, extending this class gives you a
37
 * whole slew of convenient methods for free.
38
 *
39
 * @package Noz\Collection
40
 *
41
 * @author Luke Visinoni <[email protected]>
42
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
43
 *
44
 * @todo Implement Serializable, other Interfaces
45
 */
46
class Collection implements
47
    CollectionInterface,
48
    ArrayableInterface,
49
    Countable,
50
    Iterator
51
{
52
    use IsArrayable;
53
    use IsImmutable;
54
55
    /**
56
     * @var array The collection of data this object represents
57
     */
58
    private $data = [];
59
60
    /**
61
     * @var bool True unless we have advanced past the end of the data array
62
     */
63
    protected $isValid = true;
64
65
    /**
66
     * AbstractCollection constructor.
67
     *
68
     * @param mixed $data The data to wrap
69
     */
70
    public function __construct($data = null)
71
    {
72
        if (is_null($data)) {
73
            $data = [];
74
        }
75
        if (!is_traversable($data)) {
76
            throw new InvalidArgumentException(sprintf(
77
                'Invalid input for %s. Expecting traversable data, got "%s".',
78
                __METHOD__,
79
                typeof($data)
80
            ));
81
        }
82
        $this->setData($data);
83
    }
84
85
    /**
86
     * Set underlying data array.
87
     *
88
     * Sets the collection data. This method should NEVER be called anywhere other than in __construct().
89
     *
90
     * @param array|Traversable $data The data to wrap
91
     */
92
    private function setData($data)
93
    {
94
        foreach ($data as $index => $value) {
95
            $this->data[$index] = $value;
96
        }
97
        reset($this->data);
98
    }
99
100
    /**
101
     * Get copy of underlying data array.
102
     *
103
     * Returns a copy of this collection's underlying data array. It returns a copy because collections are supposed to
104
     * be immutable. Nothing outside of the constructor should ever have direct access to the actual underlying array.
105
     *
106
     * @return array
107
     */
108
    protected function getData()
109
    {
110
        return $data = $this->data;
0 ignored issues
show
Unused Code introduced by
$data is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
111
    }
112
113
    /**
114
     * @inheritDoc
115
     */
116
    public function count(callable $callback = null)
117
    {
118
        if (!is_null($callback)) {
119
            return $this->filter($callback)->count();
120
        }
121
        return count($this->getData());
122
    }
123
124
    /**
125
     * Return the current element.
126
     *
127
     * Returns the current element in the collection. The internal array pointer
128
     * of the data array wrapped by the collection should not be advanced by this
129
     * method. No side effects. Return current element only.
130
     *
131
     * @return mixed
132
     */
133
    public function current()
134
    {
135
        return current($this->data);
136
    }
137
138
    /**
139
     * Return the current key.
140
     *
141
     * Returns the current key in the collection. No side effects.
142
     *
143
     * @return mixed
144
     */
145
    public function key()
146
    {
147
        return key($this->data);
148
    }
149
150
    /**
151
     * Advance the internal pointer forward.
152
     *
153
     * Although this method will return the current value after advancing the
154
     * pointer, you should not expect it to. The interface does not require it
155
     * to return any value at all.
156
     *
157
     * @return mixed
158
     */
159
    public function next()
160
    {
161
        $next = next($this->data);
162
        $key  = key($this->data);
163
        if (isset($key)) {
164
            return $next;
165
        }
166
        $this->isValid = false;
167
    }
168
169
    /**
170
     * Rewind the internal pointer.
171
     *
172
     * Return the internal pointer to the first element in the collection. Again,
173
     * this method is not required to return anything by its interface, so you
174
     * should not count on a return value.
175
     *
176
     * @return mixed
177
     */
178
    public function rewind()
179
    {
180
        $this->isValid = !empty($this->data);
181
182
        return reset($this->data);
183
    }
184
185
    /**
186
     * Is internal pointer in a valid position?
187
     *
188
     * If the internal pointer is advanced beyond the end of the collection, this method will return false.
189
     *
190
     * @return bool True if internal pointer isn't past the end
191
     */
192
    public function valid()
193
    {
194
        return $this->isValid;
195
    }
196
197
    /**
198
     * @inheritDoc
199
     */
200
    public function sort($alg = null)
201
    {
202
        if (is_null($alg)) {
203
            $alg = 'strnatcasecmp';
204
        }
205
        $data = $this->getData();
206
        uasort($data, $alg);
207
208
        return collect($data);
209
    }
210
211
    /**
212
     * @inheritDoc
213
     */
214
    public function sortkeys($alg = null)
215
    {
216
        if (is_null($alg)) {
217
            $alg = 'strnatcasecmp';
218
        }
219
        $data = $this->getData();
220
        uksort($data, $alg);
221
222
        return collect($data);
223
    }
224
225
    /**
226
     * Does this collection have a value at given index?
227
     *
228
     * @param mixed $index The index to check
229
     *
230
     * @return bool
231
     */
232
    public function has($index)
233
    {
234
        return array_key_exists($index, $this->getData());
235
    }
236
237
    /**
238
     * Set value at given index.
239
     *
240
     * This method simulates setting a value in this collection, but because collections are immutable, it actually
241
     * returns a copy of this collection with the value in the new collection set to specified value.
242
     *
243
     * @param mixed $index The index to set a value at
244
     * @param mixed $val   The value to set $index to
245
     *
246
     * @return CollectionInterface
247
     */
248
    public function set($index, $val)
249
    {
250
        $copy = $this->getData();
251
        $copy[$index] = $val;
252
        return collect($copy);
253
    }
254
255
    /**
256
     * Unset (delete) value at the given index.
257
     *
258
     * Get copy of collection with given index removed.
259
     *
260
     * @param mixed $index The index to unset
261
     *
262
     * @return CollectionInterface
263
     */
264
    public function delete($index)
265
    {
266
        return $this->except([$index]);
267
    }
268
269
    /**
270
     * Get index of a value.
271
     *
272
     * Given a value, this method will return the index of the first occurrence of that value.
273
     *
274
     * @param mixed $value Value to get the index of
275
     *
276
     * @return int|null|string
277
     */
278
    public function indexOf($value)
279
    {
280
        return $this->foldRight(function($carry, $val, $key, $iter) use ($value) {
0 ignored issues
show
Unused Code introduced by
The parameter $iter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
281
            if (is_null($carry) && $val == $value) {
282
                return $key;
283
            }
284
            return $carry;
285
        });
286
    }
287
288
    /**
289
     * Get this collection's keys as a collection.
290
     *
291
     * @return CollectionInterface Containing this collection's keys
292
     */
293
    public function keys()
294
    {
295
        return collect(array_keys($this->getData()));
296
    }
297
298
    /**
299
     * Get this collection's values as a collection.
300
     *
301
     * This method returns this collection's values but completely re-indexed (numerically).
302
     *
303
     * @return CollectionInterface Containing this collection's values
304
     */
305
    public function values()
306
    {
307
        return collect(array_values($this->getData()));
308
    }
309
310
    /**
311
     * Determine if this collection contains a value.
312
     *
313
     * Allows you to pass in a value or a callback function and optionally an index,
314
     * and tells you whether or not this collection contains that value.
315
     * If the $index param is specified, only that index will be looked under.
316
     *
317
     * @param mixed|callable $value The value to check for
318
     * @param mixed          $index The (optional) index to look under
319
     *
320
     * @return bool True if this collection contains $value
321
     *
322
     * @todo Maybe add $identical param for identical comparison (===)
323
     * @todo Allow negative offset for second param
324
     */
325
    public function contains($value, $index = null)
326
    {
327
        return (bool) $this->first(function ($val, $key) use ($value, $index) {
328
            if (is_callable($value)) {
329
                $found = $value($val, $key);
330
            } else {
331
                $found = ($value == $val);
332
            }
333
            if ($found) {
334
                if (is_null($index)) {
335
                    return true;
336
                }
337
                if (is_array($index)) {
338
                    return in_array($key, $index);
339
                }
340
341
                return $key == $index;
342
            }
343
344
            return false;
345
        });
346
    }
347
348
    /**
349
     * Pad collection to a certain size.
350
     *
351
     * Returns a new collection, padded to the given size, with the given value.
352
     *
353
     * @param int   $size The number of items that should be in the collection
354
     * @param mixed $with The value to pad the collection with
355
     *
356
     * @return CollectionInterface A new collection padded to specified length
357
     */
358
    public function pad($size, $with = null)
359
    {
360
        return collect(array_pad($this->getData(), $size, $with));
361
    }
362
363
    /**
364
     * Apply a callback to each item in collection.
365
     *
366
     * Applies a callback to each item in collection and returns a new collection
367
     * containing each iteration's return value.
368
     *
369
     * @param callable $callback The callback to apply
370
     *
371
     * @return CollectionInterface A new collection with callback return values
372
     */
373
    public function map(callable $callback)
374
    {
375
        $iter = 0;
376
        $transform = [];
377
        foreach ($this as $key => $val) {
378
            $transform[$key] = $callback($val, $key, $iter++);
379
        }
380
        return collect($transform);
381
    }
382
383
    /**
384
     * Iterate over each item in the collection, calling $callback on it. Return false to stop iterating.
385
     *
386
     * @param callable    $callback A callback to use
387
     *
388
     * @return $this
389
     */
390
    public function each(callable $callback)
391
    {
392
        foreach ($this as $key => $val) {
393
            if (!$callback($val, $key)) {
394
                break;
395
            }
396
        }
397
398
        return $this;
399
    }
400
401
    /**
402
     * Filter the collection.
403
     *
404
     * Using a callback function, this method will filter out unwanted values, returning
405
     * a new collection containing only the values that weren't filtered.
406
     *
407
     * @param callable $callback The callback function used to filter
408
     *
409
     * @return CollectionInterface A new collection with only values that weren't filtered
410
     */
411
    public function filter(callable $callback)
412
    {
413
        $iter = 0;
414
        $filtered = [];
415
        foreach ($this as $key => $val) {
416
            if ($callback($val, $key, $iter++)) {
417
                $filtered[$key] = $val;
418
            }
419
        }
420
        return collect($filtered);
421
    }
422
423
    /**
424
     * Filter the collection.
425
     *
426
     * Using a callback function, this method will filter out unwanted values, returning
427
     * a new collection containing only the values that weren't filtered.
428
     *
429
     * @param callable $callback The callback function used to filter
430
     *
431
     * @return CollectionInterface A new collection with only values that weren't filtered
432
     */
433
    public function exclude(callable $callback)
434
    {
435
        $iter = 0;
436
        $filtered = [];
437
        foreach ($this as $key => $val) {
438
            if (!$callback($val, $key, $iter++)) {
439
                $filtered[$key] = $val;
440
            }
441
        }
442
        return collect($filtered);
443
    }
444
445
    /**
446
     * Return the first item that meets given criteria.
447
     *
448
     * Using a callback function, this method will return the first item in the collection
449
     * that causes the callback function to return true.
450
     *
451
     * @param callable|null $callback The callback function
452
     * @param mixed|null    $default  The default return value
453
     *
454
     * @return mixed
455
     */
456
    public function first(callable $callback = null, $default = null)
457
    {
458
        if (is_null($callback)) {
459
            return $this->getOffset(0);
460
        }
461
462
        foreach ($this as $index => $value) {
463
            if ($callback($value, $index)) {
464
                return $value;
465
            }
466
        }
467
468
        return $default;
469
    }
470
471
    /**
472
     * Return the last item that meets given criteria.
473
     *
474
     * Using a callback function, this method will return the last item in the collection
475
     * that causes the callback function to return true.
476
     *
477
     * @param callable|null $callback The callback function
478
     * @param mixed|null    $default  The default return value
479
     *
480
     * @return mixed
481
     */
482
    public function last(callable $callback = null, $default = null)
483
    {
484
        $reverse = $this->reverse();
485
        if (is_null($callback)) {
486
            return $reverse->getOffset(0);
487
        }
488
        return $reverse->first($callback);
489
    }
490
491
    /**
492
     * Returns collection in reverse order.
493
     *
494
     * @return CollectionInterface This collection in reverse order.
495
     */
496
    public function reverse()
497
    {
498
        return collect(array_reverse($this->getData(), true));
499
    }
500
501
    /**
502
     * Get unique items.
503
     *
504
     * Returns a collection of all the unique items in this collection.
505
     *
506
     * @return CollectionInterface This collection with duplicate items removed
507
     */
508
    public function unique()
509
    {
510
        return collect(array_unique($this->getData()));
511
    }
512
513
    /**
514
     * Collection factory method.
515
     *
516
     * This method will analyze input data and determine the most appropriate Collection
517
     * class to use. It will then instantiate said Collection class with the given
518
     * data and return it.
519
     *
520
     * @param mixed $data The data to wrap
521
     *
522
     * @return CollectionInterface A collection containing $data
523
     */
524
    public static function factory($data = null)
525
    {
526
        return new Collection($data);
527
    }
528
529
    /**
530
     * Determine if structure contains all numeric values.
531
     *
532
     * @return bool
533
     */
534
    public function isNumeric()
535
    {
536
        $data = $this->getData();
537
        if (!is_traversable($data) || empty($data)) {
538
            return false;
539
        }
540
        foreach ($data as $val) {
541
            if (!is_numeric($val)) {
542
                return false;
543
            }
544
        }
545
        return true;
546
    }
547
548
    /**
549
     * @inheritdoc
550
     */
551
    public function hasOffset($offset)
552
    {
553
        try {
554
            $this->getOffsetKey($offset);
555
            return true;
556
        } catch (OutOfBoundsException $e) {
557
            return false;
558
        }
559
    }
560
561
    /**
562
     * @inheritdoc
563
     */
564
    public function getOffsetKey($offset)
565
    {
566
        if (!is_null($key = $this->foldRight(function($carry, $val, $key, $iter) use ($offset) {
567
            return ($iter === $offset) ? $key : $carry;
568
        }))) {
569
            return $key;
570
        }
571
        throw new OutOfBoundsException("Offset does not exist: $offset");
572
    }
573
574
    /**
575
     * @inheritdoc
576
     */
577
    public function getOffset($offset)
578
    {
579
        return $this->retrieve($this->getOffsetKey($offset));
580
    }
581
582
    /**
583
     * @param int $offset The numerical offset
584
     *
585
     * @throws OutOfBoundsException if no pair at position
586
     *
587
     * @return array
588
     */
589
    public function getOffsetPair($offset)
590
    {
591
        $pairs = $this->pairs();
592
593
        return $pairs[$this->getOffsetKey($offset)];
594
    }
595
596
    /**
597
     * Get each key/value as an array pair.
598
     *
599
     * Returns a collection of arrays where each item in the collection is [key,value]
600
     *
601
     * @return CollectionInterface
602
     */
603
    public function pairs()
604
    {
605
        return collect(array_map(
606
            function ($key, $val) {
607
                return [$key, $val];
608
            },
609
            array_keys($this->getData()),
610
            array_values($this->getData())
611
        ));
612
    }
613
614
    /**
615
     * Get duplicate values.
616
     *
617
     * Returns a collection of arrays where the key is the duplicate value
618
     * and the value is an array of keys from the original collection.
619
     *
620
     * @return CollectionInterface A new collection with duplicate values.
621
     */
622
    public function duplicates()
623
    {
624
        $dups = [];
625
        $this->walk(function ($val, $key) use (&$dups) {
626
            $dups[$val][] = $key;
627
        });
628
629
        return collect($dups)->filter(function ($val) {
630
            return count($val) > 1;
631
        });
632
    }
633
634
    // END Iterator methods
635
636
    /**
637
     * Counts how many times each value occurs in a collection.
638
     *
639
     * Returns a new collection with values as keys and how many times that
640
     * value appears in the collection. Works best with scalar values but will
641
     * attempt to work on collections of objects as well.
642
     *
643
     * @return CollectionInterface
644
     *
645
     * @todo Right now, collections of arrays or objects are supported via the
646
     * __toString() or spl_object_hash()
647
     * @todo NumericCollection::counts() does the same thing...
648
     */
649
    public function frequency()
650
    {
651
        $frequency = [];
652
        foreach ($this as $key => $val) {
653
            if (!is_scalar($val)) {
654
                if (!is_object($val)) {
655
                    $val = new ArrayIterator($val);
656
                }
657
658
                if (method_exists($val, '__toString')) {
659
                    $val = (string) $val;
660
                } else {
661
                    $val = spl_object_hash($val);
662
                }
663
            }
664
            if (!isset($frequency[$val])) {
665
                $frequency[$val] = 0;
666
            }
667
            $frequency[$val]++;
668
        }
669
670
        return collect($frequency);
671
    }
672
673
    /**
674
     * @inheritDoc
675
     */
676
    public function add($index, $value)
677
    {
678
        if (!$this->has($index)) {
679
            return $this->set($index, $value);
680
        }
681
        return collect($this);
682
    }
683
684
    /**
685
     * @inheritdoc
686
     * @todo Maybe read would be a better name for this?
687
     */
688
    public function get($index, $default = null)
689
    {
690
        try {
691
            return $this->retrieve($index);
692
        } catch (OutOfBoundsException $e) {
693
            return $default;
694
        }
695
    }
696
697
    /**
698
     * @inheritdoc
699
     * @todo Maybe read would be a better name for this?
700
     */
701
    public function retrieve($index)
702
    {
703
        if (!$this->has($index)) {
704
            throw new OutOfBoundsException(__CLASS__ . ' could not retrieve value at index ' . $index);
705
        }
706
        return $this->getData()[$index];
707
    }
708
709
    /**
710
     * @inheritDoc
711
     */
712
    public function prepend($item)
713
    {
714
        $data = $this->getData();
715
        array_unshift($data, $item);
716
717
        return collect($data);
718
    }
719
720
    /**
721
     * @inheritDoc
722
     */
723
    public function append($item)
724
    {
725
        $data = $this->getData();
726
        array_push($data, $item);
727
728
        return collect($data);
729
    }
730
731
    /**
732
     * @inheritDoc
733
     */
734
    public function chunk($size)
735
    {
736
        $numchunks = (int) ($this->count() / $size);
737
        return collect($this->foldRight(function($chunks, $val, $key, $iter) use ($size, $numchunks) {
738
            if (is_null($chunks)) {
739
                $chunks = [];
740
            }
741
            if ($iter % $size == 0) {
742
                // start new chunk
743
                array_push($chunks, []);
744
            }
745
            $chunk = array_pop($chunks);
746
            array_push($chunk, $val);
747
            array_push($chunks, $chunk);
748
749
            return $chunks;
750
        }));
751
    }
752
753
    public function combine($values)
754
    {
755
        if (!is_traversable($values)) {
756
            throw new InvalidArgumentException(sprintf(
757
                'Expecting traversable data for %s but got %s.',
758
                __METHOD__,
759
                typeof($values)
760
            ));
761
        }
762
        return collect(
763
            array_combine(
764
                $this->keys()->toArray(),
765
                collect($values)->values()->toArray()
0 ignored issues
show
Bug introduced by
It seems like $values defined by parameter $values on line 753 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
766
            )
767
        );
768
    }
769
770
    /**
771
     * @inheritDoc
772
     */
773
    public function diff($data)
774
    {
775
        return collect(
776
            array_diff(
777
                $this->toArray(),
778
                collect($data)->toArray()
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 773 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
779
            )
780
        );
781
    }
782
783
    /**
784
     * @inheritDoc
785
     */
786
    public function diffKeys($data)
787
    {
788
        return collect(
789
            array_diff_key(
790
                $this->getData(),
791
                collect($data)->toArray()
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 786 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
792
            )
793
        );
794
    }
795
796
    /**
797
     * @inheritDoc
798
     */
799
    public function every($nth, $offset = null)
800
    {
801
        return $this->slice($offset)->filter(function($val, $key, $iter) use ($nth) {
802
            return $iter % $nth == 0;
803
        });
804
    }
805
806
    /**
807
     * @inheritDoc
808
     */
809
    public function except($indexes)
810
    {
811
        return $this->diffKeys(collect($indexes)->flip());
0 ignored issues
show
Bug introduced by
It seems like $indexes defined by parameter $indexes on line 809 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
812
    }
813
814
    /**
815
     * @inheritDoc
816
     */
817
    public function flip()
818
    {
819
        return collect(array_flip($this->getData()));
820
    }
821
822
    /**
823
     * @inheritDoc
824
     */
825
    public function intersect($data)
826
    {
827
        return collect(
828
            array_intersect(
829
                $this->toArray(),
830
                collect($data)->toArray()
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 825 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
831
            )
832
        );
833
    }
834
835
    /**
836
     * @inheritDoc
837
     */
838
    public function intersectKeys($data)
839
    {
840
        return collect(
841
            array_intersect_key(
842
                $this->toArray(),
843
                collect($data)->toArray()
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 838 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
844
            )
845
        );
846
    }
847
848
    /**
849
     * @inheritDoc
850
     */
851
    public function isEmpty(callable $callback = null)
852
    {
853
        if (!is_null($callback)) {
854
            return $this->all($callback);
855
        }
856
        return empty($this->getData());
857
    }
858
859
    /**
860
     * @inheritDoc
861
     */
862
    public function only($indices)
863
    {
864
        return $this->intersectKeys(collect($indices)->flip()->toArray());
0 ignored issues
show
Bug introduced by
It seems like $indices defined by parameter $indices on line 862 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
865
    }
866
867
    /**
868
     * @inheritDoc
869
     */
870
    public function pipe(callable $callback)
871
    {
872
        return $callback($this);
873
    }
874
875
    /**
876
     * @inheritDoc
877
     */
878
    public function random($num)
879
    {
880
        return $this->shuffle()->slice(0, $num);
881
    }
882
883
    /**
884
     * @inheritDoc
885
     */
886
    public function indicesOf($value)
887
    {
888
        return $this->filter(function($val) use ($value) {
889
            return $val == $value;
890
        })->map(function($val, $key) {
891
            return $key;
892
        });
893
    }
894
895
    /**
896
     * @inheritDoc
897
     */
898
    public function shuffle()
899
    {
900
        return collect(shuffle($this->getData()));
0 ignored issues
show
Bug introduced by
$this->getData() cannot be passed to shuffle() as the parameter $array expects a reference.
Loading history...
Documentation introduced by
shuffle($this->getData()) is of type boolean, but the function expects a array|object<Iterator>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
901
    }
902
903
    /**
904
     * @inheritDoc
905
     */
906
    public function slice($offset, $length = null)
907
    {
908
        return collect(array_slice($this->getData(), $offset, $length, true));
909
    }
910
911
    /**
912
     * @inheritDoc
913
     */
914
    public function split($num)
915
    {
916
        $data = [];
917
        $group = $iter = 0;
918
        $size = (int) ($this->count() / $num);
919
        foreach ($this as $key => $val) {
920
            $data[$group][$key] = $val;
921
            if ($iter++ > $size) {
922
                $group++;
923
                $iter = 0;
924
            }
925
        }
926
        return collect($data);
927
    }
928
929
    /**
930
     * @inheritDoc
931
     */
932
    public function union($data)
933
    {
934
        return collect(
935
            array_merge(
936
                $this->toArray(),
937
                collect($data)->toArray()
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 932 can also be of type object<Noz\Contracts\CollectionInterface>; however, Noz\collect() does only seem to accept array|object<Iterator>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
938
            )
939
        );
940
    }
941
942
    /**
943
     * @inheritDoc
944
     */
945
    public function zip(...$data)
946
    {
947
        return collect(
948
            array_map(
949
                $this->toArray(),
950
                ...$data
951
            )
952
        );
953
    }
954
955
    /**
956
     * @inheritDoc
957
     */
958
    public function foldRight(callable $callback, $initial = null)
959
    {
960
        $iter = 0;
961
        $carry = $initial;
962
        foreach ($this as $key => $val) {
963
            $carry = $callback($carry, $val, $key, $iter++);
964
        }
965
        return $carry;
966
    }
967
968
    /**
969
     * @inheritDoc
970
     */
971
    public function foldLeft(callable $callback, $initial = null)
972
    {
973
        return $this->reverse()->foldRight($callback, $initial);
974
    }
975
976
    /**
977
     * @inheritDoc
978
     */
979
    public function all(callable $callback = null)
980
    {
981
        if (is_null($callback)) {
982
            $callback = function($val) {
983
                return (bool) $val;
984
            };
985
        }
986
        return $this->filter($callback)->isEmpty();
987
    }
988
989
    /**
990
     * @inheritDoc
991
     */
992
    public function none(callable $callback = null)
993
    {
994
        if (is_null($callback)) {
995
            $callback = function($val) {
996
                return (bool) $val;
997
            };
998
        }
999
        return $this->filter($callback)->isEmpty();
1000
    }
1001
1002
    // BEGIN Numeric Collection Methods
1003
    // These methods only really work on numeric data.
1004
1005
    /**
1006
     * Increment an item.
1007
     *
1008
     * Increment the item specified by $key by one value. Intended for integers
1009
     * but also works (using this term loosely) for letters. Any other data type
1010
     * it may modify is unintended behavior at best.
1011
     *
1012
     * This method modifies its internal data array rather than returning a new
1013
     * collection.
1014
     *
1015
     * @param mixed $index    The key of the item you want to increment.
1016
     * @param int   $interval The interval that $key should be incremented by
1017
     *
1018
     * @return CollectionInterface
1019
     */
1020
    public function increment($index, $interval = 1)
1021
    {
1022
        $val = $this->retrieve($index);
1023
        $val += $interval;
1024
        return $this->set($index, $val);
1025
    }
1026
1027
    /**
1028
     * Decrement an item.
1029
     *
1030
     * Frcrement the item specified by $key by one value. Intended for integers.
1031
     * Does not work for letters and if it does anything to anything else, it's
1032
     * unintended at best.
1033
     *
1034
     * This method modifies its internal data array rather than returning a new
1035
     * collection.
1036
     *
1037
     * @param mixed $index      The key of the item you want to decrement.
1038
     * @param int   $interval The interval that $key should be decremented by
1039
     *
1040
     * @return CollectionInterface
1041
     */
1042
    public function decrement($index, $interval = 1)
1043
    {
1044
        $val = $this->retrieve($index);
1045
        $val -= $interval;
1046
        return $this->set($index, $val);
1047
    }
1048
1049
    /**
1050
     * Get the sum.
1051
     *
1052
     * @return int|float The sum of all values in collection
1053
     */
1054
    public function sum()
1055
    {
1056
        return array_sum($this->toArray());
1057
    }
1058
1059
    /**
1060
     * Get the average.
1061
     *
1062
     * @return float|int The average value from the collection
1063
     */
1064
    public function average()
1065
    {
1066
        return $this->sum() / $this->count();
1067
    }
1068
1069
    /**
1070
     * Get the mode.
1071
     *
1072
     * @return float|int The mode
1073
     */
1074
    public function mode()
1075
    {
1076
        $counts = $this->counts()->toArray();
1077
        arsort($counts);
1078
        $mode = key($counts);
1079
1080
        return (strpos($mode, '.')) ? floatval($mode) : intval($mode);
1081
    }
1082
1083
    /**
1084
     * Get the median value.
1085
     *
1086
     * @return float|int The median value
1087
     */
1088
    public function median()
1089
    {
1090
        $count = $this->count();
1091
        $data  = $this->toArray();
1092
        natcasesort($data);
1093
        $middle = $count / 2;
1094
        $values = array_values($data);
1095
        if ($count % 2 == 0) {
1096
            // even number, use middle
1097
            $low  = $values[$middle - 1];
1098
            $high = $values[$middle];
1099
1100
            return ($low + $high) / 2;
1101
        }
1102
        // odd number return median
1103
        return $values[$middle];
1104
    }
1105
1106
    /**
1107
     * Get the maximum value.
1108
     *
1109
     * @return mixed The maximum
1110
     */
1111
    public function max()
1112
    {
1113
        return max($this->getData());
1114
    }
1115
1116
    /**
1117
     * Get the minimum value.
1118
     *
1119
     * @return mixed The minimum
1120
     */
1121
    public function min()
1122
    {
1123
        return min($this->getData());
1124
    }
1125
1126
    /**
1127
     * Get the number of times each item occurs in the collection.
1128
1129
     * This method will return a NumericCollection where keys are the
1130
     * values and values are the number of times that value occurs in
1131
     * the original collection.
1132
1133
     * @return CollectionInterface
1134
     */
1135
    public function counts()
1136
    {
1137
        return collect(array_count_values($this->toArray()));
1138
    }
1139
}
1140