Completed
Push — master ( be043b...2df975 )
by Lars
01:40
created

Arrayy::replaceKeys()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 1
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
use Arrayy\TypeCheck\TypeCheckArray;
8
use Arrayy\TypeCheck\TypeCheckInterface;
9
use Arrayy\TypeCheck\TypeCheckPhpDoc;
10
11
/** @noinspection ClassReImplementsParentInterfaceInspection */
12
13
/**
14
 * Methods to manage arrays.
15
 *
16
 * For the full copyright and license information, please view the LICENSE
17
 * file that was distributed with this source code.
18
 */
19
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
20
{
21
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
22
23
    /**
24
     * @var array
25
     */
26
    protected $array = [];
27
28
    /**
29
     * @var ArrayyRewindableGenerator|null
30
     */
31
    protected $generator;
32
33
    /**
34
     * @var string
35
     */
36
    protected $iteratorClass = ArrayyIterator::class;
37
38
    /**
39
     * @var string
40
     */
41
    protected $pathSeparator = '.';
42
43
    /**
44
     * @var bool
45
     */
46
    protected $checkPropertyTypes = false;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $checkForMissingPropertiesInConstructor = false;
52
53
    /**
54
     * @var bool
55
     */
56
    protected $checkPropertiesMismatchInConstructor = false;
57
58
    /**
59
     * @var bool
60
     */
61
    protected $checkPropertiesMismatch = true;
62
63
    /**
64
     * @var array|TypeCheckArray|TypeCheckInterface[]
65
     */
66
    protected $properties = [];
67
68
    /**
69
     * Initializes
70
     *
71
     * @param mixed  $data                         <p>
72
     *                                             Should be an array or a generator, otherwise it will try
73
     *                                             to convert it into an array.
74
     *                                             </p>
75
     * @param string $iteratorClass                optional <p>
76
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
77
     *                                             need this option.
78
     *                                             </p>
79
     * @param bool   $checkPropertiesInConstructor optional <p>
80
     *                                             You need to extend the "Arrayy"-class and you need to set
81
     *                                             the $checkPropertiesMismatchInConstructor class property
82
     *                                             to
83
     *                                             true, otherwise this option didn't not work anyway.
84
     *                                             </p>
85
     */
86 1050
    public function __construct(
87
        $data = [],
88
        string $iteratorClass = ArrayyIterator::class,
89
        bool $checkPropertiesInConstructor = true
90
    ) {
91 1050
        $data = $this->fallbackForArray($data);
92
93
        // used only for serialize + unserialize, all other methods are overwritten
94 1048
        parent::__construct([], 0, $iteratorClass);
95
96 1048
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
97
98 1041
        $this->setIteratorClass($iteratorClass);
99 1041
    }
100
101
    /**
102
     * Call object as function.
103
     *
104
     * @param mixed $key
105
     *
106
     * @return mixed
107
     */
108 1
    public function __invoke($key = null)
109
    {
110 1
        if ($key !== null) {
111 1
            $this->generatorToArray();
112
113 1
            return $this->array[$key] ?? false;
114
        }
115
116
        return $this->getArray();
117
    }
118
119
    /**
120
     * Whether or not an element exists by key.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return bool
125
     *              <p>True is the key/index exists, otherwise false.</p>
126
     */
127
    public function __isset($key): bool
128
    {
129
        return $this->offsetExists($key);
130
    }
131
132
    /**
133
     * Assigns a value to the specified element.
134
     *
135
     * @param mixed $key
136
     * @param mixed $value
137
     */
138 2
    public function __set($key, $value)
139
    {
140 2
        $this->internalSet($key, $value);
141 2
    }
142
143
    /**
144
     * magic to string
145
     *
146
     * @return string
147
     */
148 15
    public function __toString(): string
149
    {
150 15
        return $this->toString();
151
    }
152
153
    /**
154
     * Unset element by key.
155
     *
156
     * @param mixed $key
157
     */
158
    public function __unset($key)
159
    {
160
        $this->internalRemove($key);
161
    }
162
163
    /**
164
     * Get a value by key.
165
     *
166
     * @param mixed $key
167
     *
168
     * @return mixed
169
     *               <p>Get a Value from the current array.</p>
170
     */
171 4
    public function &__get($key)
172
    {
173 4
        $return = $this->get($key);
174
175 4
        if (\is_array($return) === true) {
176
            return static::create($return, $this->iteratorClass, false);
177
        }
178
179 4
        return $return;
180
    }
181
182
    /**
183
     * alias: for "Arrayy->append()"
184
     *
185
     * @param mixed $value
186
     *
187
     * @return static
188
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
189
     *
190
     * @see Arrayy::append()
191
     */
192 3
    public function add($value): self
193
    {
194 3
        return $this->append($value);
195
    }
196
197
    /**
198
     * Append a (key) + value to the current array.
199
     *
200
     * @param mixed $value
201
     * @param mixed $key
202
     *
203
     * @return static
204
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
205
     */
206 13
    public function append($value, $key = null): self
207
    {
208 13
        $this->generatorToArray();
209
210 13
        if ($this->properties !== []) {
211 4
            $this->checkType($key, $value);
212
        }
213
214 12
        if ($key !== null) {
215
            if (
216
                isset($this->array[$key])
217
                &&
218
                \is_array($this->array[$key]) === true
219
            ) {
220
                $this->array[$key][] = $value;
221
            } else {
222
                $this->array[$key] = $value;
223
            }
224
        } else {
225 12
            $this->array[] = $value;
226
        }
227
228 12
        return $this;
229
    }
230
231
    /**
232
     * Sort the entries by value.
233
     *
234
     * @param int $sort_flags [optional] <p>
235
     *                        You may modify the behavior of the sort using the optional
236
     *                        parameter sort_flags, for details
237
     *                        see sort.
238
     *                        </p>
239
     *
240
     * @return static
241
     *                <p>(Mutable) Return this Arrayy object.</p>
242
     */
243 4
    public function asort(int $sort_flags = 0): self
244
    {
245 4
        $this->generatorToArray();
246
247 4
        \asort($this->array, $sort_flags);
248
249 4
        return $this;
250
    }
251
252
    /**
253
     * Counts all elements in an array, or something in an object.
254
     *
255
     * <p>
256
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
257
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
258
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
259
     * implemented and used in PHP.
260
     * </p>
261
     *
262
     * @see http://php.net/manual/en/function.count.php
263
     *
264
     * @param int $mode [optional] If the optional mode parameter is set to
265
     *                  COUNT_RECURSIVE (or 1), count
266
     *                  will recursively count the array. This is particularly useful for
267
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
268
     *
269
     * @return int
270
     *             <p>
271
     *             The number of elements in var, which is
272
     *             typically an array, since anything else will have one
273
     *             element.
274
     *             </p>
275
     *             <p>
276
     *             If var is not an array or an object with
277
     *             implemented Countable interface,
278
     *             1 will be returned.
279
     *             There is one exception, if var is &null;,
280
     *             0 will be returned.
281
     *             </p>
282
     *             <p>
283
     *             Caution: count may return 0 for a variable that isn't set,
284
     *             but it may also return 0 for a variable that has been initialized with an
285
     *             empty array. Use isset to test if a variable is set.
286
     *             </p>
287
     */
288 50
    public function count(int $mode = \COUNT_NORMAL): int
289
    {
290 50
        return \count($this->getArray(), $mode);
291
    }
292
293
    /**
294
     * Exchange the array for another one.
295
     *
296
     * @param array|static $data
297
     *
298
     * @return array
299
     */
300 1
    public function exchangeArray($data): array
301
    {
302 1
        $this->array = $this->fallbackForArray($data);
303
304 1
        return $this->array;
305
    }
306
307
    /**
308
     * Creates a copy of the ArrayyObject.
309
     *
310
     * @return array
311
     */
312 5
    public function getArrayCopy(): array
313
    {
314 5
        $this->generatorToArray();
315
316 5
        return $this->array;
317
    }
318
319
    /**
320
     * Returns a new iterator, thus implementing the \Iterator interface.
321
     *
322
     * @return \Iterator
323
     *                   <p>An iterator for the values in the array.</p>
324
     */
325 24
    public function getIterator(): \Iterator
326
    {
327 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
328 1
            return $this->generator;
329
        }
330
331 23
        $iterator = $this->getIteratorClass();
332
333 23
        if ($iterator === ArrayyIterator::class) {
334 23
            return new $iterator($this->getArray(), 0, static::class);
335
        }
336
337
        return new $iterator($this->getArray());
338
    }
339
340
    /**
341
     * Gets the iterator classname for the ArrayObject.
342
     *
343
     * @return string
344
     */
345 23
    public function getIteratorClass(): string
346
    {
347 23
        return $this->iteratorClass;
348
    }
349
350
    /**
351
     * Sort the entries by key
352
     *
353
     * @param int $sort_flags [optional] <p>
354
     *                        You may modify the behavior of the sort using the optional
355
     *                        parameter sort_flags, for details
356
     *                        see sort.
357
     *                        </p>
358
     *
359
     * @return static
360
     *                <p>(Mutable) Return this Arrayy object.</p>
361
     */
362 4
    public function ksort(int $sort_flags = 0): self
363
    {
364 4
        $this->generatorToArray();
365
366 4
        \ksort($this->array, $sort_flags);
367
368 4
        return $this;
369
    }
370
371
    /**
372
     * Sort an array using a case insensitive "natural order" algorithm
373
     *
374
     * @return static
375
     *                <p>(Mutable) Return this Arrayy object.</p>
376
     */
377
    public function natcasesort(): self
378
    {
379
        $this->generatorToArray();
380
381
        \natcasesort($this->array);
382
383
        return $this;
384
    }
385
386
    /**
387
     * Sort entries using a "natural order" algorithm
388
     *
389
     * @return static
390
     *                <p>(Mutable) Return this Arrayy object.</p>
391
     */
392 1
    public function natsort(): self
393
    {
394 1
        $this->generatorToArray();
395
396 1
        \natsort($this->array);
397
398 1
        return $this;
399
    }
400
401
    /**
402
     * Whether or not an offset exists.
403
     *
404
     * @param bool|int|string $offset
405
     *
406
     * @return bool
407
     */
408 126
    public function offsetExists($offset): bool
409
    {
410 126
        $this->generatorToArray();
411
412 126
        if ($this->array === []) {
413 5
            return false;
414
        }
415
416
        // php cast "bool"-index into "int"-index
417 121
        if ((bool) $offset === $offset) {
418 1
            $offset = (int) $offset;
419
        }
420
421 121
        $tmpReturn = $this->keyExists($offset);
422
423
        if (
424 121
            $tmpReturn === true
425
            ||
426
            (
427 88
                $tmpReturn === false
428
                &&
429 121
                \strpos((string) $offset, $this->pathSeparator) === false
430
            )
431
        ) {
432 119
            return $tmpReturn;
433
        }
434
435 3
        $offsetExists = false;
436
437
        if (
438 3
            $this->pathSeparator
439
            &&
440 3
            (string) $offset === $offset
441
            &&
442 3
            \strpos($offset, $this->pathSeparator) !== false
443
        ) {
444 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
445 3
            if ($explodedPath !== false) {
446 3
                $lastOffset = \array_pop($explodedPath);
447 3
                if ($lastOffset !== null) {
448 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
449
450 3
                    $this->callAtPath(
451 3
                        $containerPath,
452
                        static function ($container) use ($lastOffset, &$offsetExists) {
453 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
454 3
                        }
455
                    );
456
                }
457
            }
458
        }
459
460 3
        return $offsetExists;
461
    }
462
463
    /**
464
     * Returns the value at specified offset.
465
     *
466
     * @param int|string $offset
467
     *
468
     * @return mixed
469
     *               <p>Will return null if the offset did not exists.</p>
470
     */
471 97
    public function offsetGet($offset)
472
    {
473 97
        return $this->offsetExists($offset) ? $this->get($offset) : null;
474
    }
475
476
    /**
477
     * Assigns a value to the specified offset.
478
     *
479
     * @param int|string|null $offset
480
     * @param mixed           $value
481
     */
482 21
    public function offsetSet($offset, $value)
483
    {
484 21
        $this->generatorToArray();
485
486 21
        if ($offset === null) {
487 5
            if ($this->properties !== []) {
488 1
                $this->checkType(null, $value);
489
            }
490
491 4
            $this->array[] = $value;
492
        } else {
493 16
            $this->internalSet(
494 16
                $offset,
495 16
                $value,
496 16
                true
497
            );
498
        }
499 20
    }
500
501
    /**
502
     * Unset an offset.
503
     *
504
     * @param int|string $offset
505
     */
506 12
    public function offsetUnset($offset)
507
    {
508 12
        $this->generatorToArray();
509
510 12
        if ($this->array === []) {
511 3
            return;
512
        }
513
514 10
        if ($this->keyExists($offset)) {
515 7
            unset($this->array[$offset]);
516
517 7
            return;
518
        }
519
520
        if (
521 5
            $this->pathSeparator
522
            &&
523 5
            (string) $offset === $offset
524
            &&
525 5
            \strpos($offset, $this->pathSeparator) !== false
526
        ) {
527 2
            $path = \explode($this->pathSeparator, (string) $offset);
528
529 2
            if ($path !== false) {
530 2
                $pathToUnset = \array_pop($path);
531
532 2
                $this->callAtPath(
533 2
                    \implode($this->pathSeparator, $path),
534
                    static function (&$offset) use ($pathToUnset) {
535 2
                        unset($offset[$pathToUnset]);
536 2
                    }
537
                );
538
            }
539
        }
540
541 5
        unset($this->array[$offset]);
542 5
    }
543
544
    /**
545
     * Serialize the current "Arrayy"-object.
546
     *
547
     * @return string
548
     */
549 2
    public function serialize(): string
550
    {
551 2
        $this->generatorToArray();
552
553 2
        if (\PHP_VERSION_ID < 70400) {
554 2
            return parent::serialize();
555
        }
556
557
        return \serialize($this);
558
    }
559
560
    /**
561
     * Sets the iterator classname for the current "Arrayy"-object.
562
     *
563
     * @param string $class
564
     *
565
     * @throws \InvalidArgumentException
566
     */
567 1041
    public function setIteratorClass($class)
568
    {
569 1041
        if (\class_exists($class)) {
570 1041
            $this->iteratorClass = $class;
571
572 1041
            return;
573
        }
574
575
        if (\strpos($class, '\\') === 0) {
576
            $class = '\\' . $class;
577
            if (\class_exists($class)) {
578
                $this->iteratorClass = $class;
579
580
                return;
581
            }
582
        }
583
584
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
585
    }
586
587
    /**
588
     * Sort the entries with a user-defined comparison function and maintain key association.
589
     *
590
     * @param callable $function
591
     *
592
     * @throws \InvalidArgumentException
593
     *
594
     * @return static
595
     *                <p>(Mutable) Return this Arrayy object.</p>
596
     */
597 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
598
    {
599
        if (!\is_callable($function)) {
600
            throw new \InvalidArgumentException('Passed function must be callable');
601
        }
602
603
        $this->generatorToArray();
604
605
        \uasort($this->array, $function);
606
607
        return $this;
608
    }
609
610
    /**
611
     * Sort the entries by keys using a user-defined comparison function.
612
     *
613
     * @param callable $function
614
     *
615
     * @throws \InvalidArgumentException
616
     *
617
     * @return static
618
     *                <p>(Mutable) Return this Arrayy object.</p>
619
     */
620 5
    public function uksort($function): self
621
    {
622 5
        return $this->customSortKeys($function);
623
    }
624
625
    /**
626
     * Unserialize an string and return the instance of the "Arrayy"-class.
627
     *
628
     * @param string $string
629
     *
630
     * @return static
631
     */
632 2
    public function unserialize($string): self
633
    {
634 2
        if (\PHP_VERSION_ID < 70400) {
635 2
            parent::unserialize($string);
636
637 2
            return $this;
638
        }
639
640
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
641
    }
642
643
    /**
644
     * Append a (key) + values to the current array.
645
     *
646
     * @param array $values
647
     * @param mixed $key
648
     *
649
     * @return static
650
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
651
     */
652 1
    public function appendArrayValues(array $values, $key = null): self
653
    {
654 1
        $this->generatorToArray();
655
656 1
        if ($key !== null) {
657
            if (
658 1
                isset($this->array[$key])
659
                &&
660 1
                \is_array($this->array[$key]) === true
661
            ) {
662 1
                foreach ($values as $value) {
663 1
                    $this->array[$key][] = $value;
664
                }
665
            } else {
666
                foreach ($values as $value) {
667 1
                    $this->array[$key] = $value;
668
                }
669
            }
670
        } else {
671
            foreach ($values as $value) {
672
                $this->array[] = $value;
673
            }
674
        }
675
676 1
        return $this;
677
    }
678
679
    /**
680
     * Add a suffix to each key.
681
     *
682
     * @param mixed $prefix
683
     *
684
     * @return static
685
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
686
     */
687 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
688
    {
689
        // init
690 10
        $result = [];
691
692 10
        foreach ($this->getGenerator() as $key => $item) {
693 9
            if ($item instanceof self) {
694
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
695 9
            } elseif (\is_array($item) === true) {
696
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
697
                    ->appendToEachKey($prefix)
698
                    ->toArray();
699
            } else {
700 9
                $result[$prefix . $key] = $item;
701
            }
702
        }
703
704 10
        return self::create($result, $this->iteratorClass, false);
705
    }
706
707
    /**
708
     * Add a prefix to each value.
709
     *
710
     * @param mixed $prefix
711
     *
712
     * @return static
713
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
714
     */
715 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
    {
717
        // init
718 10
        $result = [];
719
720 10
        foreach ($this->getGenerator() as $key => $item) {
721 9
            if ($item instanceof self) {
722
                $result[$key] = $item->appendToEachValue($prefix);
723 9
            } elseif (\is_array($item) === true) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item) === true) {
726 1
                $result[$key] = $item;
727
            } else {
728 9
                $result[$key] = $prefix . $item;
729
            }
730
        }
731
732 10
        return self::create($result, $this->iteratorClass, false);
733
    }
734
735
    /**
736
     * Sort an array in reverse order and maintain index association.
737
     *
738
     * @return static
739
     *                <p>(Mutable) Return this Arrayy object.</p>
740
     */
741 10
    public function arsort(): self
742
    {
743 10
        $this->generatorToArray();
744
745 10
        \arsort($this->array);
746
747 10
        return $this;
748
    }
749
750
    /**
751
     * Iterate over the current array and execute a callback for each loop.
752
     *
753
     * @param \Closure $closure
754
     *
755
     * @return static
756
     *                <p>(Immutable)</p>
757
     */
758 2
    public function at(\Closure $closure): self
759
    {
760 2
        $arrayy = clone $this;
761
762 2
        foreach ($arrayy->getGenerator() as $key => $value) {
763 2
            $closure($value, $key);
764
        }
765
766 2
        return static::create(
767 2
            $arrayy->toArray(),
768 2
            $this->iteratorClass,
769 2
            false
770
        );
771
    }
772
773
    /**
774
     * Returns the average value of the current array.
775
     *
776
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
777
     *
778
     * @return float|int
779
     *                   <p>The average value.</p>
780
     */
781 10
    public function average($decimals = 0)
782
    {
783 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
784
785 10
        if (!$count) {
786 2
            return 0;
787
        }
788
789 8
        if ((int) $decimals !== $decimals) {
790 3
            $decimals = 0;
791
        }
792
793 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
794
    }
795
796
    /**
797
     * Changes all keys in an array.
798
     *
799
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
800
     *                  or <strong>CASE_LOWER</strong> (default)</p>
801
     *
802
     * @return static
803
     *                <p>(Immutable)</p>
804
     */
805 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
806
    {
807
        if (
808 1
            $case !== \CASE_LOWER
809
            &&
810 1
            $case !== \CASE_UPPER
811
        ) {
812
            $case = \CASE_LOWER;
813
        }
814
815 1
        $return = [];
816 1
        foreach ($this->getGenerator() as $key => $value) {
817 1
            if ($case === \CASE_LOWER) {
818 1
                $key = \mb_strtolower((string) $key);
819
            } else {
820 1
                $key = \mb_strtoupper((string) $key);
821
            }
822
823 1
            $return[$key] = $value;
824
        }
825
826 1
        return static::create(
827 1
            $return,
828 1
            $this->iteratorClass,
829 1
            false
830
        );
831
    }
832
833
    /**
834
     * Change the path separator of the array wrapper.
835
     *
836
     * By default, the separator is: "."
837
     *
838
     * @param string $separator <p>Separator to set.</p>
839
     *
840
     * @return static
841
     *                <p>Mutable</p>
842
     */
843 10
    public function changeSeparator($separator): self
844
    {
845 10
        $this->pathSeparator = $separator;
846
847 10
        return $this;
848
    }
849
850
    /**
851
     * Create a chunked version of the current array.
852
     *
853
     * @param int  $size         <p>Size of each chunk.</p>
854
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
855
     *
856
     * @return static
857
     *                <p>(Immutable) A new array of chunks from the original array.</p>
858
     */
859 5
    public function chunk($size, $preserveKeys = false): self
860
    {
861 5
        return static::create(
862 5
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 5
            $this->iteratorClass,
864 5
            false
865
        );
866
    }
867
868
    /**
869
     * Clean all falsy values from the current array.
870
     *
871
     * @return static
872
     *                <p>(Immutable)</p>
873
     */
874 8
    public function clean(): self
875
    {
876 8
        return $this->filter(
877
            static function ($value) {
878 7
                return (bool) $value;
879 8
            }
880
        );
881
    }
882
883
    /**
884
     * WARNING!!! -> Clear the current array.
885
     *
886
     * @return static
887
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
888
     */
889 4
    public function clear(): self
890
    {
891 4
        $this->array = [];
892 4
        $this->generator = null;
893
894 4
        return $this;
895
    }
896
897
    /**
898
     * Check if an item is in the current array.
899
     *
900
     * @param float|int|string $value
901
     * @param bool             $recursive
902
     * @param bool             $strict
903
     *
904
     * @return bool
905
     */
906 24
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 24
        if ($recursive === true) {
909 19
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 14
        foreach ($this->getGenerator() as $valueFromArray) {
913 11
            if ($strict) {
914 11
                if ($value === $valueFromArray) {
915 11
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920 7
                    return true;
921
                }
922
            }
923
        }
924
925 7
        return false;
926
    }
927
928
    /**
929
     * Check if an (case-insensitive) string is in the current array.
930
     *
931
     * @param string $value
932
     * @param bool   $recursive
933
     *
934
     * @return bool
935
     */
936 26
    public function containsCaseInsensitive($value, $recursive = false): bool
937
    {
938 26
        if ($recursive === true) {
939 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
940 22
                if (\is_array($valueTmp) === true) {
941 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
942 5
                    if ($return === true) {
943 5
                        return $return;
944
                    }
945 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
946 22
                    return true;
947
                }
948
            }
949
950 10
            return false;
951
        }
952
953 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
954 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
955 11
                return true;
956
            }
957
        }
958
959 5
        return false;
960
    }
961
962
    /**
963
     * Check if the given key/index exists in the array.
964
     *
965
     * @param int|string $key <p>key/index to search for</p>
966
     *
967
     * @return bool
968
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
969
     */
970 4
    public function containsKey($key): bool
971
    {
972 4
        return $this->offsetExists($key);
973
    }
974
975
    /**
976
     * Check if all given needles are present in the array as key/index.
977
     *
978
     * @param array $needles   <p>The keys you are searching for.</p>
979
     * @param bool  $recursive
980
     *
981
     * @return bool
982
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
983
     */
984 2
    public function containsKeys(array $needles, $recursive = false): bool
985
    {
986 2
        if ($recursive === true) {
987
            return
988 2
                \count(
989 2
                    \array_intersect(
990 2
                        $needles,
991 2
                        $this->keys(true)->getArray()
992
                    ),
993 2
                    \COUNT_RECURSIVE
994
                )
995
                ===
996 2
                \count(
997 2
                    $needles,
998 2
                    \COUNT_RECURSIVE
999
                );
1000
        }
1001
1002 1
        return \count(
1003 1
            \array_intersect($needles, $this->keys()->getArray()),
1004 1
            \COUNT_NORMAL
1005
        )
1006
               ===
1007 1
               \count(
1008 1
                   $needles,
1009 1
                   \COUNT_NORMAL
1010
               );
1011
    }
1012
1013
    /**
1014
     * Check if all given needles are present in the array as key/index.
1015
     *
1016
     * @param array $needles <p>The keys you are searching for.</p>
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 1
    public function containsKeysRecursive(array $needles): bool
1022
    {
1023 1
        return $this->containsKeys($needles, true);
1024
    }
1025
1026
    /**
1027
     * alias: for "Arrayy->contains()"
1028
     *
1029
     * @param float|int|string $value
1030
     *
1031
     * @return bool
1032
     *
1033
     * @see Arrayy::contains()
1034
     */
1035 9
    public function containsValue($value): bool
1036
    {
1037 9
        return $this->contains($value);
1038
    }
1039
1040
    /**
1041
     * alias: for "Arrayy->contains($value, true)"
1042
     *
1043
     * @param float|int|string $value
1044
     *
1045
     * @return bool
1046
     *
1047
     * @see Arrayy::contains()
1048
     */
1049 18
    public function containsValueRecursive($value): bool
1050
    {
1051 18
        return $this->contains($value, true);
1052
    }
1053
1054
    /**
1055
     * Check if all given needles are present in the array.
1056
     *
1057
     * @param array $needles
1058
     *
1059
     * @return bool
1060
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1061
     */
1062 1
    public function containsValues(array $needles): bool
1063
    {
1064 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1065
               ===
1066 1
               \count($needles, \COUNT_NORMAL);
1067
    }
1068
1069
    /**
1070
     * Counts all the values of an array
1071
     *
1072
     * @see http://php.net/manual/en/function.array-count-values.php
1073
     *
1074
     * @return static
1075
     *                <p>
1076
     *                (Immutable)
1077
     *                An associative Arrayy-object of values from input as
1078
     *                keys and their count as value.
1079
     *                </p>
1080
     */
1081 7
    public function countValues(): self
1082
    {
1083 7
        return self::create(\array_count_values($this->getArray()), $this->iteratorClass);
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $data
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 657
    public static function create(
1097
        $data = [],
1098
        string $iteratorClass = ArrayyIterator::class,
1099
        bool $checkPropertiesInConstructor = true
1100
    ): self {
1101 657
        return new static(
1102 657
            $data,
1103 657
            $iteratorClass,
1104 657
            $checkPropertiesInConstructor
1105
        );
1106
    }
1107
1108
    /**
1109
     * WARNING: Creates an Arrayy object by reference.
1110
     *
1111
     * @param array $array
1112
     *
1113
     * @return static
1114
     *                <p>(Mutable) Return this Arrayy object.</p>
1115
     */
1116 1
    public function createByReference(array &$array = []): self
1117
    {
1118 1
        $array = $this->fallbackForArray($array);
1119
1120 1
        $this->array = &$array;
1121
1122 1
        return $this;
1123
    }
1124
1125
    /**
1126
     * Create an new instance from a callable function which will return an Generator.
1127
     *
1128
     * @param callable():Generator $generatorFunction
0 ignored issues
show
Documentation introduced by
The doc-type callable():Generator could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1129
     *
1130
     * @return static
1131
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1132
     */
1133 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1134
    {
1135 5
        return self::create($generatorFunction);
1136
    }
1137
1138
    /**
1139
     * Create an new instance filled with a copy of values from a "Generator"-object.
1140
     *
1141
     * @param \Generator $generator
1142
     *
1143
     * @return static
1144
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1145
     */
1146 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1147
    {
1148 4
        return self::create(\iterator_to_array($generator, true));
1149
    }
1150
1151
    /**
1152
     * Create an new Arrayy object via JSON.
1153
     *
1154
     * @param string $json
1155
     *
1156
     * @return static
1157
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1158
     */
1159 5
    public static function createFromJson(string $json): self
1160
    {
1161 5
        return static::create(\json_decode($json, true));
1162
    }
1163
1164
    /**
1165
     * Create an new instance filled with values from an object that is iterable.
1166
     *
1167
     * @param \Traversable $object <p>iterable object</p>
1168
     *
1169
     * @return static
1170
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1171
     */
1172 4
    public static function createFromObject(\Traversable $object): self
1173
    {
1174
        // init
1175 4
        $array = self::create();
1176
1177 4
        if ($object instanceof self) {
1178 4
            $objectArray = $object->getGenerator();
1179
        } else {
1180
            $objectArray = $object;
1181
        }
1182
1183 4
        foreach ($objectArray as $key => $value) {
1184 3
            $array[$key] = $value;
1185
        }
1186
1187 4
        return $array;
1188
    }
1189
1190
    /**
1191
     * Create an new instance filled with values from an object.
1192
     *
1193
     * @param object $object
1194
     *
1195
     * @return static
1196
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1197
     */
1198 5
    public static function createFromObjectVars($object): self
1199
    {
1200 5
        return self::create(self::objectToArray($object));
1201
    }
1202
1203
    /**
1204
     * Create an new Arrayy object via string.
1205
     *
1206
     * @param string      $str       <p>The input string.</p>
1207
     * @param string|null $delimiter <p>The boundary string.</p>
1208
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1209
     *                               used.</p>
1210
     *
1211
     * @return static
1212
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1213
     */
1214 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1215
    {
1216 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1217 1
            \preg_match_all($regEx, $str, $array);
1218
1219 1
            if (!empty($array)) {
1220 1
                $array = $array[0];
1221
            }
1222
        } else {
1223
            /** @noinspection NestedPositiveIfStatementsInspection */
1224 9
            if ($delimiter !== null) {
1225 7
                $array = \explode($delimiter, $str);
1226
            } else {
1227 2
                $array = [$str];
1228
            }
1229
        }
1230
1231
        // trim all string in the array
1232 10
        \array_walk(
1233 10
            $array,
1234
            static function (&$val) {
1235 10
                if ((string) $val === $val) {
1236 10
                    $val = \trim($val);
1237
                }
1238 10
            }
1239
        );
1240
1241 10
        return static::create($array);
1242
    }
1243
1244
    /**
1245
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1246
     *
1247
     * @param \Traversable $traversable
1248
     *
1249
     * @return static
1250
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1251
     */
1252 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1253
    {
1254 1
        return self::create(\iterator_to_array($traversable, true));
1255
    }
1256
1257
    /**
1258
     * Create an new instance containing a range of elements.
1259
     *
1260
     * @param mixed $low  <p>First value of the sequence.</p>
1261
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1262
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1263
     *
1264
     * @return static
1265
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1266
     */
1267 2
    public static function createWithRange($low, $high, int $step = 1): self
1268
    {
1269 2
        return static::create(\range($low, $high, $step));
1270
    }
1271
1272
    /**
1273
     * Custom sort by index via "uksort".
1274
     *
1275
     * @see http://php.net/manual/en/function.uksort.php
1276
     *
1277
     * @param callable $function
1278
     *
1279
     * @throws \InvalidArgumentException
1280
     *
1281
     * @return static
1282
     *                <p>(Mutable) Return this Arrayy object.</p>
1283
     */
1284 5 View Code Duplication
    public function customSortKeys($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1285
    {
1286 5
        if (\is_callable($function) === false) {
1287
            throw new \InvalidArgumentException('Passed function must be callable');
1288
        }
1289
1290 5
        $this->generatorToArray();
1291
1292 5
        \uksort($this->array, $function);
1293
1294 5
        return $this;
1295
    }
1296
1297
    /**
1298
     * Custom sort by value via "usort".
1299
     *
1300
     * @see http://php.net/manual/en/function.usort.php
1301
     *
1302
     * @param callable $function
1303
     *
1304
     * @throws \InvalidArgumentException
1305
     *
1306
     * @return static
1307
     *                <p>(Mutable) Return this Arrayy object.</p>
1308
     */
1309 6 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1310
    {
1311 6
        if (\is_callable($function) === false) {
1312
            throw new \InvalidArgumentException('Passed function must be callable');
1313
        }
1314
1315 6
        $this->generatorToArray();
1316
1317 6
        \usort($this->array, $function);
1318
1319 6
        return $this;
1320
    }
1321
1322
    /**
1323
     * Delete the given key or keys.
1324
     *
1325
     * @param int|int[]|string|string[] $keyOrKeys
1326
     */
1327 4
    public function delete($keyOrKeys)
1328
    {
1329 4
        $keyOrKeys = (array) $keyOrKeys;
1330
1331 4
        foreach ($keyOrKeys as $key) {
1332 4
            $this->offsetUnset($key);
1333
        }
1334 4
    }
1335
1336
    /**
1337
     * Return values that are only in the current array.
1338
     *
1339
     * @param array ...$array
1340
     *
1341
     * @return static
1342
     *                <p>(Immutable)</p>
1343
     */
1344 13
    public function diff(...$array): self
1345
    {
1346 13
        return static::create(
1347 13
            \array_diff($this->getArray(), ...$array),
1348 13
            $this->iteratorClass,
1349 13
            false
1350
        );
1351
    }
1352
1353
    /**
1354
     * Return values that are only in the current array.
1355
     *
1356
     * @param array ...$array
1357
     *
1358
     * @return static
1359
     *                <p>(Immutable)</p>
1360
     */
1361 8
    public function diffKey(...$array): self
1362
    {
1363 8
        return static::create(
1364 8
            \array_diff_key($this->getArray(), ...$array),
1365 8
            $this->iteratorClass,
1366 8
            false
1367
        );
1368
    }
1369
1370
    /**
1371
     * Return values and Keys that are only in the current array.
1372
     *
1373
     * @param array $array
1374
     *
1375
     * @return static
1376
     *                <p>(Immutable)</p>
1377
     */
1378 8
    public function diffKeyAndValue(array $array = []): self
1379
    {
1380 8
        return static::create(
1381 8
            \array_diff_assoc($this->getArray(), $array),
1382 8
            $this->iteratorClass,
1383 8
            false
1384
        );
1385
    }
1386
1387
    /**
1388
     * Return values that are only in the current multi-dimensional array.
1389
     *
1390
     * @param array      $array
1391
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1392
     *
1393
     * @return static
1394
     *                <p>(Immutable)</p>
1395
     */
1396 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1397
    {
1398
        // init
1399 1
        $result = [];
1400
1401
        if (
1402 1
            $helperVariableForRecursion !== null
1403
            &&
1404 1
            \is_array($helperVariableForRecursion) === true
1405
        ) {
1406
            $arrayForTheLoop = $helperVariableForRecursion;
1407
        } else {
1408 1
            $arrayForTheLoop = $this->getGenerator();
1409
        }
1410
1411 1
        foreach ($arrayForTheLoop as $key => $value) {
1412 1
            if ($value instanceof self) {
1413
                $value = $value->getArray();
1414
            }
1415
1416 1
            if (\array_key_exists($key, $array)) {
1417 1
                if ($value !== $array[$key]) {
1418 1
                    $result[$key] = $value;
1419
                }
1420
            } else {
1421 1
                $result[$key] = $value;
1422
            }
1423
        }
1424
1425 1
        return static::create(
1426 1
            $result,
1427 1
            $this->iteratorClass,
1428 1
            false
1429
        );
1430
    }
1431
1432
    /**
1433
     * Return values that are only in the new $array.
1434
     *
1435
     * @param array $array
1436
     *
1437
     * @return static
1438
     *                <p>(Immutable)</p>
1439
     */
1440 8
    public function diffReverse(array $array = []): self
1441
    {
1442 8
        return static::create(
1443 8
            \array_diff($array, $this->getArray()),
1444 8
            $this->iteratorClass,
1445 8
            false
1446
        );
1447
    }
1448
1449
    /**
1450
     * Divide an array into two arrays. One with keys and the other with values.
1451
     *
1452
     * @return static
1453
     *                <p>(Immutable)</p>
1454
     */
1455 1
    public function divide(): self
1456
    {
1457 1
        return static::create(
1458
            [
1459 1
                $this->keys(),
1460 1
                $this->values(),
1461
            ],
1462 1
            $this->iteratorClass,
1463 1
            false
1464
        );
1465
    }
1466
1467
    /**
1468
     * Iterate over the current array and modify the array's value.
1469
     *
1470
     * @param \Closure $closure
1471
     *
1472
     * @return static
1473
     *                <p>(Immutable)</p>
1474
     */
1475 5 View Code Duplication
    public function each(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1476
    {
1477
        // init
1478 5
        $array = [];
1479
1480 5
        foreach ($this->getGenerator() as $key => $value) {
1481 5
            $array[$key] = $closure($value, $key);
1482
        }
1483
1484 5
        return static::create(
1485 5
            $array,
1486 5
            $this->iteratorClass,
1487 5
            false
1488
        );
1489
    }
1490
1491
    /**
1492
     * Check if a value is in the current array using a closure.
1493
     *
1494
     * @param \Closure $closure
1495
     *
1496
     * @return bool
1497
     *              <p>Returns true if the given value is found, false otherwise.</p>
1498
     */
1499 4
    public function exists(\Closure $closure): bool
1500
    {
1501
        // init
1502 4
        $isExists = false;
1503
1504 4
        foreach ($this->getGenerator() as $key => $value) {
1505 3
            if ($closure($value, $key)) {
1506 1
                $isExists = true;
1507
1508 3
                break;
1509
            }
1510
        }
1511
1512 4
        return $isExists;
1513
    }
1514
1515
    /**
1516
     * Fill the array until "$num" with "$default" values.
1517
     *
1518
     * @param int   $num
1519
     * @param mixed $default
1520
     *
1521
     * @return static
1522
     *                <p>(Immutable)</p>
1523
     */
1524 8
    public function fillWithDefaults(int $num, $default = null): self
1525
    {
1526 8
        if ($num < 0) {
1527 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1528
        }
1529
1530 7
        $this->generatorToArray();
1531
1532 7
        $tmpArray = $this->array;
1533
1534 7
        $count = \count($tmpArray);
1535
1536 7
        while ($count < $num) {
1537 4
            $tmpArray[] = $default;
1538 4
            ++$count;
1539
        }
1540
1541 7
        return static::create(
1542 7
            $tmpArray,
1543 7
            $this->iteratorClass,
1544 7
            false
1545
        );
1546
    }
1547
1548
    /**
1549
     * Find all items in an array that pass the truth test.
1550
     *
1551
     * @param \Closure|null $closure [optional] <p>
1552
     *                               The callback function to use
1553
     *                               </p>
1554
     *                               <p>
1555
     *                               If no callback is supplied, all entries of
1556
     *                               input equal to false (see
1557
     *                               converting to
1558
     *                               boolean) will be removed.
1559
     *                               </p>
1560
     *                               * @param int $flag [optional] <p>
1561
     *                               Flag determining what arguments are sent to <i>callback</i>:
1562
     *                               </p><ul>
1563
     *                               <li>
1564
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1565
     *                               to <i>callback</i> instead of the value</span>
1566
     *                               </li>
1567
     *                               <li>
1568
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1569
     *                               arguments to <i>callback</i> instead of the value</span>
1570
     *                               </li>
1571
     *                               </ul>
1572
     *
1573
     * @return static
1574
     *                <p>(Immutable)</p>
1575
     */
1576 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1577
    {
1578 12
        if (!$closure) {
1579 1
            return $this->clean();
1580
        }
1581
1582 12
        return static::create(
1583 12
            \array_filter($this->getArray(), $closure, $flag),
1584 12
            $this->iteratorClass,
1585 12
            false
1586
        );
1587
    }
1588
1589
    /**
1590
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1591
     * property within that.
1592
     *
1593
     * @param string          $property
1594
     * @param string|string[] $value
1595
     * @param string          $comparisonOp
1596
     *                                      <p>
1597
     *                                      'eq' (equals),<br />
1598
     *                                      'gt' (greater),<br />
1599
     *                                      'gte' || 'ge' (greater or equals),<br />
1600
     *                                      'lt' (less),<br />
1601
     *                                      'lte' || 'le' (less or equals),<br />
1602
     *                                      'ne' (not equals),<br />
1603
     *                                      'contains',<br />
1604
     *                                      'notContains',<br />
1605
     *                                      'newer' (via strtotime),<br />
1606
     *                                      'older' (via strtotime),<br />
1607
     *                                      </p>
1608
     *
1609
     * @return static
1610
     *                <p>(Immutable)</p>
1611
     */
1612 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1613
    {
1614 1
        if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1615 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1616
        }
1617
1618
        $ops = [
1619
            'eq' => static function ($item, $prop, $value) {
1620 1
                return $item[$prop] === $value;
1621 1
            },
1622
            'gt' => static function ($item, $prop, $value) {
1623
                return $item[$prop] > $value;
1624 1
            },
1625
            'ge' => static function ($item, $prop, $value) {
1626
                return $item[$prop] >= $value;
1627 1
            },
1628
            'gte' => static function ($item, $prop, $value) {
1629
                return $item[$prop] >= $value;
1630 1
            },
1631
            'lt' => static function ($item, $prop, $value) {
1632 1
                return $item[$prop] < $value;
1633 1
            },
1634
            'le' => static function ($item, $prop, $value) {
1635
                return $item[$prop] <= $value;
1636 1
            },
1637
            'lte' => static function ($item, $prop, $value) {
1638
                return $item[$prop] <= $value;
1639 1
            },
1640
            'ne' => static function ($item, $prop, $value) {
1641
                return $item[$prop] !== $value;
1642 1
            },
1643
            'contains' => static function ($item, $prop, $value) {
1644 1
                return \in_array($item[$prop], (array) $value, true);
1645 1
            },
1646
            'notContains' => static function ($item, $prop, $value) {
1647
                return !\in_array($item[$prop], (array) $value, true);
1648 1
            },
1649
            'newer' => static function ($item, $prop, $value) {
1650
                return \strtotime($item[$prop]) > \strtotime($value);
1651 1
            },
1652
            'older' => static function ($item, $prop, $value) {
1653
                return \strtotime($item[$prop]) < \strtotime($value);
1654 1
            },
1655
        ];
1656
1657 1
        $result = \array_values(
1658 1
            \array_filter(
1659 1
                $this->getArray(),
1660
                static function ($item) use (
1661 1
                    $property,
1662 1
                    $value,
1663 1
                    $ops,
1664 1
                    $comparisonOp
1665
                ) {
1666 1
                    $item = (array) $item;
1667 1
                    $itemArrayy = static::create($item);
1668 1
                    $item[$property] = $itemArrayy->get($property, []);
1669
1670 1
                    return $ops[$comparisonOp]($item, $property, $value);
1671 1
                }
1672
            )
1673
        );
1674
1675 1
        return static::create(
1676 1
            $result,
1677 1
            $this->iteratorClass,
1678 1
            false
1679
        );
1680
    }
1681
1682
    /**
1683
     * Find the first item in an array that passes the truth test,
1684
     *  otherwise return false
1685
     *
1686
     * @param \Closure $closure
1687
     *
1688
     * @return false|mixed
1689
     *                     <p>Return false if we did not find the value.</p>
1690
     */
1691 8
    public function find(\Closure $closure)
1692
    {
1693 8
        foreach ($this->getGenerator() as $key => $value) {
1694 6
            if ($closure($value, $key)) {
1695 6
                return $value;
1696
            }
1697
        }
1698
1699 3
        return false;
1700
    }
1701
1702
    /**
1703
     * find by ...
1704
     *
1705
     * @param string          $property
1706
     * @param string|string[] $value
1707
     * @param string          $comparisonOp
1708
     *
1709
     * @return static
1710
     *                <p>(Immutable)</p>
1711
     */
1712 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1713
    {
1714 1
        return $this->filterBy($property, $value, $comparisonOp);
1715
    }
1716
1717
    /**
1718
     * Get the first value from the current array.
1719
     *
1720
     * @return mixed
1721
     *               <p>Return null if there wasn't a element.</p>
1722
     */
1723 22
    public function first()
1724
    {
1725 22
        $key_first = $this->firstKey();
1726 22
        if ($key_first === null) {
1727 3
            return null;
1728
        }
1729
1730 19
        return $this->get($key_first);
1731
    }
1732
1733
    /**
1734
     * Get the first key from the current array.
1735
     *
1736
     * @return mixed
1737
     *               <p>Return null if there wasn't a element.</p>
1738
     */
1739 29
    public function firstKey()
1740
    {
1741 29
        $this->generatorToArray();
1742
1743 29
        return \array_key_first($this->array);
1744
    }
1745
1746
    /**
1747
     * Get the first value(s) from the current array.
1748
     * And will return an empty array if there was no first entry.
1749
     *
1750
     * @param int|null $number <p>How many values you will take?</p>
1751
     *
1752
     * @return static
1753
     *                <p>(Immutable)</p>
1754
     */
1755 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1756
    {
1757 37
        $arrayTmp = $this->getArray();
1758
1759 37
        if ($number === null) {
1760 14
            $array = (array) \array_shift($arrayTmp);
1761
        } else {
1762 23
            $number = (int) $number;
1763 23
            $array = \array_splice($arrayTmp, 0, $number);
1764
        }
1765
1766 37
        return static::create(
1767 37
            $array,
1768 37
            $this->iteratorClass,
1769 37
            false
1770
        );
1771
    }
1772
1773
    /**
1774
     * Get the first value(s) from the current array.
1775
     * And will return an empty array if there was no first entry.
1776
     *
1777
     * @param int|null $number <p>How many values you will take?</p>
1778
     *
1779
     * @return static
1780
     *                <p>(Immutable)</p>
1781
     */
1782 3 View Code Duplication
    public function firstsKeys(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1783
    {
1784 3
        $arrayTmp = $this->keys()->getArray();
1785
1786 3
        if ($number === null) {
1787
            $array = (array) \array_shift($arrayTmp);
1788
        } else {
1789 3
            $number = (int) $number;
1790 3
            $array = \array_splice($arrayTmp, 0, $number);
1791
        }
1792
1793 3
        return static::create(
1794 3
            $array,
1795 3
            $this->iteratorClass,
1796 3
            false
1797
        );
1798
    }
1799
1800
    /**
1801
     * Get and rmove the first value(s) from the current array.
1802
     * And will return an empty array if there was no first entry.
1803
     *
1804
     * @param int|null $number <p>How many values you will take?</p>
1805
     *
1806
     * @return static
1807
     *                <p>(Mutable)</p>
1808
     */
1809 34
    public function firstsMutable(int $number = null): self
1810
    {
1811 34
        $this->generatorToArray();
1812
1813 34
        if ($number === null) {
1814 19
            $this->array = (array) \array_shift($this->array);
1815
        } else {
1816 15
            $number = (int) $number;
1817 15
            $this->array = \array_splice($this->array, 0, $number);
1818
        }
1819
1820 34
        return $this;
1821
    }
1822
1823
    /**
1824
     * Exchanges all keys with their associated values in an array.
1825
     *
1826
     * @return static
1827
     *                <p>(Immutable)</p>
1828
     */
1829 1
    public function flip(): self
1830
    {
1831 1
        return static::create(
1832 1
            \array_flip($this->getArray()),
1833 1
            $this->iteratorClass,
1834 1
            false
1835
        );
1836
    }
1837
1838
    /**
1839
     * Get a value from an array (optional using dot-notation).
1840
     *
1841
     * @param mixed $key      <p>The key to look for.</p>
1842
     * @param mixed $fallback <p>Value to fallback to.</p>
1843
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1844
     *                        class.</p>
1845
     *
1846
     * @return mixed|static
1847
     */
1848 168
    public function get($key, $fallback = null, array $array = null)
1849
    {
1850 168
        if ($array !== null) {
1851 4
            $usedArray = $array;
1852
        } else {
1853 165
            $this->generatorToArray();
1854
1855 165
            $usedArray = $this->array;
1856
        }
1857
1858 168
        if ($key === null) {
1859 1
            return static::create(
1860 1
                $usedArray,
1861 1
                $this->iteratorClass,
1862 1
                false
1863
            );
1864
        }
1865
1866
        // php cast "bool"-index into "int"-index
1867 168
        if ((bool) $key === $key) {
1868 3
            $key = (int) $key;
1869
        }
1870
1871 168
        if (\array_key_exists($key, $usedArray) === true) {
1872 158
            if (\is_array($usedArray[$key]) === true) {
1873 12
                return static::create(
1874 12
                    $usedArray[$key],
1875 12
                    $this->iteratorClass,
1876 12
                    false
1877
                );
1878
            }
1879
1880 150
            return $usedArray[$key];
1881
        }
1882
1883
        // crawl through array, get key according to object or not
1884 24
        $usePath = false;
1885
        if (
1886 24
            $this->pathSeparator
1887
            &&
1888 24
            (string) $key === $key
1889
            &&
1890 24
            \strpos($key, $this->pathSeparator) !== false
1891
        ) {
1892 7
            $segments = \explode($this->pathSeparator, (string) $key);
1893 7
            if ($segments !== false) {
1894 7
                $usePath = true;
1895
1896 7
                foreach ($segments as $segment) {
1897
                    if (
1898
                        (
1899 7
                            \is_array($usedArray) === true
1900
                            ||
1901 7
                            $usedArray instanceof \ArrayAccess
1902
                        )
1903
                        &&
1904 7
                        isset($usedArray[$segment])
1905
                    ) {
1906 7
                        $usedArray = $usedArray[$segment];
1907
1908 7
                        continue;
1909
                    }
1910
1911
                    if (
1912 6
                        \is_object($usedArray) === true
1913
                        &&
1914 6
                        \property_exists($usedArray, $segment)
1915
                    ) {
1916 1
                        $usedArray = $usedArray->{$segment};
1917
1918 1
                        continue;
1919
                    }
1920
1921 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1922
                }
1923
            }
1924
        }
1925
1926 24
        if (!$usePath && !isset($usedArray[$key])) {
1927 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1928
        }
1929
1930 7
        if (\is_array($usedArray) === true) {
1931 1
            return static::create(
1932 1
                $usedArray,
1933 1
                $this->iteratorClass,
1934 1
                false
1935
            );
1936
        }
1937
1938 7
        return $usedArray;
1939
    }
1940
1941
    /**
1942
     * alias: for "Arrayy->getArray()"
1943
     *
1944
     * @return array
1945
     *
1946
     * @see Arrayy::getArray()
1947
     */
1948 1
    public function getAll(): array
1949
    {
1950 1
        return $this->getArray();
1951
    }
1952
1953
    /**
1954
     * Get the current array from the "Arrayy"-object.
1955
     *
1956
     * @param bool $convertAllArrayyElements
1957
     *
1958
     * @return array
1959
     */
1960 840
    public function getArray($convertAllArrayyElements = false): array
1961
    {
1962
        // init
1963 840
        $array = [];
1964
1965 840
        if ($convertAllArrayyElements) {
1966 1
            foreach ($this->getGenerator() as $key => $value) {
1967 1
                if ($value instanceof Arrayy) {
1968 1
                    $value = $value->getArray(true);
1969
                }
1970
1971 1
                $array[$key] = $value;
1972
            }
1973
        } else {
1974 840
            foreach ($this->getGenerator() as $key => $value) {
1975 731
                $array[$key] = $value;
1976
            }
1977
        }
1978
1979 840
        return $array;
1980
    }
1981
1982
    /**
1983
     * Returns the values from a single column of the input array, identified by
1984
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1985
     *
1986
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1987
     * array by the values from the $indexKey column in the input array.
1988
     *
1989
     * @param mixed $columnKey
1990
     * @param mixed $indexKey
1991
     *
1992
     * @return static
1993
     *                <p>(Immutable)</p>
1994
     */
1995 1
    public function getColumn($columnKey = null, $indexKey = null): self
1996
    {
1997 1
        return static::create(
1998 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1999 1
            $this->iteratorClass,
2000 1
            false
2001
        );
2002
    }
2003
2004
    /**
2005
     * Get the current array from the "Arrayy"-object as generator.
2006
     *
2007
     * @return \Generator
2008
     */
2009 918
    public function getGenerator(): \Generator
2010
    {
2011 918
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2012 39
            yield from $this->generator;
2013
        }
2014
2015 918
        yield from $this->array;
2016 874
    }
2017
2018
    /**
2019
     * alias: for "Arrayy->keys()"
2020
     *
2021
     * @return static
2022
     *                <p>(Immutable)</p>
2023
     *
2024
     * @see Arrayy::keys()
2025
     */
2026 2
    public function getKeys(): self
2027
    {
2028 2
        return $this->keys();
2029
    }
2030
2031
    /**
2032
     * Get the current array from the "Arrayy"-object as object.
2033
     *
2034
     * @return \stdClass
2035
     */
2036 4
    public function getObject(): \stdClass
2037
    {
2038 4
        return self::arrayToObject($this->getArray());
2039
    }
2040
2041
    /**
2042
     * alias: for "Arrayy->randomImmutable()"
2043
     *
2044
     * @return static
2045
     *                <p>(Immutable)</p>
2046
     *
2047
     * @see Arrayy::randomImmutable()
2048
     */
2049 4
    public function getRandom(): self
2050
    {
2051 4
        return $this->randomImmutable();
2052
    }
2053
2054
    /**
2055
     * alias: for "Arrayy->randomKey()"
2056
     *
2057
     * @return mixed
2058
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2059
     *
2060
     * @see Arrayy::randomKey()
2061
     */
2062 3
    public function getRandomKey()
2063
    {
2064 3
        return $this->randomKey();
2065
    }
2066
2067
    /**
2068
     * alias: for "Arrayy->randomKeys()"
2069
     *
2070
     * @param int $number
2071
     *
2072
     * @return static
2073
     *                <p>(Immutable)</p>
2074
     *
2075
     * @see Arrayy::randomKeys()
2076
     */
2077 8
    public function getRandomKeys(int $number): self
2078
    {
2079 8
        return $this->randomKeys($number);
2080
    }
2081
2082
    /**
2083
     * alias: for "Arrayy->randomValue()"
2084
     *
2085
     * @return mixed
2086
     *               <p>Get a random value or null if there wasn't a value.</p>
2087
     *
2088
     * @see Arrayy::randomValue()
2089
     */
2090 3
    public function getRandomValue()
2091
    {
2092 3
        return $this->randomValue();
2093
    }
2094
2095
    /**
2096
     * alias: for "Arrayy->randomValues()"
2097
     *
2098
     * @param int $number
2099
     *
2100
     * @return static
2101
     *                <p>(Immutable)</p>
2102
     *
2103
     * @see Arrayy::randomValues()
2104
     */
2105 6
    public function getRandomValues(int $number): self
2106
    {
2107 6
        return $this->randomValues($number);
2108
    }
2109
2110
    /**
2111
     * Group values from a array according to the results of a closure.
2112
     *
2113
     * @param callable|string $grouper  <p>A callable function name.</p>
2114
     * @param bool            $saveKeys
2115
     *
2116
     * @return static
2117
     *                <p>(Immutable)</p>
2118
     */
2119 4
    public function group($grouper, bool $saveKeys = false): self
2120
    {
2121
        // init
2122 4
        $result = [];
2123
2124
        // Iterate over values, group by property/results from closure.
2125 4
        foreach ($this->getGenerator() as $key => $value) {
2126 4
            if (\is_callable($grouper) === true) {
2127 3
                $groupKey = $grouper($value, $key);
2128
            } else {
2129 1
                $groupKey = $this->get($grouper);
2130
            }
2131
2132 4
            $newValue = $this->get($groupKey, null, $result);
2133
2134 4
            if ($groupKey instanceof self) {
2135
                $groupKey = $groupKey->getArray();
2136
            }
2137
2138 4
            if ($newValue instanceof self) {
2139 4
                $newValue = $newValue->getArray();
2140
            }
2141
2142
            // Add to results.
2143 4
            if ($groupKey !== null) {
2144 3
                if ($saveKeys) {
2145 2
                    $result[$groupKey] = $newValue;
2146 2
                    $result[$groupKey][$key] = $value;
2147
                } else {
2148 1
                    $result[$groupKey] = $newValue;
2149 4
                    $result[$groupKey][] = $value;
2150
                }
2151
            }
2152
        }
2153
2154 4
        return static::create(
2155 4
            $result,
2156 4
            $this->iteratorClass,
2157 4
            false
2158
        );
2159
    }
2160
2161
    /**
2162
     * Check if an array has a given value.
2163
     *
2164
     * INFO: if you need to search recursive please use ```contains()```
2165
     *
2166
     * @param mixed $value
2167
     *
2168
     * @return bool
2169
     */
2170 1
    public function hasValue($value): bool
2171
    {
2172 1
        return $this->contains($value);
2173
    }
2174
2175
    /**
2176
     * Check if an array has a given key.
2177
     *
2178
     * @param mixed $key
2179
     *
2180
     * @return bool
2181
     */
2182 23
    public function has($key): bool
2183
    {
2184 23
        static $UN_FOUND = null;
2185
2186 23
        if ($UN_FOUND === null) {
2187
            // Generate unique string to use as marker.
2188 1
            $UN_FOUND = \uniqid('arrayy', true);
2189
        }
2190
2191 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2192
    }
2193
2194
    /**
2195
     * Implodes the values of this array.
2196
     *
2197
     * @param string $glue
2198
     *
2199
     * @return string
2200
     */
2201 28
    public function implode(string $glue = ''): string
2202
    {
2203 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2204
    }
2205
2206
    /**
2207
     * Implodes the keys of this array.
2208
     *
2209
     * @param string $glue
2210
     *
2211
     * @return string
2212
     */
2213 8
    public function implodeKeys(string $glue = ''): string
2214
    {
2215 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2216
    }
2217
2218
    /**
2219
     * Given a list and an iterate-function that returns
2220
     * a key for each element in the list (or a property name),
2221
     * returns an object with an index of each item.
2222
     *
2223
     * @param mixed $key
2224
     *
2225
     * @return static
2226
     *                <p>(Immutable)</p>
2227
     */
2228 4
    public function indexBy($key): self
2229
    {
2230
        // init
2231 4
        $results = [];
2232
2233 4
        foreach ($this->getGenerator() as $a) {
2234 4
            if (\array_key_exists($key, $a) === true) {
2235 4
                $results[$a[$key]] = $a;
2236
            }
2237
        }
2238
2239 4
        return static::create(
2240 4
            $results,
2241 4
            $this->iteratorClass,
2242 4
            false
2243
        );
2244
    }
2245
2246
    /**
2247
     * alias: for "Arrayy->searchIndex()"
2248
     *
2249
     * @param mixed $value <p>The value to search for.</p>
2250
     *
2251
     * @return mixed
2252
     *
2253
     * @see Arrayy::searchIndex()
2254
     */
2255 4
    public function indexOf($value)
2256
    {
2257 4
        return $this->searchIndex($value);
2258
    }
2259
2260
    /**
2261
     * Get everything but the last..$to items.
2262
     *
2263
     * @param int $to
2264
     *
2265
     * @return static
2266
     *                <p>(Immutable)</p>
2267
     */
2268 12
    public function initial(int $to = 1): self
2269
    {
2270 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2271
    }
2272
2273
    /**
2274
     * Return an array with all elements found in input array.
2275
     *
2276
     * @param array ...$array
2277
     *
2278
     * @return static
2279
     *                <p>(Immutable)</p>
2280
     */
2281 1
    public function intersectionMulti(...$array): self
2282
    {
2283 1
        return static::create(
2284 1
            \array_values(\array_intersect($this->getArray(), ...$array)),
2285 1
            $this->iteratorClass,
2286 1
            false
2287
        );
2288
    }
2289
2290
    /**
2291
     * Return an array with all elements found in input array.
2292
     *
2293
     * @param array $search
2294
     * @param bool  $keepKeys
2295
     *
2296
     * @return static
2297
     *                <p>(Immutable)</p>
2298
     */
2299 4
    public function intersection(array $search, bool $keepKeys = false): self
2300
    {
2301 4
        if ($keepKeys) {
2302 1
            return static::create(
2303 1
                \array_uintersect(
2304 1
                    $this->array,
2305 1
                    $search,
2306
                    static function ($a, $b) {
2307 1
                        return $a === $b ? 0 : -1;
2308 1
                    }
2309
                ),
2310 1
                $this->iteratorClass,
2311 1
                false
2312
            );
2313
        }
2314
2315 3
        return static::create(
2316 3
            \array_values(\array_intersect($this->getArray(), $search)),
2317 3
            $this->iteratorClass,
2318 3
            false
2319
        );
2320
    }
2321
2322
    /**
2323
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2324
     *
2325
     * @param array $search
2326
     *
2327
     * @return bool
2328
     */
2329 1
    public function intersects(array $search): bool
2330
    {
2331 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2332
    }
2333
2334
    /**
2335
     * Invoke a function on all of an array's values.
2336
     *
2337
     * @param callable $callable
2338
     * @param mixed    $arguments
2339
     *
2340
     * @return static
2341
     *                <p>(Immutable)</p>
2342
     */
2343 1
    public function invoke($callable, $arguments = []): self
2344
    {
2345
        // If one argument given for each iteration, create an array for it.
2346 1
        if (\is_array($arguments) === false) {
2347 1
            $arguments = \array_fill(
2348 1
                0,
2349 1
                \count($this->getArray(), \COUNT_NORMAL),
2350 1
                $arguments
2351
            );
2352
        }
2353
2354
        // If the callable has arguments, pass them.
2355 1
        if ($arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2356 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2357
        } else {
2358 1
            $array = $this->map($callable);
2359
        }
2360
2361 1
        return static::create(
2362 1
            $array,
2363 1
            $this->iteratorClass,
2364 1
            false
2365
        );
2366
    }
2367
2368
    /**
2369
     * Check whether array is associative or not.
2370
     *
2371
     * @param bool $recursive
2372
     *
2373
     * @return bool
2374
     *              <p>Returns true if associative, false otherwise.</p>
2375
     */
2376 15 View Code Duplication
    public function isAssoc(bool $recursive = false): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2377
    {
2378 15
        if ($this->isEmpty()) {
2379 3
            return false;
2380
        }
2381
2382 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2383 13
            if ((string) $key !== $key) {
2384 13
                return false;
2385
            }
2386
        }
2387
2388 3
        return true;
2389
    }
2390
2391
    /**
2392
     * Check if a given key or keys are empty.
2393
     *
2394
     * @param int|int[]|string|string[]|null $keys
2395
     *
2396
     * @return bool
2397
     *              <p>Returns true if empty, false otherwise.</p>
2398
     */
2399 38
    public function isEmpty($keys = null): bool
2400
    {
2401 38
        if ($this->generator) {
2402
            return $this->getArray() === [];
2403
        }
2404
2405 38
        if ($keys === null) {
2406 38
            return $this->array === [];
2407
        }
2408
2409
        foreach ((array) $keys as $key) {
2410
            if (!empty($this->get($key))) {
2411
                return false;
2412
            }
2413
        }
2414
2415
        return true;
2416
    }
2417
2418
    /**
2419
     * Check if the current array is equal to the given "$array" or not.
2420
     *
2421
     * @param array $array
2422
     *
2423
     * @return bool
2424
     */
2425 1
    public function isEqual(array $array): bool
2426
    {
2427 1
        return $this->getArray() === $array;
2428
    }
2429
2430
    /**
2431
     * Check if the current array is a multi-array.
2432
     *
2433
     * @return bool
2434
     */
2435 22
    public function isMultiArray(): bool
2436
    {
2437
        return !(
2438 22
            \count($this->getArray(), \COUNT_NORMAL)
2439
            ===
2440 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2441
        );
2442
    }
2443
2444
    /**
2445
     * Check whether array is numeric or not.
2446
     *
2447
     * @return bool
2448
     *              <p>Returns true if numeric, false otherwise.</p>
2449
     */
2450 5 View Code Duplication
    public function isNumeric(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2451
    {
2452 5
        if ($this->isEmpty()) {
2453 2
            return false;
2454
        }
2455
2456 4
        foreach ($this->keys()->getGenerator() as $key) {
2457 4
            if ((int) $key !== $key) {
2458 4
                return false;
2459
            }
2460
        }
2461
2462 2
        return true;
2463
    }
2464
2465
    /**
2466
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2467
     *
2468
     * @param bool $recursive
2469
     *
2470
     * @return bool
2471
     */
2472 1
    public function isSequential(bool $recursive = false): bool
2473
    {
2474
2475
        // recursive
2476
2477 1
        if ($recursive === true) {
2478
            return $this->array_keys_recursive($this->getArray())
2479
                   ===
2480
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2481
        }
2482
2483
        // non recursive
2484
2485 1
        return \array_keys($this->getArray())
2486
               ===
2487 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2488
    }
2489
2490
    /**
2491
     * @return array
2492
     */
2493
    public function jsonSerialize(): array
2494
    {
2495
        return $this->getArray();
2496
    }
2497
2498
    /**
2499
     * Checks if the given key exists in the provided array.
2500
     *
2501
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2502
     *       then you need to use "Arrayy->offsetExists()".
2503
     *
2504
     * @param int|string $key the key to look for
2505
     *
2506
     * @return bool
2507
     */
2508 123
    public function keyExists($key): bool
2509
    {
2510 123
        return \array_key_exists($key, $this->array);
2511
    }
2512
2513
    /**
2514
     * Get all keys from the current array.
2515
     *
2516
     * @param bool       $recursive     [optional] <p>
2517
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2518
     *                                  </p>
2519
     * @param mixed|null $search_values [optional] <p>
2520
     *                                  If specified, then only keys containing these values are returned.
2521
     *                                  </p>
2522
     * @param bool       $strict        [optional] <p>
2523
     *                                  Determines if strict comparison (===) should be used during the search.
2524
     *                                  </p>
2525
     *
2526
     * @return static
2527
     *                <p>(Immutable) An array of all the keys in input.</p>
2528
     */
2529 29
    public function keys(
2530
        bool $recursive = false,
2531
        $search_values = null,
2532
        bool $strict = true
2533
    ): self {
2534
2535
        // recursive
2536
2537 29
        if ($recursive === true) {
2538 4
            $array = $this->array_keys_recursive(
2539 4
                null,
2540 4
                $search_values,
2541 4
                $strict
2542
            );
2543
2544 4
            return static::create(
2545 4
                $array,
2546 4
                $this->iteratorClass,
2547 4
                false
2548
            );
2549
        }
2550
2551
        // non recursive
2552
2553 28
        if ($search_values === null) {
2554
            $arrayFunction = function () {
2555 28
                foreach ($this->getGenerator() as $key => $value) {
2556 26
                    yield $key;
2557
                }
2558 28
            };
2559
        } else {
2560
            $arrayFunction = function () use ($search_values, $strict) {
2561 1
                $is_array_tmp = \is_array($search_values);
2562
2563 1
                foreach ($this->getGenerator() as $key => $value) {
2564 View Code Duplication
                    if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2565
                        (
2566 1
                            $is_array_tmp === false
2567
                            &&
2568 1
                            $strict === true
2569
                            &&
2570 1
                            $search_values === $value
2571
                        )
2572
                        ||
2573
                        (
2574 1
                            $is_array_tmp === false
2575
                            &&
2576 1
                            $strict === false
2577
                            &&
2578 1
                            $search_values == $value
2579
                        )
2580
                        ||
2581
                        (
2582 1
                            $is_array_tmp === true
2583
                            &&
2584 1
                            \in_array($value, $search_values, $strict)
2585
                        )
2586
                    ) {
2587 1
                        yield $key;
2588
                    }
2589
                }
2590 1
            };
2591
        }
2592
2593 28
        return static::create(
2594 28
            $arrayFunction,
2595 28
            $this->iteratorClass,
2596 28
            false
2597
        );
2598
    }
2599
2600
    /**
2601
     * Sort an array by key in reverse order.
2602
     *
2603
     * @param int $sort_flags [optional] <p>
2604
     *                        You may modify the behavior of the sort using the optional
2605
     *                        parameter sort_flags, for details
2606
     *                        see sort.
2607
     *                        </p>
2608
     *
2609
     * @return static
2610
     *                <p>(Mutable) Return this Arrayy object.</p>
2611
     */
2612 4
    public function krsort(int $sort_flags = 0): self
2613
    {
2614 4
        $this->generatorToArray();
2615
2616 4
        \krsort($this->array, $sort_flags);
2617
2618 4
        return $this;
2619
    }
2620
2621
    /**
2622
     * Get the last value from the current array.
2623
     *
2624
     * @return mixed
2625
     *               <p>Return null if there wasn't a element.</p>
2626
     */
2627 17
    public function last()
2628
    {
2629 17
        $key_last = $this->lastKey();
2630 17
        if ($key_last === null) {
2631 2
            return null;
2632
        }
2633
2634 15
        return $this->get($key_last);
2635
    }
2636
2637
    /**
2638
     * Get the last key from the current array.
2639
     *
2640
     * @return mixed
2641
     *               <p>Return null if there wasn't a element.</p>
2642
     */
2643 21
    public function lastKey()
2644
    {
2645 21
        $this->generatorToArray();
2646
2647 21
        return \array_key_last($this->array);
2648
    }
2649
2650
    /**
2651
     * Get the last value(s) from the current array.
2652
     *
2653
     * @param int|null $number
2654
     *
2655
     * @return static
2656
     *                <p>(Immutable)</p>
2657
     */
2658 13
    public function lastsImmutable(int $number = null): self
2659
    {
2660 13
        if ($this->isEmpty()) {
2661 1
            return static::create(
2662 1
                [],
2663 1
                $this->iteratorClass,
2664 1
                false
2665
            );
2666
        }
2667
2668 12
        if ($number === null) {
2669 8
            $poppedValue = $this->last();
2670
2671 8
            if ($poppedValue === null) {
2672 1
                $poppedValue = [$poppedValue];
2673
            } else {
2674 7
                $poppedValue = (array) $poppedValue;
2675
            }
2676
2677 8
            $arrayy = static::create(
2678 8
                $poppedValue,
2679 8
                $this->iteratorClass,
2680 8
                false
2681
            );
2682
        } else {
2683 4
            $number = (int) $number;
2684 4
            $arrayy = $this->rest(-$number);
2685
        }
2686
2687 12
        return $arrayy;
2688
    }
2689
2690
    /**
2691
     * Get the last value(s) from the current array.
2692
     *
2693
     * @param int|null $number
2694
     *
2695
     * @return static
2696
     *                <p>(Mutable)</p>
2697
     */
2698 13
    public function lastsMutable(int $number = null): self
2699
    {
2700 13
        if ($this->isEmpty()) {
2701 1
            return $this;
2702
        }
2703
2704 12
        if ($number === null) {
2705 8
            $poppedValue = $this->last();
2706
2707 8
            if ($poppedValue === null) {
2708 1
                $poppedValue = [$poppedValue];
2709
            } else {
2710 7
                $poppedValue = (array) $poppedValue;
2711
            }
2712
2713 8
            $this->array = static::create(
2714 8
                $poppedValue,
2715 8
                $this->iteratorClass,
2716 8
                false
2717 8
            )->getArray();
2718
        } else {
2719 4
            $number = (int) $number;
2720 4
            $this->array = $this->rest(-$number)->getArray();
2721
        }
2722
2723 12
        $this->generator = null;
2724
2725 12
        return $this;
2726
    }
2727
2728
    /**
2729
     * Count the values from the current array.
2730
     *
2731
     * alias: for "Arrayy->count()"
2732
     *
2733
     * @param int $mode
2734
     *
2735
     * @return int
2736
     *
2737
     * @see Arrayy::count()
2738
     */
2739 20
    public function length(int $mode = \COUNT_NORMAL): int
2740
    {
2741 20
        return $this->count($mode);
2742
    }
2743
2744
    /**
2745
     * Apply the given function to the every element of the array,
2746
     * collecting the results.
2747
     *
2748
     * @param callable $callable
2749
     * @param bool     $useKeyAsSecondParameter
2750
     * @param mixed    ...$arguments
2751
     *
2752
     * @return static
2753
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2754
     */
2755 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2756
    {
2757 5
        $useArguments = \func_num_args() > 2;
2758
2759 5
        return static::create(
2760
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2761 5
                foreach ($this->getGenerator() as $key => $value) {
2762 4
                    if ($useArguments) {
2763 3
                        if ($useKeyAsSecondParameter) {
2764
                            yield $key => $callable($value, $key, ...$arguments);
2765
                        } else {
2766 3
                            yield $key => $callable($value, ...$arguments);
2767
                        }
2768
                    } else {
2769
                        /** @noinspection NestedPositiveIfStatementsInspection */
2770 4
                        if ($useKeyAsSecondParameter) {
2771
                            yield $key => $callable($value, $key);
2772
                        } else {
2773 4
                            yield $key => $callable($value);
2774
                        }
2775
                    }
2776
                }
2777 5
            },
2778 5
            $this->iteratorClass,
2779 5
            false
2780
        );
2781
    }
2782
2783
    /**
2784
     * Check if all items in current array match a truth test.
2785
     *
2786
     * @param \Closure $closure
2787
     *
2788
     * @return bool
2789
     */
2790 15 View Code Duplication
    public function matches(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2791
    {
2792 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2793 2
            return false;
2794
        }
2795
2796 13
        foreach ($this->getGenerator() as $key => $value) {
2797 13
            $value = $closure($value, $key);
2798
2799 13
            if ($value === false) {
2800 13
                return false;
2801
            }
2802
        }
2803
2804 7
        return true;
2805
    }
2806
2807
    /**
2808
     * Check if any item in the current array matches a truth test.
2809
     *
2810
     * @param \Closure $closure
2811
     *
2812
     * @return bool
2813
     */
2814 14 View Code Duplication
    public function matchesAny(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2815
    {
2816 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2817 2
            return false;
2818
        }
2819
2820 12
        foreach ($this->getGenerator() as $key => $value) {
2821 12
            $value = $closure($value, $key);
2822
2823 12
            if ($value === true) {
2824 12
                return true;
2825
            }
2826
        }
2827
2828 4
        return false;
2829
    }
2830
2831
    /**
2832
     * Get the max value from an array.
2833
     *
2834
     * @return mixed
2835
     */
2836 10 View Code Duplication
    public function max()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2837
    {
2838 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2839 1
            return false;
2840
        }
2841
2842 9
        return \max($this->getArray());
2843
    }
2844
2845
    /**
2846
     * Merge the new $array into the current array.
2847
     *
2848
     * - keep key,value from the current array, also if the index is in the new $array
2849
     *
2850
     * @param array $array
2851
     * @param bool  $recursive
2852
     *
2853
     * @return static
2854
     *                <p>(Immutable)</p>
2855
     */
2856 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2857
    {
2858 25
        if ($recursive === true) {
2859 4
            $result = \array_replace_recursive($this->getArray(), $array);
2860
        } else {
2861 21
            $result = \array_replace($this->getArray(), $array);
2862
        }
2863
2864 25
        return static::create(
2865 25
            $result,
2866 25
            $this->iteratorClass,
2867 25
            false
2868
        );
2869
    }
2870
2871
    /**
2872
     * Merge the new $array into the current array.
2873
     *
2874
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2875
     * - create new indexes
2876
     *
2877
     * @param array $array
2878
     * @param bool  $recursive
2879
     *
2880
     * @return static
2881
     *                <p>(Immutable)</p>
2882
     */
2883 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2884
    {
2885 16
        if ($recursive === true) {
2886 4
            $result = \array_merge_recursive($this->getArray(), $array);
2887
        } else {
2888 12
            $result = \array_merge($this->getArray(), $array);
2889
        }
2890
2891 16
        return static::create(
2892 16
            $result,
2893 16
            $this->iteratorClass,
2894 16
            false
2895
        );
2896
    }
2897
2898
    /**
2899
     * Merge the the current array into the $array.
2900
     *
2901
     * - use key,value from the new $array, also if the index is in the current array
2902
     *
2903
     * @param array $array
2904
     * @param bool  $recursive
2905
     *
2906
     * @return static
2907
     *                <p>(Immutable)</p>
2908
     */
2909 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2910
    {
2911 16
        if ($recursive === true) {
2912 4
            $result = \array_replace_recursive($array, $this->getArray());
2913
        } else {
2914 12
            $result = \array_replace($array, $this->getArray());
2915
        }
2916
2917 16
        return static::create(
2918 16
            $result,
2919 16
            $this->iteratorClass,
2920 16
            false
2921
        );
2922
    }
2923
2924
    /**
2925
     * Merge the current array into the new $array.
2926
     *
2927
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2928
     * - create new indexes
2929
     *
2930
     * @param array $array
2931
     * @param bool  $recursive
2932
     *
2933
     * @return static
2934
     *                <p>(Immutable)</p>
2935
     */
2936 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2937
    {
2938 17
        if ($recursive === true) {
2939 4
            $result = \array_merge_recursive($array, $this->getArray());
2940
        } else {
2941 13
            $result = \array_merge($array, $this->getArray());
2942
        }
2943
2944 17
        return static::create(
2945 17
            $result,
2946 17
            $this->iteratorClass,
2947 17
            false
2948
        );
2949
    }
2950
2951
    /**
2952
     * @return ArrayyMeta|static
2953
     */
2954 15
    public static function meta()
2955
    {
2956 15
        return (new ArrayyMeta())->getMetaObject(static::class);
2957
    }
2958
2959
    /**
2960
     * Get the min value from an array.
2961
     *
2962
     * @return mixed
2963
     */
2964 10 View Code Duplication
    public function min()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2965
    {
2966 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2967 1
            return false;
2968
        }
2969
2970 9
        return \min($this->getArray());
2971
    }
2972
2973
    /**
2974
     * Get the most used value from the array.
2975
     *
2976
     * @return mixed
2977
     *               <p>Return null if there wasn't a element.</p>
2978
     */
2979 3
    public function mostUsedValue()
2980
    {
2981 3
        return $this->countValues()->arsort()->firstKey();
2982
    }
2983
2984
    /**
2985
     * Get the most used value from the array.
2986
     *
2987
     * @param int|null $number <p>How many values you will take?</p>
2988
     *
2989
     * @return static
2990
     *                <p>(Immutable)</p>
2991
     */
2992 3
    public function mostUsedValues(int $number = null): self
2993
    {
2994 3
        return $this->countValues()->arsort()->firstsKeys($number);
2995
    }
2996
2997
    /**
2998
     * Move an array element to a new index.
2999
     *
3000
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3001
     *
3002
     * @param int|string $from
3003
     * @param int        $to
3004
     *
3005
     * @return static
3006
     *                <p>(Immutable)</p>
3007
     */
3008 1
    public function moveElement($from, $to): self
3009
    {
3010 1
        $array = $this->getArray();
3011
3012 1
        if ((int) $from === $from) {
3013 1
            $tmp = \array_splice($array, $from, 1);
3014 1
            \array_splice($array, (int) $to, 0, $tmp);
3015 1
            $output = $array;
3016 1
        } elseif ((string) $from === $from) {
3017 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3018 1
            $itemToMove = $array[$from];
3019 1
            if ($indexToMove !== false) {
3020 1
                \array_splice($array, $indexToMove, 1);
3021
            }
3022 1
            $i = 0;
3023 1
            $output = [];
3024 1
            foreach ($array as $key => $item) {
3025 1
                if ($i === $to) {
3026 1
                    $output[$from] = $itemToMove;
3027
                }
3028 1
                $output[$key] = $item;
3029 1
                ++$i;
3030
            }
3031
        } else {
3032
            $output = [];
3033
        }
3034
3035 1
        return static::create(
3036 1
            $output,
3037 1
            $this->iteratorClass,
3038 1
            false
3039
        );
3040
    }
3041
3042
    /**
3043
     * Move an array element to the first place.
3044
     *
3045
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3046
     *       loss the keys of an indexed array.
3047
     *
3048
     * @param int|string $key
3049
     *
3050
     * @return static
3051
     *                <p>(Immutable)</p>
3052
     */
3053 1 View Code Duplication
    public function moveElementToFirstPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3054
    {
3055 1
        $array = $this->getArray();
3056
3057 1
        if ($this->offsetExists($key)) {
3058 1
            $tmpValue = $this->get($key);
3059 1
            unset($array[$key]);
3060 1
            $array = [$key => $tmpValue] + $array;
3061
        }
3062
3063 1
        return static::create(
3064 1
            $array,
3065 1
            $this->iteratorClass,
3066 1
            false
3067
        );
3068
    }
3069
3070
    /**
3071
     * Move an array element to the last place.
3072
     *
3073
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3074
     *       loss the keys of an indexed array.
3075
     *
3076
     * @param int|string $key
3077
     *
3078
     * @return static
3079
     *                <p>(Immutable)</p>
3080
     */
3081 1 View Code Duplication
    public function moveElementToLastPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3082
    {
3083 1
        $array = $this->getArray();
3084
3085 1
        if ($this->offsetExists($key)) {
3086 1
            $tmpValue = $this->get($key);
3087 1
            unset($array[$key]);
3088 1
            $array += [$key => $tmpValue];
3089
        }
3090
3091 1
        return static::create(
3092 1
            $array,
3093 1
            $this->iteratorClass,
3094 1
            false
3095
        );
3096
    }
3097
3098
    /**
3099
     * Get a subset of the items from the given array.
3100
     *
3101
     * @param mixed[] $keys
3102
     *
3103
     * @return static
3104
     *                <p>(Immutable)</p>
3105
     */
3106
    public function only(array $keys): self
3107
    {
3108
        $array = $this->getArray();
3109
3110
        return static::create(
3111
            \array_intersect_key($array, \array_flip($keys)),
3112
            $this->iteratorClass,
3113
            false
3114
        );
3115
    }
3116
3117
    /**
3118
     * Pad array to the specified size with a given value.
3119
     *
3120
     * @param int   $size  <p>Size of the result array.</p>
3121
     * @param mixed $value <p>Empty value by default.</p>
3122
     *
3123
     * @return static
3124
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3125
     */
3126 5
    public function pad(int $size, $value): self
3127
    {
3128 5
        return static::create(
3129 5
            \array_pad($this->getArray(), $size, $value),
3130 5
            $this->iteratorClass,
3131 5
            false
3132
        );
3133
    }
3134
3135
    /**
3136
     * Pop a specified value off the end of the current array.
3137
     *
3138
     * @return mixed
3139
     *               <p>(Mutable) The popped element from the current array.</p>
3140
     */
3141 5
    public function pop()
3142
    {
3143 5
        $this->generatorToArray();
3144
3145 5
        return \array_pop($this->array);
3146
    }
3147
3148
    /**
3149
     * Prepend a (key) + value to the current array.
3150
     *
3151
     * @param mixed $value
3152
     * @param mixed $key
3153
     *
3154
     * @return static
3155
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3156
     */
3157 11
    public function prepend($value, $key = null): self
3158
    {
3159 11
        $this->generatorToArray();
3160
3161 11
        if ($this->properties !== []) {
3162 3
            $this->checkType($key, $value);
3163
        }
3164
3165 9
        if ($key === null) {
3166 8
            \array_unshift($this->array, $value);
3167
        } else {
3168 2
            $this->array = [$key => $value] + $this->array;
3169
        }
3170
3171 9
        return $this;
3172
    }
3173
3174
    /**
3175
     * Add a suffix to each key.
3176
     *
3177
     * @param mixed $suffix
3178
     *
3179
     * @return static
3180
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3181
     */
3182 10 View Code Duplication
    public function prependToEachKey($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3183
    {
3184
        // init
3185 10
        $result = [];
3186
3187 10
        foreach ($this->getGenerator() as $key => $item) {
3188 9
            if ($item instanceof self) {
3189
                $result[$key] = $item->prependToEachKey($suffix);
3190 9
            } elseif (\is_array($item) === true) {
3191
                $result[$key] = self::create(
3192
                    $item,
3193
                    $this->iteratorClass,
3194
                    false
3195
                )->prependToEachKey($suffix)
3196
                    ->toArray();
3197
            } else {
3198 9
                $result[$key . $suffix] = $item;
3199
            }
3200
        }
3201
3202 10
        return self::create(
3203 10
            $result,
3204 10
            $this->iteratorClass,
3205 10
            false
3206
        );
3207
    }
3208
3209
    /**
3210
     * Add a suffix to each value.
3211
     *
3212
     * @param mixed $suffix
3213
     *
3214
     * @return static
3215
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3216
     */
3217 10 View Code Duplication
    public function prependToEachValue($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3218
    {
3219
        // init
3220 10
        $result = [];
3221
3222 10
        foreach ($this->getGenerator() as $key => $item) {
3223 9
            if ($item instanceof self) {
3224
                $result[$key] = $item->prependToEachValue($suffix);
3225 9
            } elseif (\is_array($item) === true) {
3226
                $result[$key] = self::create(
3227
                    $item,
3228
                    $this->iteratorClass,
3229
                    false
3230
                )->prependToEachValue($suffix)
3231
                    ->toArray();
3232 9
            } elseif (\is_object($item) === true) {
3233 1
                $result[$key] = $item;
3234
            } else {
3235 9
                $result[$key] = $item . $suffix;
3236
            }
3237
        }
3238
3239 10
        return self::create(
3240 10
            $result,
3241 10
            $this->iteratorClass,
3242 10
            false
3243
        );
3244
    }
3245
3246
    /**
3247
     * Return the value of a given key and
3248
     * delete the key.
3249
     *
3250
     * @param int|int[]|string|string[]|null $keyOrKeys
3251
     * @param mixed                          $fallback
3252
     *
3253
     * @return mixed
3254
     */
3255 1
    public function pull($keyOrKeys = null, $fallback = null)
3256
    {
3257 1
        if ($keyOrKeys === null) {
3258
            $array = $this->getArray();
3259
            $this->clear();
3260
3261
            return $array;
3262
        }
3263
3264 1
        if (\is_array($keyOrKeys) === true) {
3265 1
            $valueOrValues = [];
3266 1
            foreach ($keyOrKeys as $key) {
3267 1
                $valueOrValues[] = $this->get($key, $fallback);
3268 1
                $this->offsetUnset($key);
3269
            }
3270
        } else {
3271 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3272 1
            $this->offsetUnset($keyOrKeys);
3273
        }
3274
3275 1
        return $valueOrValues;
3276
    }
3277
3278
    /**
3279
     * Push one or more values onto the end of array at once.
3280
     *
3281
     * @param array ...$args
3282
     *
3283
     * @return static
3284
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3285
     *
3286
     * @noinspection ReturnTypeCanBeDeclaredInspection
3287
     */
3288 5
    public function push(...$args)
3289
    {
3290 5
        $this->generatorToArray();
3291
3292
        if (
3293 5
            $this->checkPropertyTypes
3294
            &&
3295 5
            $this->properties !== []
3296
        ) {
3297 1
            foreach ($args as $key => $value) {
3298 1
                $this->checkType($key, $value);
3299
            }
3300
        }
3301
3302 5
        \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3303
3304 5
        return $this;
3305
    }
3306
3307
    /**
3308
     * Get a random value from the current array.
3309
     *
3310
     * @param int|null $number <p>How many values you will take?</p>
3311
     *
3312
     * @return static
3313
     *                <p>(Immutable)</p>
3314
     */
3315 19
    public function randomImmutable(int $number = null): self
3316
    {
3317 19
        $this->generatorToArray();
3318
3319 19 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3320 1
            return static::create(
3321 1
                [],
3322 1
                $this->iteratorClass,
3323 1
                false
3324
            );
3325
        }
3326
3327 18 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3328
            /** @noinspection NonSecureArrayRandUsageInspection */
3329 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3330
3331 13
            return static::create(
3332 13
                $arrayRandValue,
3333 13
                $this->iteratorClass,
3334 13
                false
3335
            );
3336
        }
3337
3338 6
        $arrayTmp = $this->array;
3339
        /** @noinspection NonSecureShuffleUsageInspection */
3340 6
        \shuffle($arrayTmp);
3341
3342 6
        return static::create(
3343 6
            $arrayTmp,
3344 6
            $this->iteratorClass,
3345 6
            false
3346 6
        )->firstsImmutable($number);
3347
    }
3348
3349
    /**
3350
     * Pick a random key/index from the keys of this array.
3351
     *
3352
     * @throws \RangeException If array is empty
3353
     *
3354
     * @return mixed
3355
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3356
     */
3357 4
    public function randomKey()
3358
    {
3359 4
        $result = $this->randomKeys(1);
3360
3361 4
        if (!isset($result[0])) {
3362
            $result[0] = null;
3363
        }
3364
3365 4
        return $result[0];
3366
    }
3367
3368
    /**
3369
     * Pick a given number of random keys/indexes out of this array.
3370
     *
3371
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3372
     *
3373
     * @throws \RangeException If array is empty
3374
     *
3375
     * @return static
3376
     *                <p>(Immutable)</p>
3377
     */
3378 13
    public function randomKeys(int $number): self
3379
    {
3380 13
        $this->generatorToArray();
3381
3382 13
        $count = \count($this->array, \COUNT_NORMAL);
3383
3384 13
        if ($number === 0 || $number > $count) {
3385 2
            throw new \RangeException(
3386 2
                \sprintf(
3387 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3388 2
                    $number,
3389 2
                    $count
3390
                )
3391
            );
3392
        }
3393
3394 11
        $result = (array) \array_rand($this->array, $number);
3395
3396 11
        return static::create(
3397 11
            $result,
3398 11
            $this->iteratorClass,
3399 11
            false
3400
        );
3401
    }
3402
3403
    /**
3404
     * Get a random value from the current array.
3405
     *
3406
     * @param int|null $number <p>How many values you will take?</p>
3407
     *
3408
     * @return static
3409
     *                <p>(Mutable)</p>
3410
     */
3411 17
    public function randomMutable(int $number = null): self
3412
    {
3413 17
        $this->generatorToArray();
3414
3415 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3416
            return static::create(
3417
                [],
3418
                $this->iteratorClass,
3419
                false
3420
            );
3421
        }
3422
3423 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3424
            /** @noinspection NonSecureArrayRandUsageInspection */
3425 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3426 7
            $this->array = $arrayRandValue;
3427
3428 7
            return $this;
3429
        }
3430
3431
        /** @noinspection NonSecureShuffleUsageInspection */
3432 11
        \shuffle($this->array);
3433
3434 11
        return $this->firstsMutable($number);
3435
    }
3436
3437
    /**
3438
     * Pick a random value from the values of this array.
3439
     *
3440
     * @return mixed
3441
     *               <p>Get a random value or null if there wasn't a value.</p>
3442
     */
3443 4
    public function randomValue()
3444
    {
3445 4
        $result = $this->randomImmutable();
3446
3447 4
        if (!isset($result[0])) {
3448
            $result[0] = null;
3449
        }
3450
3451 4
        return $result[0];
3452
    }
3453
3454
    /**
3455
     * Pick a given number of random values out of this array.
3456
     *
3457
     * @param int $number
3458
     *
3459
     * @return static
3460
     *                <p>(Mutable)</p>
3461
     */
3462 7
    public function randomValues(int $number): self
3463
    {
3464 7
        return $this->randomMutable($number);
3465
    }
3466
3467
    /**
3468
     * Get a random value from an array, with the ability to skew the results.
3469
     *
3470
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3471
     *
3472
     * @param array    $array
3473
     * @param int|null $number <p>How many values you will take?</p>
3474
     *
3475
     * @return static
3476
     *                <p>(Immutable)</p>
3477
     */
3478 9
    public function randomWeighted(array $array, int $number = null): self
3479
    {
3480
        // init
3481 9
        $options = [];
3482
3483 9
        foreach ($array as $option => $weight) {
3484 9
            if ($this->searchIndex($option) !== false) {
3485 9
                for ($i = 0; $i < $weight; ++$i) {
3486 1
                    $options[] = $option;
3487
                }
3488
            }
3489
        }
3490
3491 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3492
    }
3493
3494
    /**
3495
     * Reduce the current array via callable e.g. anonymous-function.
3496
     *
3497
     * @param callable $callable
3498
     * @param mixed    $init
3499
     *
3500
     * @return static
3501
     *                <p>(Immutable)</p>
3502
     */
3503 18
    public function reduce($callable, $init = []): self
3504
    {
3505 18
        if ($this->generator) {
3506 1
            $result = $init;
3507
3508 1
            foreach ($this->getGenerator() as $value) {
3509 1
                $result = $callable($result, $value);
3510
            }
3511
3512 1
            return static::create(
3513 1
                $result,
3514 1
                $this->iteratorClass,
3515 1
                false
3516
            );
3517
        }
3518
3519 18
        $result = \array_reduce($this->array, $callable, $init);
3520
3521 18
        if ($result === null) {
3522
            $this->array = [];
3523
        } else {
3524 18
            $this->array = (array) $result;
3525
        }
3526
3527 18
        return static::create(
3528 18
            $this->array,
3529 18
            $this->iteratorClass,
3530 18
            false
3531
        );
3532
    }
3533
3534
    /**
3535
     * @param bool $unique
3536
     *
3537
     * @return static
3538
     *                <p>(Immutable)</p>
3539
     */
3540 14
    public function reduce_dimension(bool $unique = true): self
3541
    {
3542
        // init
3543 14
        $result = [];
3544
3545 14
        foreach ($this->getGenerator() as $val) {
3546 12
            if (\is_array($val) === true) {
3547 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3548
            } else {
3549 12
                $result[] = [$val];
3550
            }
3551
        }
3552
3553 14
        $result = $result === [] ? [] : \array_merge(...$result);
3554
3555 14
        $resultArrayy = new self($result);
3556
3557 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3558
    }
3559
3560
    /**
3561
     * Create a numerically re-indexed Arrayy object.
3562
     *
3563
     * @return static
3564
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3565
     */
3566 9
    public function reindex(): self
3567
    {
3568 9
        $this->generatorToArray();
3569
3570 9
        $this->array = \array_values($this->array);
3571
3572 9
        return $this;
3573
    }
3574
3575
    /**
3576
     * Return all items that fail the truth test.
3577
     *
3578
     * @param \Closure $closure
3579
     *
3580
     * @return static
3581
     *                <p>(Immutable)</p>
3582
     */
3583 1 View Code Duplication
    public function reject(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3584
    {
3585
        // init
3586 1
        $filtered = [];
3587
3588 1
        foreach ($this->getGenerator() as $key => $value) {
3589 1
            if (!$closure($value, $key)) {
3590 1
                $filtered[$key] = $value;
3591
            }
3592
        }
3593
3594 1
        return static::create(
3595 1
            $filtered,
3596 1
            $this->iteratorClass,
3597 1
            false
3598
        );
3599
    }
3600
3601
    /**
3602
     * Remove a value from the current array (optional using dot-notation).
3603
     *
3604
     * @param mixed $key
3605
     *
3606
     * @return static
3607
     *                <p>(Mutable)</p>
3608
     */
3609 18
    public function remove($key): self
3610
    {
3611
        // recursive call
3612 18
        if (\is_array($key) === true) {
3613
            foreach ($key as $k) {
3614
                $this->internalRemove($k);
3615
            }
3616
3617
            return static::create(
3618
                $this->getArray(),
3619
                $this->iteratorClass,
3620
                false
3621
            );
3622
        }
3623
3624 18
        $this->internalRemove($key);
3625
3626 18
        return static::create(
3627 18
            $this->getArray(),
3628 18
            $this->iteratorClass,
3629 18
            false
3630
        );
3631
    }
3632
3633
    /**
3634
     * Remove the first value from the current array.
3635
     *
3636
     * @return static
3637
     *                <p>(Immutable)</p>
3638
     */
3639 7 View Code Duplication
    public function removeFirst(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3640
    {
3641 7
        $tmpArray = $this->getArray();
3642
3643 7
        \array_shift($tmpArray);
3644
3645 7
        return static::create(
3646 7
            $tmpArray,
3647 7
            $this->iteratorClass,
3648 7
            false
3649
        );
3650
    }
3651
3652
    /**
3653
     * Remove the last value from the current array.
3654
     *
3655
     * @return static
3656
     *                <p>(Immutable)</p>
3657
     */
3658 7 View Code Duplication
    public function removeLast(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3659
    {
3660 7
        $tmpArray = $this->getArray();
3661
3662 7
        \array_pop($tmpArray);
3663
3664 7
        return static::create(
3665 7
            $tmpArray,
3666 7
            $this->iteratorClass,
3667 7
            false
3668
        );
3669
    }
3670
3671
    /**
3672
     * Removes a particular value from an array (numeric or associative).
3673
     *
3674
     * @param mixed $value
3675
     *
3676
     * @return static
3677
     *                <p>(Immutable)</p>
3678
     */
3679 7
    public function removeValue($value): self
3680
    {
3681 7
        $this->generatorToArray();
3682
3683
        // init
3684 7
        $isNumericArray = true;
3685
3686 7
        foreach ($this->getGenerator() as $key => $item) {
3687 6
            if ($item === $value) {
3688 6
                if ((int) $key !== $key) {
3689
                    $isNumericArray = false;
3690
                }
3691 6
                unset($this->array[$key]);
3692
            }
3693
        }
3694
3695 7
        if ($isNumericArray) {
3696 7
            $this->array = \array_values($this->array);
3697
        }
3698
3699 7
        return static::create(
3700 7
            $this->array,
3701 7
            $this->iteratorClass,
3702 7
            false
3703
        );
3704
    }
3705
3706
    /**
3707
     * Generate array of repeated arrays.
3708
     *
3709
     * @param int $times <p>How many times has to be repeated.</p>
3710
     *
3711
     * @return static
3712
     *                <p>(Immutable)</p>
3713
     */
3714 1
    public function repeat($times): self
3715
    {
3716 1
        if ($times === 0) {
3717 1
            return static::create([], $this->iteratorClass);
3718
        }
3719
3720 1
        return static::create(
3721 1
            \array_fill(0, (int) $times, $this->getArray()),
3722 1
            $this->iteratorClass,
3723 1
            false
3724
        );
3725
    }
3726
3727
    /**
3728
     * Replace a key with a new key/value pair.
3729
     *
3730
     * @param mixed $replace
3731
     * @param mixed $key
3732
     * @param mixed $value
3733
     *
3734
     * @return static
3735
     *                <p>(Immutable)</p>
3736
     */
3737 2
    public function replace($replace, $key, $value): self
3738
    {
3739 2
        $that = clone $this;
3740
3741 2
        return $that->remove($replace)
3742 2
            ->set($key, $value);
3743
    }
3744
3745
    /**
3746
     * Create an array using the current array as values and the other array as keys.
3747
     *
3748
     * @param array $keys <p>An array of keys.</p>
3749
     *
3750
     * @return static
3751
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3752
     */
3753 2
    public function replaceAllKeys(array $keys): self
3754
    {
3755 2
        return static::create(
3756 2
            \array_combine($keys, $this->getArray()),
3757 2
            $this->iteratorClass,
3758 2
            false
3759
        );
3760
    }
3761
3762
    /**
3763
     * Create an array using the current array as keys and the other array as values.
3764
     *
3765
     * @param array $array <p>An array o values.</p>
3766
     *
3767
     * @return static
3768
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3769
     */
3770 2
    public function replaceAllValues(array $array): self
3771
    {
3772 2
        return static::create(
3773 2
            \array_combine($this->array, $array),
3774 2
            $this->iteratorClass,
3775 2
            false
3776
        );
3777
    }
3778
3779
    /**
3780
     * Replace the keys in an array with another set.
3781
     *
3782
     * @param array $keys <p>An array of keys matching the array's size</p>
3783
     *
3784
     * @return static
3785
     *                <p>(Immutable)</p>
3786
     */
3787 1
    public function replaceKeys(array $keys): self
3788
    {
3789 1
        $values = \array_values($this->getArray());
3790 1
        $result = \array_combine($keys, $values);
3791
3792 1
        return static::create(
3793 1
            $result,
3794 1
            $this->iteratorClass,
3795 1
            false
3796
        );
3797
    }
3798
3799
    /**
3800
     * Replace the first matched value in an array.
3801
     *
3802
     * @param mixed $search      <p>The value to replace.</p>
3803
     * @param mixed $replacement <p>The value to replace.</p>
3804
     *
3805
     * @return static
3806
     *                <p>(Immutable)</p>
3807
     */
3808 3
    public function replaceOneValue($search, $replacement = ''): self
3809
    {
3810 3
        $array = $this->getArray();
3811 3
        $key = \array_search($search, $array, true);
3812
3813 3
        if ($key !== false) {
3814 3
            $array[$key] = $replacement;
3815
        }
3816
3817 3
        return static::create(
3818 3
            $array,
3819 3
            $this->iteratorClass,
3820 3
            false
3821
        );
3822
    }
3823
3824
    /**
3825
     * Replace values in the current array.
3826
     *
3827
     * @param mixed $search      <p>The value to replace.</p>
3828
     * @param mixed $replacement <p>What to replace it with.</p>
3829
     *
3830
     * @return static
3831
     *                <p>(Immutable)</p>
3832
     */
3833 1
    public function replaceValues($search, $replacement = ''): self
3834
    {
3835 1
        return $this->each(
3836
            static function ($value) use ($search, $replacement) {
3837 1
                return \str_replace($search, $replacement, $value);
3838 1
            }
3839
        );
3840
    }
3841
3842
    /**
3843
     * Get the last elements from index $from until the end of this array.
3844
     *
3845
     * @param int $from
3846
     *
3847
     * @return static
3848
     *                <p>(Immutable)</p>
3849
     */
3850 15 View Code Duplication
    public function rest(int $from = 1): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3851
    {
3852 15
        $tmpArray = $this->getArray();
3853
3854 15
        return static::create(
3855 15
            \array_splice($tmpArray, $from),
3856 15
            $this->iteratorClass,
3857 15
            false
3858
        );
3859
    }
3860
3861
    /**
3862
     * @param int      $offset
3863
     * @param int|null $length
3864
     * @param array    $replacement
3865
     *
3866
     * @return static
3867
     *                <p>(Immutable)</p>
3868
     */
3869 1
    public function splice(int $offset, int $length = null, $replacement = []): self
3870
    {
3871 1
        $tmpArray = $this->getArray();
3872
3873 1
        \array_splice($tmpArray, $offset, $length ?? $this->count(), $replacement);
3874
3875 1
        return static::create(
3876 1
            $tmpArray,
3877 1
            $this->iteratorClass,
3878 1
            false
3879
        );
3880
    }
3881
3882
    /**
3883
     * Return the array in the reverse order.
3884
     *
3885
     * @return static
3886
     *                <p>(Mutable) Return this Arrayy object.</p>
3887
     */
3888 9
    public function reverse(): self
3889
    {
3890 9
        $this->generatorToArray();
3891
3892 9
        $this->array = \array_reverse($this->array);
3893
3894 9
        return $this;
3895
    }
3896
3897
    /**
3898
     * Sort an array in reverse order.
3899
     *
3900
     * @param int $sort_flags [optional] <p>
3901
     *                        You may modify the behavior of the sort using the optional
3902
     *                        parameter sort_flags, for details
3903
     *                        see sort.
3904
     *                        </p>
3905
     *
3906
     * @return static
3907
     *                <p>(Mutable) Return this Arrayy object.</p>
3908
     */
3909 4
    public function rsort(int $sort_flags = 0): self
3910
    {
3911 4
        $this->generatorToArray();
3912
3913 4
        \rsort($this->array, $sort_flags);
3914
3915 4
        return $this;
3916
    }
3917
3918
    /**
3919
     * Search for the first index of the current array via $value.
3920
     *
3921
     * @param mixed $value
3922
     *
3923
     * @return false|float|int|string
3924
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3925
     */
3926 21
    public function searchIndex($value)
3927
    {
3928 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3929 20
            if ($value === $valueFromArray) {
3930 20
                return $keyFromArray;
3931
            }
3932
        }
3933
3934 11
        return false;
3935
    }
3936
3937
    /**
3938
     * Search for the value of the current array via $index.
3939
     *
3940
     * @param mixed $index
3941
     *
3942
     * @return static
3943
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3944
     */
3945 9
    public function searchValue($index): self
3946
    {
3947 9
        $this->generatorToArray();
3948
3949
        // init
3950 9
        $return = [];
3951
3952 9
        if ($this->array === []) {
3953
            return static::create(
3954
                [],
3955
                $this->iteratorClass,
3956
                false
3957
            );
3958
        }
3959
3960
        // php cast "bool"-index into "int"-index
3961 9
        if ((bool) $index === $index) {
3962 1
            $index = (int) $index;
3963
        }
3964
3965 9
        if ($this->offsetExists($index)) {
3966 7
            $return = [$this->array[$index]];
3967
        }
3968
3969 9
        return static::create(
3970 9
            $return,
3971 9
            $this->iteratorClass,
3972 9
            false
3973
        );
3974
    }
3975
3976
    /**
3977
     * Set a value for the current array (optional using dot-notation).
3978
     *
3979
     * @param string $key   <p>The key to set.</p>
3980
     * @param mixed  $value <p>Its value.</p>
3981
     *
3982
     * @return static
3983
     *                <p>(Mutable)</p>
3984
     */
3985 18
    public function set($key, $value): self
3986
    {
3987 18
        $this->generatorToArray();
3988
3989 18
        $this->internalSet($key, $value);
3990
3991 18
        return $this;
3992
    }
3993
3994
    /**
3995
     * Get a value from a array and set it if it was not.
3996
     *
3997
     * WARNING: this method only set the value, if the $key is not already set
3998
     *
3999
     * @param mixed $key      <p>The key</p>
4000
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4001
     *
4002
     * @return mixed
4003
     *               <p>(Mutable)</p>
4004
     */
4005 11
    public function setAndGet($key, $fallback = null)
4006
    {
4007 11
        $this->generatorToArray();
4008
4009
        // If the key doesn't exist, set it.
4010 11
        if (!$this->has($key)) {
4011 4
            $this->array = $this->set($key, $fallback)->getArray();
4012
        }
4013
4014 11
        return $this->get($key);
4015
    }
4016
4017
    /**
4018
     * Shifts a specified value off the beginning of array.
4019
     *
4020
     * @return mixed
4021
     *               <p>(Mutable) A shifted element from the current array.</p>
4022
     */
4023 5
    public function shift()
4024
    {
4025 5
        $this->generatorToArray();
4026
4027 5
        return \array_shift($this->array);
4028
    }
4029
4030
    /**
4031
     * Shuffle the current array.
4032
     *
4033
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4034
     * @param array $array  [optional]
4035
     *
4036
     * @return static
4037
     *                <p>(Immutable)</p>
4038
     */
4039 2
    public function shuffle(bool $secure = false, array $array = null): self
4040
    {
4041 2
        if ($array === null) {
4042 2
            $array = $this->getArray();
4043
        }
4044
4045 2
        if ($secure !== true) {
4046
            /** @noinspection NonSecureShuffleUsageInspection */
4047 2
            \shuffle($array);
4048
        } else {
4049 1
            $size = \count($array, \COUNT_NORMAL);
4050 1
            $keys = \array_keys($array);
4051 1
            for ($i = $size - 1; $i > 0; --$i) {
4052
                try {
4053 1
                    $r = \random_int(0, $i);
4054
                } catch (\Exception $e) {
4055
                    /** @noinspection RandomApiMigrationInspection */
4056
                    $r = \mt_rand(0, $i);
4057
                }
4058 1
                if ($r !== $i) {
4059
                    $temp = $array[$keys[$r]];
4060
                    $array[$keys[$r]] = $array[$keys[$i]];
4061
                    $array[$keys[$i]] = $temp;
4062
                }
4063
            }
4064
4065
            // reset indices
4066 1
            $array = \array_values($array);
4067
        }
4068
4069 2
        foreach ($array as $key => $value) {
4070
            // check if recursive is needed
4071 2
            if (\is_array($value) === true) {
4072 2
                $array[$key] = $this->shuffle($secure, $value);
4073
            }
4074
        }
4075
4076 2
        return static::create(
4077 2
            $array,
4078 2
            $this->iteratorClass,
4079 2
            false
4080
        );
4081
    }
4082
4083
    /**
4084
     * Count the values from the current array.
4085
     *
4086
     * alias: for "Arrayy->count()"
4087
     *
4088
     * @param int $mode
4089
     *
4090
     * @return int
4091
     */
4092 20
    public function size(int $mode = \COUNT_NORMAL): int
4093
    {
4094 20
        return $this->count($mode);
4095
    }
4096
4097
    /**
4098
     * Checks whether array has exactly $size items.
4099
     *
4100
     * @param int $size
4101
     *
4102
     * @return bool
4103
     */
4104 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4105
    {
4106
        // init
4107 1
        $itemsTempCount = 0;
4108
4109 1
        foreach ($this->getGenerator() as $key => $value) {
4110 1
            ++$itemsTempCount;
4111 1
            if ($itemsTempCount > $size) {
4112 1
                return false;
4113
            }
4114
        }
4115
4116 1
        return $itemsTempCount === $size;
4117
    }
4118
4119
    /**
4120
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4121
     * smaller than $fromSize.
4122
     *
4123
     * @param int $fromSize
4124
     * @param int $toSize
4125
     *
4126
     * @return bool
4127
     */
4128 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4129
    {
4130 1
        if ($fromSize > $toSize) {
4131 1
            $tmp = $toSize;
4132 1
            $toSize = $fromSize;
4133 1
            $fromSize = $tmp;
4134
        }
4135
4136
        // init
4137 1
        $itemsTempCount = 0;
4138
4139 1
        foreach ($this->getGenerator() as $key => $value) {
4140 1
            ++$itemsTempCount;
4141 1
            if ($itemsTempCount > $toSize) {
4142 1
                return false;
4143
            }
4144
        }
4145
4146 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4147
    }
4148
4149
    /**
4150
     * Checks whether array has more than $size items.
4151
     *
4152
     * @param int $size
4153
     *
4154
     * @return bool
4155
     */
4156 1 View Code Duplication
    public function sizeIsGreaterThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4157
    {
4158
        // init
4159 1
        $itemsTempCount = 0;
4160
4161 1
        foreach ($this->getGenerator() as $key => $value) {
4162 1
            ++$itemsTempCount;
4163 1
            if ($itemsTempCount > $size) {
4164 1
                return true;
4165
            }
4166
        }
4167
4168 1
        return $itemsTempCount > $size;
4169
    }
4170
4171
    /**
4172
     * Checks whether array has less than $size items.
4173
     *
4174
     * @param int $size
4175
     *
4176
     * @return bool
4177
     */
4178 1 View Code Duplication
    public function sizeIsLessThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4179
    {
4180
        // init
4181 1
        $itemsTempCount = 0;
4182
4183 1
        foreach ($this->getGenerator() as $key => $value) {
4184 1
            ++$itemsTempCount;
4185 1
            if ($itemsTempCount > $size) {
4186 1
                return false;
4187
            }
4188
        }
4189
4190 1
        return $itemsTempCount < $size;
4191
    }
4192
4193
    /**
4194
     * Counts all elements in an array, or something in an object.
4195
     *
4196
     * <p>
4197
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4198
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4199
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4200
     * implemented and used in PHP.
4201
     * </p>
4202
     *
4203
     * @return int
4204
     *             <p>
4205
     *             The number of elements in var, which is
4206
     *             typically an array, since anything else will have one
4207
     *             element.
4208
     *             </p>
4209
     *             <p>
4210
     *             If var is not an array or an object with
4211
     *             implemented Countable interface,
4212
     *             1 will be returned.
4213
     *             There is one exception, if var is &null;,
4214
     *             0 will be returned.
4215
     *             </p>
4216
     *             <p>
4217
     *             Caution: count may return 0 for a variable that isn't set,
4218
     *             but it may also return 0 for a variable that has been initialized with an
4219
     *             empty array. Use isset to test if a variable is set.
4220
     *             </p>
4221
     */
4222 10
    public function sizeRecursive(): int
4223
    {
4224 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4225
    }
4226
4227
    /**
4228
     * Extract a slice of the array.
4229
     *
4230
     * @param int      $offset       <p>Slice begin index.</p>
4231
     * @param int|null $length       <p>Length of the slice.</p>
4232
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4233
     *
4234
     * @return static
4235
     *                <p>A slice of the original array with length $length.</p>
4236
     */
4237 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4238
    {
4239 5
        return static::create(
4240 5
            \array_slice(
4241 5
                $this->getArray(),
4242 5
                $offset,
4243 5
                $length,
4244 5
                $preserveKeys
4245
            ),
4246 5
            $this->iteratorClass,
4247 5
            false
4248
        );
4249
    }
4250
4251
    /**
4252
     * Sort the current array and optional you can keep the keys.
4253
     *
4254
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4255
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4256
     *                              <strong>SORT_NATURAL</strong></p>
4257
     * @param bool       $keepKeys
4258
     *
4259
     * @return static
4260
     *                <p>(Mutable) Return this Arrayy object.</p>
4261
     */
4262 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4263
    {
4264 20
        $this->generatorToArray();
4265
4266 20
        return $this->sorting(
4267 20
            $this->array,
4268 20
            $direction,
4269 20
            $strategy,
4270 20
            $keepKeys
4271
        );
4272
    }
4273
4274
    /**
4275
     * Sort the current array by key.
4276
     *
4277
     * @see http://php.net/manual/en/function.ksort.php
4278
     * @see http://php.net/manual/en/function.krsort.php
4279
     *
4280
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4281
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4282
     *                              <strong>SORT_NATURAL</strong></p>
4283
     *
4284
     * @return static
4285
     *                <p>(Mutable) Return this Arrayy object.</p>
4286
     */
4287 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4288
    {
4289 18
        $this->generatorToArray();
4290
4291 18
        $this->sorterKeys($this->array, $direction, $strategy);
4292
4293 18
        return $this;
4294
    }
4295
4296
    /**
4297
     * Sort the current array by value.
4298
     *
4299
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4300
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4301
     *                              <strong>SORT_NATURAL</strong></p>
4302
     *
4303
     * @return static
4304
     *                <p>(Mutable)</p>
4305
     */
4306 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4307
    {
4308 1
        return $this->sort($direction, $strategy, true);
4309
    }
4310
4311
    /**
4312
     * Sort the current array by value.
4313
     *
4314
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4315
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4316
     *                              <strong>SORT_NATURAL</strong></p>
4317
     *
4318
     * @return static
4319
     *                <p>(Mutable)</p>
4320
     */
4321 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4322
    {
4323 1
        return $this->sort($direction, $strategy, false);
4324
    }
4325
4326
    /**
4327
     * Sort a array by value, by a closure or by a property.
4328
     *
4329
     * - If the sorter is null, the array is sorted naturally.
4330
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4331
     *
4332
     * @param callable|string|null $sorter
4333
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4334
     *                                        <strong>SORT_DESC</strong></p>
4335
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4336
     *                                        <strong>SORT_NATURAL</strong></p>
4337
     *
4338
     * @return static
4339
     *                <p>(Immutable)</p>
4340
     */
4341 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4342
    {
4343 1
        $array = $this->getArray();
4344 1
        $direction = $this->getDirection($direction);
4345
4346
        // Transform all values into their results.
4347 1
        if ($sorter) {
4348 1
            $arrayy = static::create(
4349 1
                $array,
4350 1
                $this->iteratorClass,
4351 1
                false
4352
            );
4353
4354 1
            $results = $arrayy->each(
4355
                function ($value) use ($sorter) {
4356 1
                    if (\is_callable($sorter) === true) {
4357 1
                        return $sorter($value);
4358
                    }
4359
4360 1
                    return $this->get($sorter);
4361 1
                }
4362
            );
4363
4364 1
            $results = $results->getArray();
4365
        } else {
4366 1
            $results = $array;
4367
        }
4368
4369
        // Sort by the results and replace by original values
4370 1
        \array_multisort($results, $direction, $strategy, $array);
4371
4372 1
        return static::create(
4373 1
            $array,
4374 1
            $this->iteratorClass,
4375 1
            false
4376
        );
4377
    }
4378
4379
    /**
4380
     * Split an array in the given amount of pieces.
4381
     *
4382
     * @param int  $numberOfPieces
4383
     * @param bool $keepKeys
4384
     *
4385
     * @return static
4386
     *                <p>(Immutable)</p>
4387
     */
4388 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4389
    {
4390 1
        $this->generatorToArray();
4391
4392 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4393
4394 1
        if ($arrayCount === 0) {
4395 1
            $result = [];
4396
        } else {
4397 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4398 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4399
        }
4400
4401 1
        return static::create(
4402 1
            $result,
4403 1
            $this->iteratorClass,
4404 1
            false
4405
        );
4406
    }
4407
4408
    /**
4409
     * Stripe all empty items.
4410
     *
4411
     * @return static
4412
     *                <p>(Immutable)</p>
4413
     */
4414 1
    public function stripEmpty(): self
4415
    {
4416 1
        return $this->filter(
4417
            static function ($item) {
4418 1
                if ($item === null) {
4419 1
                    return false;
4420
                }
4421
4422 1
                return (bool) \trim((string) $item);
4423 1
            }
4424
        );
4425
    }
4426
4427
    /**
4428
     * Swap two values between positions by key.
4429
     *
4430
     * @param int|string $swapA <p>a key in the array</p>
4431
     * @param int|string $swapB <p>a key in the array</p>
4432
     *
4433
     * @return static
4434
     *                <p>(Immutable)</p>
4435
     */
4436 1
    public function swap($swapA, $swapB): self
4437
    {
4438 1
        $array = $this->getArray();
4439
4440 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4441
4442 1
        return static::create(
4443 1
            $array,
4444 1
            $this->iteratorClass,
4445 1
            false
4446
        );
4447
    }
4448
4449
    /**
4450
     * alias: for "Arrayy->getArray()"
4451
     *
4452
     * @see Arrayy::getArray()
4453
     *
4454
     * @param bool $convertAllArrayyElements
4455
     */
4456 236
    public function toArray(bool $convertAllArrayyElements = false)
4457
    {
4458 236
        return $this->getArray($convertAllArrayyElements);
4459
    }
4460
4461
    /**
4462
     * Convert the current array to JSON.
4463
     *
4464
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4465
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4466
     *
4467
     * @return string
4468
     */
4469 7
    public function toJson(int $options = 0, int $depth = 512): string
4470
    {
4471 7
        $return = \json_encode($this->getArray(), $options, $depth);
4472 7
        if ($return === false) {
4473
            return '';
4474
        }
4475
4476 7
        return $return;
4477
    }
4478
4479
    /**
4480
     * Implodes array to a string with specified separator.
4481
     *
4482
     * @param string $separator [optional] <p>The element's separator.</p>
4483
     *
4484
     * @return string
4485
     *                <p>The string representation of array, separated by ",".</p>
4486
     */
4487 19
    public function toString(string $separator = ','): string
4488
    {
4489 19
        return $this->implode($separator);
4490
    }
4491
4492
    /**
4493
     * Return a duplicate free copy of the current array.
4494
     *
4495
     * @return static
4496
     *                <p>(Mutable)</p>
4497
     */
4498 13
    public function unique(): self
4499
    {
4500
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4501
4502 13
        $this->array = $this->reduce(
4503
            static function ($resultArray, $value) {
4504 12
                if (!\in_array($value, $resultArray, true)) {
4505 12
                    $resultArray[] = $value;
4506
                }
4507
4508 12
                return $resultArray;
4509 13
            },
4510 13
            []
4511 13
        )->getArray();
4512 13
        $this->generator = null;
4513
4514 13
        return $this;
4515
    }
4516
4517
    /**
4518
     * Return a duplicate free copy of the current array. (with the old keys)
4519
     *
4520
     * @return static
4521
     *                <p>(Mutable)</p>
4522
     */
4523 11
    public function uniqueKeepIndex(): self
4524
    {
4525
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4526
4527
        // init
4528 11
        $array = $this->getArray();
4529
4530 11
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce(\array_key...esultArray; }, array()) of type * is incompatible with the declared type array of property $array.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4531 11
            \array_keys($array),
4532
            static function ($resultArray, $key) use ($array) {
4533 10
                if (!\in_array($array[$key], $resultArray, true)) {
4534 10
                    $resultArray[$key] = $array[$key];
4535
                }
4536
4537 10
                return $resultArray;
4538 11
            },
4539 11
            []
4540
        );
4541 11
        $this->generator = null;
4542
4543 11
        if ($this->array === null) {
4544
            $this->array = [];
4545
        } else {
4546 11
            $this->array = (array) $this->array;
4547
        }
4548
4549 11
        return $this;
4550
    }
4551
4552
    /**
4553
     * alias: for "Arrayy->unique()"
4554
     *
4555
     * @return static
4556
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4557
     *
4558
     * @see Arrayy::unique()
4559
     */
4560 10
    public function uniqueNewIndex(): self
4561
    {
4562 10
        return $this->unique();
4563
    }
4564
4565
    /**
4566
     * Prepends one or more values to the beginning of array at once.
4567
     *
4568
     * @param array ...$args
4569
     *
4570
     * @return static
4571
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4572
     */
4573 4
    public function unshift(...$args): self
4574
    {
4575 4
        $this->generatorToArray();
4576
4577 4
        \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4578
4579 4
        return $this;
4580
    }
4581
4582
    /**
4583
     * Get all values from a array.
4584
     *
4585
     * @return static
4586
     *                <p>(Immutable)</p>
4587
     */
4588 2
    public function values(): self
4589
    {
4590 2
        return static::create(
4591
            function () {
4592
                /** @noinspection YieldFromCanBeUsedInspection */
4593 2
                foreach ($this->getGenerator() as $value) {
4594 2
                    yield $value;
4595
                }
4596 2
            },
4597 2
            $this->iteratorClass,
4598 2
            false
4599
        );
4600
    }
4601
4602
    /**
4603
     * Apply the given function to every element in the array, discarding the results.
4604
     *
4605
     * @param callable $callable
4606
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4607
     *
4608
     * @return static
4609
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4610
     */
4611 12
    public function walk($callable, bool $recursive = false): self
4612
    {
4613 12
        $this->generatorToArray();
4614
4615 12
        if ($recursive === true) {
4616 6
            \array_walk_recursive($this->array, $callable);
4617
        } else {
4618 6
            \array_walk($this->array, $callable);
4619
        }
4620
4621 12
        return $this;
4622
    }
4623
4624
    /**
4625
     * @param array $data
4626
     * @param bool  $checkPropertiesInConstructor
4627
     */
4628 1048
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
4629
    {
4630 1048
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
4631
                                        &&
4632 1048
                                        $checkPropertiesInConstructor === true;
4633
4634 1048
        if ($this->properties !== []) {
4635 73
            foreach ($data as $key => &$valueInner) {
4636 73
                $this->internalSet(
4637 73
                    $key,
4638 73
                    $valueInner,
4639 73
                    $checkPropertiesInConstructor
4640
                );
4641
            }
4642
        } else {
4643
            if (
4644 985
                $this->checkPropertyTypes === true
4645
                ||
4646 985
                $checkPropertiesInConstructor === true
4647
            ) {
4648 16
                $this->properties = $this->getPropertiesFromPhpDoc();
4649
            }
4650
4651
            if (
4652 985
                $this->checkPropertiesMismatchInConstructor === true
4653
                &&
4654 985
                \count($data) !== 0
4655
                &&
4656 985
                \count(\array_diff_key($this->properties, $data)) > 0
4657
            ) {
4658 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...his->properties), true).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4659
            }
4660
4661 984
            foreach ($data as $key => &$valueInner) {
4662 854
                $this->internalSet(
4663 854
                    $key,
4664 854
                    $valueInner,
4665 854
                    $checkPropertiesInConstructor
4666
                );
4667
            }
4668
        }
4669 1041
    }
4670
4671
    /**
4672
     * Convert an array into a object.
4673
     *
4674
     * @param array $array PHP array
4675
     *
4676
     * @return \stdClass
4677
     */
4678 4
    protected static function arrayToObject(array $array = []): \stdClass
4679
    {
4680
        // init
4681 4
        $object = new \stdClass();
4682
4683 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4684 1
            return $object;
4685
        }
4686
4687 3
        foreach ($array as $name => $value) {
4688 3
            if (\is_array($value) === true) {
4689 1
                $object->{$name} = self::arrayToObject($value);
4690
            } else {
4691 3
                $object->{$name} = $value;
4692
            }
4693
        }
4694
4695 3
        return $object;
4696
    }
4697
4698
    /**
4699
     * @param array|\Generator|null $input         <p>
4700
     *                                             An array containing keys to return.
4701
     *                                             </p>
4702
     * @param mixed|null            $search_values [optional] <p>
4703
     *                                             If specified, then only keys containing these values are returned.
4704
     *                                             </p>
4705
     * @param bool                  $strict        [optional] <p>
4706
     *                                             Determines if strict comparison (===) should be used during the
4707
     *                                             search.
4708
     *                                             </p>
4709
     *
4710
     * @return array
4711
     *               <p>an array of all the keys in input</p>
4712
     */
4713 11
    protected function array_keys_recursive(
4714
        $input = null,
4715
        $search_values = null,
4716
        bool $strict = true
4717
    ): array {
4718
        // init
4719 11
        $keys = [];
4720 11
        $keysTmp = [];
4721
4722 11
        if ($input === null) {
4723 4
            $input = $this->getGenerator();
4724
        }
4725
4726 11
        if ($search_values === null) {
4727 11
            foreach ($input as $key => $value) {
4728 11
                $keys[] = $key;
4729
4730
                // check if recursive is needed
4731 11
                if (\is_array($value) === true) {
4732 11
                    $keysTmp[] = $this->array_keys_recursive($value);
4733
                }
4734
            }
4735
        } else {
4736 1
            $is_array_tmp = \is_array($search_values);
4737
4738 1
            foreach ($input as $key => $value) {
4739 View Code Duplication
                if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4740
                    (
4741 1
                        $is_array_tmp === false
4742
                        &&
4743 1
                        $strict === true
4744
                        &&
4745 1
                        $search_values === $value
4746
                    )
4747
                    ||
4748
                    (
4749 1
                        $is_array_tmp === false
4750
                        &&
4751 1
                        $strict === false
4752
                        &&
4753 1
                        $search_values == $value
4754
                    )
4755
                    ||
4756
                    (
4757 1
                        $is_array_tmp === true
4758
                        &&
4759 1
                        \in_array($value, $search_values, $strict)
4760
                    )
4761
                ) {
4762 1
                    $keys[] = $key;
4763
                }
4764
4765
                // check if recursive is needed
4766 1
                if (\is_array($value) === true) {
4767 1
                    $keysTmp[] = $this->array_keys_recursive($value);
4768
                }
4769
            }
4770
        }
4771
4772 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
4773
    }
4774
4775
    /**
4776
     * @param mixed      $path
4777
     * @param callable   $callable
4778
     * @param array|null $currentOffset
4779
     */
4780 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4781
    {
4782 4
        $this->generatorToArray();
4783
4784 4
        if ($currentOffset === null) {
4785 4
            $currentOffset = &$this->array;
4786
        }
4787
4788 4
        $explodedPath = \explode($this->pathSeparator, $path);
4789 4
        if ($explodedPath === false) {
4790
            return;
4791
        }
4792
4793 4
        $nextPath = \array_shift($explodedPath);
4794
4795 4
        if (!isset($currentOffset[$nextPath])) {
4796
            return;
4797
        }
4798
4799 4
        if (!empty($explodedPath)) {
4800 1
            $this->callAtPath(
4801 1
                \implode($this->pathSeparator, $explodedPath),
4802 1
                $callable,
4803 1
                $currentOffset[$nextPath]
4804
            );
4805
        } else {
4806 4
            $callable($currentOffset[$nextPath]);
4807
        }
4808 4
    }
4809
4810
    /**
4811
     * create a fallback for array
4812
     *
4813
     * 1. use the current array, if it's a array
4814
     * 2. fallback to empty array, if there is nothing
4815
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4816
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4817
     * 5. call "__toArray()" on object, if the method exists
4818
     * 6. cast a string or object with "__toString()" into an array
4819
     * 7. throw a "InvalidArgumentException"-Exception
4820
     *
4821
     * @param mixed $data
4822
     *
4823
     * @throws \InvalidArgumentException
4824
     *
4825
     * @return array
4826
     */
4827 1050
    protected function fallbackForArray(&$data): array
4828
    {
4829 1050
        $data = $this->internalGetArray($data);
4830
4831 1050
        if ($data === null) {
4832 2
            throw new \InvalidArgumentException('Passed value should be a array');
4833
        }
4834
4835 1048
        return $data;
4836
    }
4837
4838
    /**
4839
     * Get correct PHP constant for direction.
4840
     *
4841
     * @param int|string $direction
4842
     *
4843
     * @return int
4844
     */
4845 39
    protected function getDirection($direction): int
4846
    {
4847 39
        if ((string) $direction === $direction) {
4848 10
            $direction = \strtolower($direction);
4849
4850 10
            if ($direction === 'desc') {
4851 2
                $direction = \SORT_DESC;
4852
            } else {
4853 8
                $direction = \SORT_ASC;
4854
            }
4855
        }
4856
4857
        if (
4858 39
            $direction !== \SORT_DESC
4859
            &&
4860 39
            $direction !== \SORT_ASC
4861
        ) {
4862
            $direction = \SORT_ASC;
4863
        }
4864
4865 39
        return $direction;
4866
    }
4867
4868
    /**
4869
     * @param mixed $glue
4870
     * @param mixed $pieces
4871
     * @param bool  $useKeys
4872
     *
4873
     * @return string
4874
     */
4875 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4876
    {
4877 36
        if ($pieces instanceof self) {
4878 1
            $pieces = $pieces->getArray();
4879
        }
4880
4881 36
        if (\is_array($pieces) === true) {
4882 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4883 36
            $pieces_count_not_zero = $pieces_count > 0;
4884
4885 36
            return \implode(
4886 36
                $glue,
4887 36
                \array_map(
4888 36
                    [$this, 'implode_recursive'],
4889 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4890 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4891
                )
4892
            );
4893
        }
4894
4895
        if (
4896 36
            \is_scalar($pieces) === true
4897
            ||
4898 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4899
        ) {
4900 32
            return (string) $pieces;
4901
        }
4902
4903 8
        return '';
4904
    }
4905
4906
    /**
4907
     * @param mixed                 $needle   <p>
4908
     *                                        The searched value.
4909
     *                                        </p>
4910
     *                                        <p>
4911
     *                                        If needle is a string, the comparison is done
4912
     *                                        in a case-sensitive manner.
4913
     *                                        </p>
4914
     * @param array|\Generator|null $haystack <p>
4915
     *                                        The array.
4916
     *                                        </p>
4917
     * @param bool                  $strict   [optional] <p>
4918
     *                                        If the third parameter strict is set to true
4919
     *                                        then the in_array function will also check the
4920
     *                                        types of the
4921
     *                                        needle in the haystack.
4922
     *                                        </p>
4923
     *
4924
     * @return bool
4925
     *              <p>true if needle is found in the array, false otherwise</p>
4926
     */
4927 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4928
    {
4929 19
        if ($haystack === null) {
4930
            $haystack = $this->getGenerator();
4931
        }
4932
4933 19
        foreach ($haystack as $item) {
4934 15
            if (\is_array($item) === true) {
4935 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4936
            } else {
4937
                /** @noinspection NestedPositiveIfStatementsInspection */
4938 15
                if ($strict === true) {
4939 15
                    $returnTmp = $item === $needle;
4940
                } else {
4941
                    $returnTmp = $item == $needle;
4942
                }
4943
            }
4944
4945 15
            if ($returnTmp === true) {
4946 15
                return true;
4947
            }
4948
        }
4949
4950 8
        return false;
4951
    }
4952
4953
    /**
4954
     * @param mixed $data
4955
     *
4956
     * @return array|null
4957
     */
4958 1050
    protected function internalGetArray(&$data)
4959
    {
4960 1050
        if (\is_array($data) === true) {
4961 1047
            return $data;
4962
        }
4963
4964 51
        if (!$data) {
4965 6
            return [];
4966
        }
4967
4968 50
        if (\is_object($data) === true) {
4969 45
            if ($data instanceof \ArrayObject) {
4970 4
                return $data->getArrayCopy();
4971
            }
4972
4973 42
            if ($data instanceof \Generator) {
4974
                return static::createFromGeneratorImmutable($data)->getArray();
4975
            }
4976
4977 42
            if ($data instanceof \Traversable) {
4978
                return static::createFromObject($data)->getArray();
4979
            }
4980
4981 42
            if ($data instanceof \JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
4982
                return (array) $data->jsonSerialize();
4983
            }
4984
4985 42
            if (\method_exists($data, '__toArray')) {
4986
                return (array) $data->__toArray();
4987
            }
4988
4989 42
            if (\method_exists($data, '__toString')) {
4990
                return [(string) $data];
4991
            }
4992
        }
4993
4994 47
        if (\is_callable($data)) {
4995 40
            $this->generator = new ArrayyRewindableGenerator($data);
4996
4997 40
            return [];
4998
        }
4999
5000 9
        if (\is_scalar($data)) {
5001 7
            return [$data];
5002
        }
5003
5004 2
        return null;
5005
    }
5006
5007
    /**
5008
     * Internal mechanics of remove method.
5009
     *
5010
     * @param mixed $key
5011
     *
5012
     * @return bool
5013
     */
5014 18
    protected function internalRemove($key): bool
5015
    {
5016 18
        $this->generatorToArray();
5017
5018
        if (
5019 18
            $this->pathSeparator
5020
            &&
5021 18
            (string) $key === $key
5022
            &&
5023 18
            \strpos($key, $this->pathSeparator) !== false
5024
        ) {
5025
            $path = \explode($this->pathSeparator, (string) $key);
5026
5027
            if ($path !== false) {
5028
                // crawl though the keys
5029
                while (\count($path, \COUNT_NORMAL) > 1) {
5030
                    $key = \array_shift($path);
5031
5032
                    if (!$this->has($key)) {
5033
                        return false;
5034
                    }
5035
5036
                    $this->array = &$this->array[$key];
5037
                }
5038
5039
                $key = \array_shift($path);
5040
            }
5041
        }
5042
5043 18
        unset($this->array[$key]);
5044
5045 18
        return true;
5046
    }
5047
5048
    /**
5049
     * Internal mechanic of set method.
5050
     *
5051
     * @param int|string|null $key
5052
     * @param mixed           $value
5053
     * @param bool            $checkProperties
5054
     *
5055
     * @return bool
5056
     */
5057 924
    protected function internalSet($key, &$value, $checkProperties = true): bool
5058
    {
5059
        if (
5060 924
            $checkProperties === true
5061
            &&
5062 924
            $this->properties !== []
5063
        ) {
5064 82
            $this->checkType($key, $value);
5065
        }
5066
5067 922
        if ($key === null) {
5068
            return false;
5069
        }
5070
5071 922
        $this->generatorToArray();
5072
5073 922
        $array = &$this->array;
5074
5075
        if (
5076 922
            $this->pathSeparator
5077
            &&
5078 922
            (string) $key === $key
5079
            &&
5080 922
            \strpos($key, $this->pathSeparator) !== false
5081
        ) {
5082 3
            $path = \explode($this->pathSeparator, (string) $key);
5083
5084 3
            if ($path !== false) {
5085
                // crawl through the keys
5086 3
                while (\count($path, \COUNT_NORMAL) > 1) {
5087 3
                    $key = \array_shift($path);
5088
5089 3
                    $array = &$array[$key];
5090
                }
5091
5092 3
                $key = \array_shift($path);
5093
            }
5094
        }
5095
5096 922
        $array[$key] = $value;
5097
5098 922
        return true;
5099
    }
5100
5101
    /**
5102
     * Convert a object into an array.
5103
     *
5104
     * @param object $object
5105
     *
5106
     * @return mixed
5107
     */
5108 5
    protected static function objectToArray($object)
5109
    {
5110 5
        if (!\is_object($object)) {
5111 4
            return $object;
5112
        }
5113
5114 5
        if (\is_object($object)) {
5115 5
            $object = \get_object_vars($object);
5116
        }
5117
5118 5
        return \array_map(['static', 'objectToArray'], $object);
5119
    }
5120
5121
    /**
5122
     * sorting keys
5123
     *
5124
     * @param array      $elements
5125
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5126
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5127
     *                              <strong>SORT_NATURAL</strong></p>
5128
     *
5129
     * @return static
5130
     *                <p>(Mutable) Return this Arrayy object.</p>
5131
     */
5132 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5133
    {
5134 18
        $direction = $this->getDirection($direction);
5135
5136
        switch ($direction) {
5137 18
            case 'desc':
5138 18
            case \SORT_DESC:
5139 6
                \krsort($elements, $strategy);
5140
5141 6
                break;
5142 13
            case 'asc':
5143 13
            case \SORT_ASC:
5144
            default:
5145 13
                \ksort($elements, $strategy);
5146
        }
5147
5148 18
        return $this;
5149
    }
5150
5151
    /**
5152
     * @param array      $elements  <p>Warning: used as reference</p>
5153
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5154
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5155
     *                              <strong>SORT_NATURAL</strong></p>
5156
     * @param bool       $keepKeys
5157
     *
5158
     * @return static
5159
     *                <p>(Mutable) Return this Arrayy object.</p>
5160
     */
5161 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5162
    {
5163 20
        $direction = $this->getDirection($direction);
5164
5165 20
        if (!$strategy) {
5166 20
            $strategy = \SORT_REGULAR;
5167
        }
5168
5169
        switch ($direction) {
5170 20
            case 'desc':
5171 20
            case \SORT_DESC:
5172 9
                if ($keepKeys) {
5173 5
                    \arsort($elements, $strategy);
5174
                } else {
5175 4
                    \rsort($elements, $strategy);
5176
                }
5177
5178 9
                break;
5179 11
            case 'asc':
5180 11
            case \SORT_ASC:
5181
            default:
5182 11
                if ($keepKeys) {
5183 4
                    \asort($elements, $strategy);
5184
                } else {
5185 7
                    \sort($elements, $strategy);
5186
                }
5187
        }
5188
5189 20
        return $this;
5190
    }
5191
5192
    /**
5193
     * @return TypeCheckPhpDoc[]
5194
     *
5195
     * @noinspection ReturnTypeCanBeDeclaredInspection
5196
     */
5197 16
    protected function getPropertiesFromPhpDoc()
5198
    {
5199 16
        static $PROPERTY_CACHE = [];
5200 16
        $cacheKey = 'Class::' . static::class;
5201
5202 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5203 15
            return $PROPERTY_CACHE[$cacheKey];
5204
        }
5205
5206
        // init
5207 2
        $properties = [];
5208
5209 2
        $reflector = new \ReflectionClass($this);
5210 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5211 2
        $docComment = $reflector->getDocComment();
5212 2
        if ($docComment) {
5213 2
            $docblock = $factory->create($docComment);
5214
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5215 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5216 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
5217
            }
5218
        }
5219
5220 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5221
    }
5222
5223
    /**
5224
     * @return bool
5225
     *
5226
     * @noinspection ReturnTypeCanBeDeclaredInspection
5227
     */
5228 967
    protected function generatorToArray()
5229
    {
5230 967
        if ($this->generator) {
5231 2
            $this->array = $this->getArray();
5232 2
            $this->generator = null;
5233
5234 2
            return true;
5235
        }
5236
5237 967
        return false;
5238
    }
5239
5240
    /**
5241
     * @param int|string|null $key
5242
     * @param mixed           $value
5243
     */
5244 82
    private function checkType($key, $value)
5245
    {
5246
        if (
5247 82
            isset($this->properties[$key]) === false
5248
            &&
5249 82
            $this->checkPropertiesMismatch === true
5250
        ) {
5251
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \get_class($this) . ').');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The key ' . $key . ' do...get_class($this) . ').'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5252
        }
5253
5254 82
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
5255 71
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
5256 17
        } elseif (isset($this->properties[$key])) {
5257 17
            $this->properties[$key]->checkType($value);
5258
        }
5259 80
    }
5260
}
5261