Completed
Pull Request — master (#36)
by Jacob
01:29
created

Arrayy::shuffle()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9.1365

Importance

Changes 0
Metric Value
cc 8
nc 12
nop 2
dl 0
loc 43
ccs 17
cts 23
cp 0.7391
crap 9.1365
rs 7.9875
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\TypeCheck\TypeCheckArray;
11
use Arrayy\TypeCheck\TypeCheckInterface;
12
use Arrayy\TypeCheck\TypeCheckPhpDoc;
13
14
/**
15
 * Methods to manage arrays.
16
 *
17
 * For the full copyright and license information, please view the LICENSE
18
 * file that was distributed with this source code.
19
 *
20
 * @template TKey of array-key
21
 * @template T
22
 * @template-extends \ArrayObject<TKey,T>
23
 * @template-implements \IteratorAggregate<TKey,T>
24
 * @template-implements \ArrayAccess<TKey|null,T>
25
 */
26
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
27
{
28
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
29
30
    /**
31
     * @var array
32
     *
33
     * @psalm-var array<TKey,T>
34
     */
35
    protected $array = [];
36
37
    /**
38
     * @var \Arrayy\ArrayyRewindableGenerator|null
39
     *
40
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
41
     */
42
    protected $generator;
43
44
    /**
45
     * @var string
46
     *
47
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
48
     */
49
    protected $iteratorClass = ArrayyIterator::class;
50
51
    /**
52
     * @var string
53
     */
54
    protected $pathSeparator = '.';
55
56
    /**
57
     * @var bool
58
     */
59
    protected $checkPropertyTypes = false;
60
61
    /**
62
     * @var bool
63
     */
64
    protected $checkForMissingPropertiesInConstructor = false;
65
66
    /**
67
     * @var bool
68
     */
69
    protected $checkPropertiesMismatchInConstructor = false;
70
71
    /**
72
     * @var bool
73
     */
74
    protected $checkPropertiesMismatch = true;
75
76
    /**
77
     * @var array<TypeCheckInterface>|\Arrayy\Type\TypeInterface|TypeCheckArray<TypeCheckInterface>
78
     */
79
    protected $properties = [];
80
81
    /**
82
     * Initializes
83
     *
84
     * @param mixed  $data                         <p>
85
     *                                             Should be an array or a generator, otherwise it will try
86
     *                                             to convert it into an array.
87
     *                                             </p>
88
     * @param string $iteratorClass                optional <p>
89
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
90
     *                                             need this option.
91
     *                                             </p>
92
     * @param bool   $checkPropertiesInConstructor optional <p>
93
     *                                             You need to extend the "Arrayy"-class and you need to set
94
     *                                             the $checkPropertiesMismatchInConstructor class property
95
     *                                             to
96
     *                                             true, otherwise this option didn't not work anyway.
97
     *                                             </p>
98
     *
99
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
100
     */
101 1066
    public function __construct(
102
        $data = [],
103
        string $iteratorClass = ArrayyIterator::class,
104
        bool $checkPropertiesInConstructor = true
105
    ) {
106 1066
        $data = $this->fallbackForArray($data);
107
108
        // used only for serialize + unserialize, all other methods are overwritten
109 1064
        parent::__construct([], 0, $iteratorClass);
110
111 1064
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
112
113 1057
        $this->setIteratorClass($iteratorClass);
114 1057
    }
115
116
    /**
117
     * Call object as function.
118
     *
119
     * @param mixed $key
120
     *
121
     * @return mixed
122
     */
123 1
    public function __invoke($key = null)
124
    {
125 1
        if ($key !== null) {
126 1
            $this->generatorToArray();
127
128 1
            return $this->array[$key] ?? false;
129
        }
130
131
        return $this->getArray();
132
    }
133
134
    /**
135
     * Whether or not an element exists by key.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return bool
140
     *              <p>True is the key/index exists, otherwise false.</p>
141
     */
142
    public function __isset($key): bool
143
    {
144
        return $this->offsetExists($key);
145
    }
146
147
    /**
148
     * Assigns a value to the specified element.
149
     *
150
     * @param mixed $key
151
     * @param mixed $value
152
     *
153
     * @return void
154
     */
155 2
    public function __set($key, $value)
156
    {
157 2
        $this->internalSet($key, $value);
158 2
    }
159
160
    /**
161
     * magic to string
162
     *
163
     * @return string
164
     */
165 15
    public function __toString(): string
166
    {
167 15
        return $this->toString();
168
    }
169
170
    /**
171
     * Unset element by key.
172
     *
173
     * @param mixed $key
174
     */
175
    public function __unset($key)
176
    {
177
        $this->internalRemove($key);
178
    }
179
180
    /**
181
     * Get a value by key.
182
     *
183
     * @param mixed $key
184
     *
185
     * @return mixed
186
     *               <p>Get a Value from the current array.</p>
187
     */
188 4
    public function &__get($key)
189
    {
190 4
        $return = $this->get($key);
191
192 4
        if (\is_array($return) === true) {
193
            return static::create($return, $this->iteratorClass, false);
194
        }
195
196 4
        return $return;
197
    }
198
199
    /**
200
     * alias: for "Arrayy->append()"
201
     *
202
     * @param mixed $value
203
     *
204
     * @return static
205
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
206
     *
207
     * @see          Arrayy::append()
208
     *
209
     * @psalm-param  T $value
210
     * @psalm-return static<TKey,T>
211
     */
212 3
    public function add($value)
213
    {
214 3
        return $this->append($value);
215
    }
216
217
    /**
218
     * Append a (key) + value to the current array.
219
     *
220
     * @param mixed $value
221
     * @param mixed $key
222
     *
223
     * @return static
224
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
225
     *
226
     * @psalm-param  T $value
227
     * @psalm-return static<TKey,T>
228
     */
229 13
    public function append($value, $key = null): self
230
    {
231 13
        $this->generatorToArray();
232
233 13
        if ($this->properties !== []) {
234 4
            $this->checkType($key, $value);
235
        }
236
237 12
        if ($key !== null) {
238
            if (
239
                isset($this->array[$key])
240
                &&
241
                \is_array($this->array[$key]) === true
242
            ) {
243
                $this->array[$key][] = $value;
244
            } else {
245
                $this->array[$key] = $value;
246
            }
247
        } else {
248 12
            $this->array[] = $value;
249
        }
250
251 12
        return $this;
252
    }
253
254
    /**
255
     * Sort the entries by value.
256
     *
257
     * @param int $sort_flags [optional] <p>
258
     *                        You may modify the behavior of the sort using the optional
259
     *                        parameter sort_flags, for details
260
     *                        see sort.
261
     *                        </p>
262
     *
263
     * @return static
264
     *                <p>(Mutable) Return this Arrayy object.</p>
265
     *
266
     * @psalm-return static<TKey,T>
267
     */
268 4
    public function asort(int $sort_flags = 0): self
269
    {
270 4
        $this->generatorToArray();
271
272 4
        \asort($this->array, $sort_flags);
273
274 4
        return $this;
275
    }
276
277
    /**
278
     * Counts all elements in an array, or something in an object.
279
     *
280
     * <p>
281
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
282
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
283
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
284
     * implemented and used in PHP.
285
     * </p>
286
     *
287
     * @see http://php.net/manual/en/function.count.php
288
     *
289
     * @param int $mode [optional] If the optional mode parameter is set to
290
     *                  COUNT_RECURSIVE (or 1), count
291
     *                  will recursively count the array. This is particularly useful for
292
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
293
     *
294
     * @return int
295
     *             <p>
296
     *             The number of elements in var, which is
297
     *             typically an array, since anything else will have one
298
     *             element.
299
     *             </p>
300
     *             <p>
301
     *             If var is not an array or an object with
302
     *             implemented Countable interface,
303
     *             1 will be returned.
304
     *             There is one exception, if var is &null;,
305
     *             0 will be returned.
306
     *             </p>
307
     *             <p>
308
     *             Caution: count may return 0 for a variable that isn't set,
309
     *             but it may also return 0 for a variable that has been initialized with an
310
     *             empty array. Use isset to test if a variable is set.
311
     *             </p>
312
     */
313 145
    public function count(int $mode = \COUNT_NORMAL): int
314
    {
315
        if (
316 145
            $this->generator
317
            &&
318 145
            $mode === \COUNT_NORMAL
319
        ) {
320 4
            return \iterator_count($this->generator);
321
        }
322
323 141
        return \count($this->getArray(), $mode);
324
    }
325
326
    /**
327
     * Exchange the array for another one.
328
     *
329
     * @param array|static $data
330
     *
331
     * @return array
332
     *
333
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
334
     * @psalm-return array<TKey,T>
335
     */
336 1
    public function exchangeArray($data): array
337
    {
338 1
        $this->array = $this->fallbackForArray($data);
339
340 1
        return $this->array;
341
    }
342
343
    /**
344
     * Creates a copy of the ArrayyObject.
345
     *
346
     * @return array
347
     *
348
     * @psalm-return array<TKey,T>
349
     */
350 5
    public function getArrayCopy(): array
351
    {
352 5
        $this->generatorToArray();
353
354 5
        return $this->array;
355
    }
356
357
    /**
358
     * Returns a new iterator, thus implementing the \Iterator interface.
359
     *
360
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (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...
361
     *                          <p>An iterator for the values in the array.</p>
362
     */
363 24
    public function getIterator(): \Iterator
364
    {
365 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
366 1
            return $this->generator;
367
        }
368
369 23
        $iterator = $this->getIteratorClass();
370
371 23
        if ($iterator === ArrayyIterator::class) {
372 23
            return new $iterator($this->getArray(), 0, static::class);
373
        }
374
375
        return new $iterator($this->getArray());
376
    }
377
378
    /**
379
     * Gets the iterator classname for the ArrayObject.
380
     *
381
     * @return string
382
     *
383
     * @psalm-return class-string
384
     */
385 23
    public function getIteratorClass(): string
386
    {
387 23
        return $this->iteratorClass;
388
    }
389
390
    /**
391
     * Sort the entries by key
392
     *
393
     * @param int $sort_flags [optional] <p>
394
     *                        You may modify the behavior of the sort using the optional
395
     *                        parameter sort_flags, for details
396
     *                        see sort.
397
     *                        </p>
398
     *
399
     * @return static
400
     *                <p>(Mutable) Return this Arrayy object.</p>
401
     *
402
     * @psalm-return static<TKey,T>
403
     */
404 4
    public function ksort(int $sort_flags = 0): self
405
    {
406 4
        $this->generatorToArray();
407
408 4
        \ksort($this->array, $sort_flags);
409
410 4
        return $this;
411
    }
412
413
    /**
414
     * Sort an array using a case insensitive "natural order" algorithm
415
     *
416
     * @return static
417
     *                <p>(Mutable) Return this Arrayy object.</p>
418
     *
419
     * @psalm-return static<TKey,T>
420
     */
421
    public function natcasesort(): self
422
    {
423
        $this->generatorToArray();
424
425
        \natcasesort($this->array);
426
427
        return $this;
428
    }
429
430
    /**
431
     * Sort entries using a "natural order" algorithm
432
     *
433
     * @return static
434
     *                <p>(Mutable) Return this Arrayy object.</p>
435
     *
436
     * @psalm-return static<TKey,T>
437
     */
438 1
    public function natsort(): self
439
    {
440 1
        $this->generatorToArray();
441
442 1
        \natsort($this->array);
443
444 1
        return $this;
445
    }
446
447
    /**
448
     * Whether or not an offset exists.
449
     *
450
     * @param bool|int|string $offset
451
     *
452
     * @return bool
453
     *
454
     * @noinspection PhpSillyAssignmentInspection
455
     */
456 130
    public function offsetExists($offset): bool
457
    {
458 130
        $this->generatorToArray();
459
460 130
        if ($this->array === []) {
461 5
            return false;
462
        }
463
464
        // php cast "bool"-index into "int"-index
465 125
        if ((bool) $offset === $offset) {
466 1
            $offset = (int) $offset;
467
        }
468
469
        /** @var int|string $offset - hint for phpstan */
470 125
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
471
472 125
        $tmpReturn = $this->keyExists($offset);
473
474
        if (
475 125
            $tmpReturn === true
476
            ||
477
            (
478 92
                $tmpReturn === false
479
                &&
480 125
                \strpos((string) $offset, $this->pathSeparator) === false
481
            )
482
        ) {
483 123
            return $tmpReturn;
484
        }
485
486 3
        $offsetExists = false;
487
488
        if (
489 3
            $this->pathSeparator
490
            &&
491 3
            (string) $offset === $offset
492
            &&
493 3
            \strpos($offset, $this->pathSeparator) !== false
494
        ) {
495 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
496 3
            if ($explodedPath !== false) {
497 3
                $lastOffset = \array_pop($explodedPath);
498 3
                if ($lastOffset !== null) {
499 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
500
501 3
                    $this->callAtPath(
502 3
                        $containerPath,
503
                        static function ($container) use ($lastOffset, &$offsetExists) {
504 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
505 3
                        }
506
                    );
507
                }
508
            }
509
        }
510
511 3
        return $offsetExists;
512
    }
513
514
    /**
515
     * Returns the value at specified offset.
516
     *
517
     * @param int|string $offset
518
     *
519
     * @return mixed
520
     *               <p>Will return null if the offset did not exists.</p>
521
     */
522 101
    public function offsetGet($offset)
523
    {
524 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
525
    }
526
527
    /**
528
     * Assigns a value to the specified offset + check the type.
529
     *
530
     * @param int|string|null $offset
531
     * @param mixed           $value
532
     *
533
     * @return void
534
     */
535 21
    public function offsetSet($offset, $value)
536
    {
537 21
        $this->generatorToArray();
538
539 21
        if ($offset === null) {
540 5
            if ($this->properties !== []) {
541 1
                $this->checkType(null, $value);
542
            }
543
544 4
            $this->array[] = $value;
545
        } else {
546 16
            $this->internalSet(
547 16
                $offset,
548 16
                $value,
549 16
                true
550
            );
551
        }
552 20
    }
553
554
    /**
555
     * Unset an offset.
556
     *
557
     * @param int|string $offset
558
     *
559
     * @return void
560
     */
561 12
    public function offsetUnset($offset)
562
    {
563 12
        $this->generatorToArray();
564
565 12
        if ($this->array === []) {
566 3
            return;
567
        }
568
569 10
        if ($this->keyExists($offset)) {
570 7
            unset($this->array[$offset]);
571
572 7
            return;
573
        }
574
575
        if (
576 5
            $this->pathSeparator
577
            &&
578 5
            (string) $offset === $offset
579
            &&
580 5
            \strpos($offset, $this->pathSeparator) !== false
581
        ) {
582 2
            $path = \explode($this->pathSeparator, (string) $offset);
583
584 2
            if ($path !== false) {
585 2
                $pathToUnset = \array_pop($path);
586
587 2
                $this->callAtPath(
588 2
                    \implode($this->pathSeparator, $path),
589
                    static function (&$offset) use ($pathToUnset) {
590 2
                        unset($offset[$pathToUnset]);
591 2
                    }
592
                );
593
            }
594
        }
595
596 5
        unset($this->array[$offset]);
597 5
    }
598
599
    /**
600
     * Serialize the current "Arrayy"-object.
601
     *
602
     * @return string
603
     */
604 2
    public function serialize(): string
605
    {
606 2
        $this->generatorToArray();
607
608 2
        if (\PHP_VERSION_ID < 70400) {
609 2
            return parent::serialize();
610
        }
611
612
        return \serialize($this);
613
    }
614
615
    /**
616
     * Sets the iterator classname for the current "Arrayy"-object.
617
     *
618
     * @param string $iteratorClass
619
     *
620
     * @throws \InvalidArgumentException
621
     *
622
     * @return void
623
     *
624
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
625
     */
626 1057
    public function setIteratorClass($iteratorClass)
627
    {
628 1057
        if (\class_exists($iteratorClass)) {
629 1057
            $this->iteratorClass = $iteratorClass;
630
631 1057
            return;
632
        }
633
634
        if (\strpos($iteratorClass, '\\') === 0) {
635
            $iteratorClass = '\\' . $iteratorClass;
636
            if (\class_exists($iteratorClass)) {
637
                $this->iteratorClass = $iteratorClass;
638
639
                return;
640
            }
641
        }
642
643
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
644
    }
645
646
    /**
647
     * Sort the entries with a user-defined comparison function and maintain key association.
648
     *
649
     * @param callable $function
650
     *
651
     * @throws \InvalidArgumentException
652
     *
653
     * @return static
654
     *                <p>(Mutable) Return this Arrayy object.</p>
655
     *
656
     * @psalm-return static<TKey,T>
657
     */
658 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...
659
    {
660
        if (!\is_callable($function)) {
661
            throw new \InvalidArgumentException('Passed function must be callable');
662
        }
663
664
        $this->generatorToArray();
665
666
        \uasort($this->array, $function);
667
668
        return $this;
669
    }
670
671
    /**
672
     * Sort the entries by keys using a user-defined comparison function.
673
     *
674
     * @param callable $function
675
     *
676
     * @throws \InvalidArgumentException
677
     *
678
     * @return static
679
     *                <p>(Mutable) Return this Arrayy object.</p>
680
     *
681
     * @psalm-return static<TKey,T>
682
     */
683 5
    public function uksort($function): self
684
    {
685 5
        return $this->customSortKeys($function);
686
    }
687
688
    /**
689
     * Unserialize an string and return the instance of the "Arrayy"-class.
690
     *
691
     * @param string $string
692
     *
693
     * @return static
694
     *
695
     * @psalm-return static<TKey,T>
696
     */
697 2
    public function unserialize($string): self
698
    {
699 2
        if (\PHP_VERSION_ID < 70400) {
700 2
            parent::unserialize($string);
701
702 2
            return $this;
703
        }
704
705
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
706
    }
707
708
    /**
709
     * Append a (key) + values to the current array.
710
     *
711
     * @param array $values
712
     * @param mixed $key
713
     *
714
     * @return static
715
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
716
     *
717
     * @psalm-param  array<mixed,T> $values
718
     * @psalm-param  TKey|null $key
719
     * @psalm-return static<TKey,T>
720
     */
721 1
    public function appendArrayValues(array $values, $key = null)
722
    {
723 1
        $this->generatorToArray();
724
725 1
        if ($key !== null) {
726
            if (
727 1
                isset($this->array[$key])
728
                &&
729 1
                \is_array($this->array[$key]) === true
730
            ) {
731 1
                foreach ($values as $value) {
732 1
                    $this->array[$key][] = $value;
733
                }
734
            } else {
735 1
                foreach ($values as $value) {
736
                    $this->array[$key] = $value;
737
                }
738
            }
739
        } else {
740
            foreach ($values as $value) {
741
                $this->array[] = $value;
742
            }
743
        }
744
745 1
        return $this;
746
    }
747
748
    /**
749
     * Add a suffix to each key.
750
     *
751
     * @param mixed $prefix
752
     *
753
     * @return static
754
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
755
     *
756
     * @psalm-return static<TKey,T>
757
     */
758 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...
759
    {
760
        // init
761 10
        $result = [];
762
763 10
        foreach ($this->getGenerator() as $key => $item) {
764 9
            if ($item instanceof self) {
765
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
766 9
            } elseif (\is_array($item) === true) {
767
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
768
                    ->appendToEachKey($prefix)
769
                    ->toArray();
770
            } else {
771 9
                $result[$prefix . $key] = $item;
772
            }
773
        }
774
775 10
        return self::create($result, $this->iteratorClass, false);
776
    }
777
778
    /**
779
     * Add a prefix to each value.
780
     *
781
     * @param mixed $prefix
782
     *
783
     * @return static
784
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
785
     *
786
     * @psalm-return static<TKey,T>
787
     */
788 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...
789
    {
790
        // init
791 10
        $result = [];
792
793 10
        foreach ($this->getGenerator() as $key => $item) {
794 9
            if ($item instanceof self) {
795
                $result[$key] = $item->appendToEachValue($prefix);
796 9
            } elseif (\is_array($item) === true) {
797
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
798 9
            } elseif (\is_object($item) === true) {
799 1
                $result[$key] = $item;
800
            } else {
801 8
                $result[$key] = $prefix . $item;
802
            }
803
        }
804
805 10
        return self::create($result, $this->iteratorClass, false);
806
    }
807
808
    /**
809
     * Sort an array in reverse order and maintain index association.
810
     *
811
     * @return static
812
     *                <p>(Mutable) Return this Arrayy object.</p>
813
     *
814
     * @psalm-return static<TKey,T>
815
     */
816 10
    public function arsort(): self
817
    {
818 10
        $this->generatorToArray();
819
820 10
        \arsort($this->array);
821
822 10
        return $this;
823
    }
824
825
    /**
826
     * Iterate over the current array and execute a callback for each loop.
827
     *
828
     * @param \Closure $closure
829
     *
830
     * @return static
831
     *                <p>(Immutable)</p>
832
     *
833
     * @psalm-return static<TKey,T>
834
     */
835 2
    public function at(\Closure $closure): self
836
    {
837 2
        $arrayy = clone $this;
838
839 2
        foreach ($arrayy->getGenerator() as $key => $value) {
840 2
            $closure($value, $key);
841
        }
842
843 2
        return static::create(
844 2
            $arrayy->toArray(),
845 2
            $this->iteratorClass,
846 2
            false
847
        );
848
    }
849
850
    /**
851
     * Returns the average value of the current array.
852
     *
853
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
854
     *
855
     * @return float|int
856
     *                   <p>The average value.</p>
857
     */
858 10
    public function average($decimals = 0)
859
    {
860 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
861
862 10
        if (!$count) {
863 2
            return 0;
864
        }
865
866 8
        if ((int) $decimals !== $decimals) {
867 3
            $decimals = 0;
868
        }
869
870 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
871
    }
872
873
    /**
874
     * Changes all keys in an array.
875
     *
876
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
877
     *                  or <strong>CASE_LOWER</strong> (default)</p>
878
     *
879
     * @return static
880
     *                <p>(Immutable)</p>
881
     *
882
     * @psalm-return static<TKey,T>
883
     */
884 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
885
    {
886
        if (
887 1
            $case !== \CASE_LOWER
888
            &&
889 1
            $case !== \CASE_UPPER
890
        ) {
891
            $case = \CASE_LOWER;
892
        }
893
894 1
        $return = [];
895 1
        foreach ($this->getGenerator() as $key => $value) {
896 1
            if ($case === \CASE_LOWER) {
897 1
                $key = \mb_strtolower((string) $key);
898
            } else {
899 1
                $key = \mb_strtoupper((string) $key);
900
            }
901
902 1
            $return[$key] = $value;
903
        }
904
905 1
        return static::create(
906 1
            $return,
907 1
            $this->iteratorClass,
908 1
            false
909
        );
910
    }
911
912
    /**
913
     * Change the path separator of the array wrapper.
914
     *
915
     * By default, the separator is: "."
916
     *
917
     * @param string $separator <p>Separator to set.</p>
918
     *
919
     * @return static
920
     *                <p>Mutable</p>
921
     *
922
     * @psalm-return static<TKey,T>
923
     */
924 11
    public function changeSeparator($separator): self
925
    {
926 11
        $this->pathSeparator = $separator;
927
928 11
        return $this;
929
    }
930
931
    /**
932
     * Create a chunked version of the current array.
933
     *
934
     * @param int  $size         <p>Size of each chunk.</p>
935
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
936
     *
937
     * @return static
938
     *                <p>(Immutable) A new array of chunks from the original array.</p>
939
     *
940
     * @psalm-return static<TKey,T>
941
     */
942 5
    public function chunk($size, $preserveKeys = false): self
943
    {
944 5
        return static::create(
945 5
            \array_chunk($this->getArray(), $size, $preserveKeys),
946 5
            $this->iteratorClass,
947 5
            false
948
        );
949
    }
950
951
    /**
952
     * Clean all falsy values from the current array.
953
     *
954
     * @return static
955
     *                <p>(Immutable)</p>
956
     *
957
     * @psalm-return static<TKey,T>
958
     */
959 8
    public function clean(): self
960
    {
961 8
        return $this->filter(
962
            static function ($value) {
963 7
                return (bool) $value;
964 8
            }
965
        );
966
    }
967
968
    /**
969
     * WARNING!!! -> Clear the current array.
970
     *
971
     * @return static
972
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
973
     *
974
     * @psalm-return static<TKey,T>
975
     */
976 5
    public function clear(): self
977
    {
978 5
        $this->array = [];
979 5
        $this->generator = null;
980
981 5
        return $this;
982
    }
983
984
    /**
985
     * Check if an item is in the current array.
986
     *
987
     * @param float|int|string $value
988
     * @param bool             $recursive
989
     * @param bool             $strict
990
     *
991
     * @return bool
992
     */
993 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
994
    {
995 23
        if ($recursive === true) {
996 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
997
        }
998
999 14
        foreach ($this->getGenerator() as $valueFromArray) {
1000 11
            if ($strict) {
1001 11
                if ($value === $valueFromArray) {
1002 11
                    return true;
1003
                }
1004
            } else {
1005
                /** @noinspection NestedPositiveIfStatementsInspection */
1006
                if ($value == $valueFromArray) {
1007
                    return true;
1008
                }
1009
            }
1010
        }
1011
1012 7
        return false;
1013
    }
1014
1015
    /**
1016
     * Check if an (case-insensitive) string is in the current array.
1017
     *
1018
     * @param string $value
1019
     * @param bool   $recursive
1020
     *
1021
     * @return bool
1022
     */
1023 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1024
    {
1025 26
        if ($recursive === true) {
1026 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
1027 22
                if (\is_array($valueTmp) === true) {
1028 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1029 5
                    if ($return === true) {
1030 5
                        return $return;
1031
                    }
1032 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1033 16
                    return true;
1034
                }
1035
            }
1036
1037 10
            return false;
1038
        }
1039
1040 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
1041 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1042 8
                return true;
1043
            }
1044
        }
1045
1046 5
        return false;
1047
    }
1048
1049
    /**
1050
     * Check if the given key/index exists in the array.
1051
     *
1052
     * @param int|string $key <p>key/index to search for</p>
1053
     *
1054
     * @return bool
1055
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1056
     */
1057 4
    public function containsKey($key): bool
1058
    {
1059 4
        return $this->offsetExists($key);
1060
    }
1061
1062
    /**
1063
     * Check if all given needles are present in the array as key/index.
1064
     *
1065
     * @param array $needles   <p>The keys you are searching for.</p>
1066
     * @param bool  $recursive
1067
     *
1068
     * @return bool
1069
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1070
     *
1071
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1072
     */
1073 2
    public function containsKeys(array $needles, $recursive = false): bool
1074
    {
1075 2
        if ($recursive === true) {
1076
            return
1077 2
                \count(
1078 2
                    \array_intersect(
1079 2
                        $needles,
1080 2
                        $this->keys(true)->getArray()
1081
                    ),
1082 2
                    \COUNT_RECURSIVE
1083
                )
1084
                ===
1085 2
                \count(
1086 2
                    $needles,
1087 2
                    \COUNT_RECURSIVE
1088
                );
1089
        }
1090
1091 1
        return \count(
1092 1
            \array_intersect($needles, $this->keys()->getArray()),
1093 1
            \COUNT_NORMAL
1094
        )
1095
               ===
1096 1
               \count(
1097 1
                   $needles,
1098 1
                   \COUNT_NORMAL
1099
               );
1100
    }
1101
1102
    /**
1103
     * Check if all given needles are present in the array as key/index.
1104
     *
1105
     * @param array $needles <p>The keys you are searching for.</p>
1106
     *
1107
     * @return bool
1108
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1109
     *
1110
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1111
     */
1112 1
    public function containsKeysRecursive(array $needles): bool
1113
    {
1114 1
        return $this->containsKeys($needles, true);
1115
    }
1116
1117
    /**
1118
     * alias: for "Arrayy->contains()"
1119
     *
1120
     * @param float|int|string $value
1121
     *
1122
     * @return bool
1123
     *
1124
     * @see Arrayy::contains()
1125
     */
1126 9
    public function containsValue($value): bool
1127
    {
1128 9
        return $this->contains($value);
1129
    }
1130
1131
    /**
1132
     * alias: for "Arrayy->contains($value, true)"
1133
     *
1134
     * @param float|int|string $value
1135
     *
1136
     * @return bool
1137
     *
1138
     * @see Arrayy::contains()
1139
     */
1140 18
    public function containsValueRecursive($value): bool
1141
    {
1142 18
        return $this->contains($value, true);
1143
    }
1144
1145
    /**
1146
     * Check if all given needles are present in the array.
1147
     *
1148
     * @param array $needles
1149
     *
1150
     * @return bool
1151
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1152
     *
1153
     * @psalm-param array<mixed>|array<T> $needles
1154
     */
1155 1
    public function containsValues(array $needles): bool
1156
    {
1157 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1158
               ===
1159 1
               \count($needles, \COUNT_NORMAL);
1160
    }
1161
1162
    /**
1163
     * Counts all the values of an array
1164
     *
1165
     * @see          http://php.net/manual/en/function.array-count-values.php
1166
     *
1167
     * @return static
1168
     *                <p>
1169
     *                (Immutable)
1170
     *                An associative Arrayy-object of values from input as
1171
     *                keys and their count as value.
1172
     *                </p>
1173
     *
1174
     * @psalm-return static<TKey,T>
1175
     */
1176 7
    public function countValues(): self
1177
    {
1178 7
        return self::create(\array_count_values($this->getArray()), $this->iteratorClass);
1179
    }
1180
1181
    /**
1182
     * Creates an Arrayy object.
1183
     *
1184
     * @param mixed  $data
1185
     * @param string $iteratorClass
1186
     * @param bool   $checkPropertiesInConstructor
1187
     *
1188
     * @return static
1189
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1190
     *
1191
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1192
     * @psalm-return static<TKey,T>
1193
     */
1194 670
    public static function create(
1195
        $data = [],
1196
        string $iteratorClass = ArrayyIterator::class,
1197
        bool $checkPropertiesInConstructor = true
1198
    ) {
1199 670
        return new static(
1200 670
            $data,
1201 670
            $iteratorClass,
1202 670
            $checkPropertiesInConstructor
1203
        );
1204
    }
1205
1206
    /**
1207
     * WARNING: Creates an Arrayy object by reference.
1208
     *
1209
     * @param array $array
1210
     *
1211
     * @return static
1212
     *                <p>(Mutable) Return this Arrayy object.</p>
1213
     *
1214
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1215
     * @psalm-return static<TKey,T>
1216
     */
1217 1
    public function createByReference(array &$array = []): self
1218
    {
1219 1
        $array = $this->fallbackForArray($array);
1220
1221 1
        $this->array = &$array;
1222
1223 1
        return $this;
1224
    }
1225
1226
    /**
1227
     * Create an new instance from a callable function which will return an Generator.
1228
     *
1229
     * @param callable $generatorFunction
1230
     *
1231
     * @return static
1232
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1233
     *
1234
     * @psalm-param  callable():\Generator<TKey,T> $generatorFunction
1235
     * @psalm-return static<TKey,T>
1236
     */
1237 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1238
    {
1239 5
        return self::create($generatorFunction);
1240
    }
1241
1242
    /**
1243
     * Create an new instance filled with a copy of values from a "Generator"-object.
1244
     *
1245
     * @param \Generator $generator
1246
     *
1247
     * @return static
1248
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1249
     *
1250
     * @psalm-param \Generator<TKey,T> $generator
1251
     * @psalm-return static<TKey,T>
1252
     */
1253 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1254
    {
1255 4
        return self::create(\iterator_to_array($generator, true));
1256
    }
1257
1258
    /**
1259
     * Create an new Arrayy object via JSON.
1260
     *
1261
     * @param string $json
1262
     *
1263
     * @return static
1264
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1265
     *
1266
     * @psalm-return static<TKey,T>
1267
     */
1268 5
    public static function createFromJson(string $json): self
1269
    {
1270 5
        return static::create(\json_decode($json, true));
1271
    }
1272
1273
    /**
1274
     * Create an new instance filled with values from an object that is iterable.
1275
     *
1276
     * @param \Traversable $object <p>iterable object</p>
1277
     *
1278
     * @return static
1279
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1280
     *
1281
     * @psalm-param \Traversable<TKey,T> $object
1282
     * @psalm-return static<TKey,T>
1283
     */
1284 4
    public static function createFromObject(\Traversable $object): self
1285
    {
1286
        // init
1287 4
        $array = self::create();
1288
1289 4
        if ($object instanceof self) {
1290 4
            $objectArray = $object->getGenerator();
1291
        } else {
1292
            $objectArray = $object;
1293
        }
1294
1295 4
        foreach ($objectArray as $key => $value) {
1296 3
            $array[$key] = $value;
1297
        }
1298
1299 4
        return $array;
1300
    }
1301
1302
    /**
1303
     * Create an new instance filled with values from an object.
1304
     *
1305
     * @param object $object
1306
     *
1307
     * @return static
1308
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1309
     *
1310
     * @psalm-return static<TKey,T>
1311
     */
1312 5
    public static function createFromObjectVars($object): self
1313
    {
1314 5
        return self::create(self::objectToArray($object));
1315
    }
1316
1317
    /**
1318
     * Create an new Arrayy object via string.
1319
     *
1320
     * @param string      $str       <p>The input string.</p>
1321
     * @param string|null $delimiter <p>The boundary string.</p>
1322
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1323
     *                               used.</p>
1324
     *
1325
     * @return static
1326
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1327
     *
1328
     * @psalm-return static<TKey,T>
1329
     */
1330 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1331
    {
1332 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...
1333 1
            \preg_match_all($regEx, $str, $array);
1334
1335 1
            if (!empty($array)) {
1336 1
                $array = $array[0];
1337
            }
1338
        } else {
1339
            /** @noinspection NestedPositiveIfStatementsInspection */
1340 9
            if ($delimiter !== null) {
1341 7
                $array = \explode($delimiter, $str);
1342
            } else {
1343 2
                $array = [$str];
1344
            }
1345
        }
1346
1347
        // trim all string in the array
1348 10
        \array_walk(
1349 10
            $array,
1350
            static function (&$val) {
1351 10
                if ((string) $val === $val) {
1352 10
                    $val = \trim($val);
1353
                }
1354 10
            }
1355
        );
1356
1357 10
        return static::create($array);
1358
    }
1359
1360
    /**
1361
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1362
     *
1363
     * @param \Traversable $traversable
1364
     *
1365
     * @return static
1366
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1367
     *
1368
     * @psalm-param \Traversable<TKey,T> $traversable
1369
     * @psalm-return static<TKey,T>
1370
     */
1371 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1372
    {
1373 1
        return self::create(\iterator_to_array($traversable, true));
1374
    }
1375
1376
    /**
1377
     * Create an new instance containing a range of elements.
1378
     *
1379
     * @param mixed $low  <p>First value of the sequence.</p>
1380
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1381
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1382
     *
1383
     * @return static
1384
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1385
     *
1386
     * @psalm-return static<TKey,T>
1387
     */
1388 2
    public static function createWithRange($low, $high, int $step = 1): self
1389
    {
1390 2
        return static::create(\range($low, $high, $step));
1391
    }
1392
1393
    /**
1394
     * Gets the element of the array at the current internal iterator position.
1395
     *
1396
     * @return false|mixed
1397
     */
1398
    public function current()
1399
    {
1400
        return \current($this->array);
1401
    }
1402
1403
    /**
1404
     * Custom sort by index via "uksort".
1405
     *
1406
     * @see          http://php.net/manual/en/function.uksort.php
1407
     *
1408
     * @param callable $function
1409
     *
1410
     * @throws \InvalidArgumentException
1411
     *
1412
     * @return static
1413
     *                <p>(Mutable) Return this Arrayy object.</p>
1414
     *
1415
     * @psalm-return static<TKey,T>
1416
     */
1417 5
    public function customSortKeys(callable $function): self
1418
    {
1419 5
        $this->generatorToArray();
1420
1421 5
        \uksort($this->array, $function);
1422
1423 5
        return $this;
1424
    }
1425
1426
    /**
1427
     * Custom sort by value via "usort".
1428
     *
1429
     * @see          http://php.net/manual/en/function.usort.php
1430
     *
1431
     * @param callable $function
1432
     *
1433
     * @throws \InvalidArgumentException
1434
     *
1435
     * @return static
1436
     *                <p>(Mutable) Return this Arrayy object.</p>
1437
     *
1438
     * @psalm-return static<TKey,T>
1439
     */
1440 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...
1441
    {
1442 6
        if (\is_callable($function) === false) {
1443
            throw new \InvalidArgumentException('Passed function must be callable');
1444
        }
1445
1446 6
        $this->generatorToArray();
1447
1448 6
        \usort($this->array, $function);
1449
1450 6
        return $this;
1451
    }
1452
1453
    /**
1454
     * Delete the given key or keys.
1455
     *
1456
     * @param int|int[]|string|string[] $keyOrKeys
1457
     *
1458
     * @return void
1459
     */
1460 4
    public function delete($keyOrKeys)
1461
    {
1462 4
        $keyOrKeys = (array) $keyOrKeys;
1463
1464 4
        foreach ($keyOrKeys as $key) {
1465 4
            $this->offsetUnset($key);
1466
        }
1467 4
    }
1468
1469
    /**
1470
     * Return values that are only in the current array.
1471
     *
1472
     * @param array ...$array
1473
     *
1474
     * @return static
1475
     *                <p>(Immutable)</p>
1476
     *
1477
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1478
     * @psalm-return static<TKey,T>
1479
     */
1480 13
    public function diff(...$array): self
1481
    {
1482 13
        return static::create(
1483 13
            \array_diff($this->getArray(), ...$array),
1484 13
            $this->iteratorClass,
1485 13
            false
1486
        );
1487
    }
1488
1489
    /**
1490
     * Return values that are only in the current array.
1491
     *
1492
     * @param array ...$array
1493
     *
1494
     * @return static
1495
     *                <p>(Immutable)</p>
1496
     *
1497
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
1498
     * @psalm-return static<TKey,T>
1499
     */
1500 8
    public function diffKey(...$array): self
1501
    {
1502 8
        return static::create(
1503 8
            \array_diff_key($this->getArray(), ...$array),
1504 8
            $this->iteratorClass,
1505 8
            false
1506
        );
1507
    }
1508
1509
    /**
1510
     * Return values and Keys that are only in the current array.
1511
     *
1512
     * @param array $array
1513
     *
1514
     * @return static
1515
     *                <p>(Immutable)</p>
1516
     *
1517
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1518
     * @psalm-return static<TKey,T>
1519
     */
1520 8
    public function diffKeyAndValue(array $array = []): self
1521
    {
1522 8
        return static::create(
1523 8
            \array_diff_assoc($this->getArray(), $array),
1524 8
            $this->iteratorClass,
1525 8
            false
1526
        );
1527
    }
1528
1529
    /**
1530
     * Return values that are only in the current multi-dimensional array.
1531
     *
1532
     * @param array      $array
1533
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1534
     *
1535
     * @return static
1536
     *                <p>(Immutable)</p>
1537
     *
1538
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1539
     * @psalm-param  null|array<TKey,T> $helperVariableForRecursion
1540
     * @psalm-return static<TKey,T>
1541
     */
1542 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1543
    {
1544
        // init
1545 1
        $result = [];
1546
1547
        if (
1548 1
            $helperVariableForRecursion !== null
1549
            &&
1550 1
            \is_array($helperVariableForRecursion) === true
1551
        ) {
1552
            $arrayForTheLoop = $helperVariableForRecursion;
1553
        } else {
1554 1
            $arrayForTheLoop = $this->getGenerator();
1555
        }
1556
1557 1
        foreach ($arrayForTheLoop as $key => $value) {
1558 1
            if ($value instanceof self) {
1559
                $value = $value->getArray();
1560
            }
1561
1562 1
            if (\array_key_exists($key, $array)) {
1563 1
                if ($value !== $array[$key]) {
1564 1
                    $result[$key] = $value;
1565
                }
1566
            } else {
1567 1
                $result[$key] = $value;
1568
            }
1569
        }
1570
1571 1
        return static::create(
1572 1
            $result,
1573 1
            $this->iteratorClass,
1574 1
            false
1575
        );
1576
    }
1577
1578
    /**
1579
     * Return values that are only in the new $array.
1580
     *
1581
     * @param array $array
1582
     *
1583
     * @return static
1584
     *                <p>(Immutable)</p>
1585
     *
1586
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
1587
     * @psalm-return static<TKey,T>
1588
     */
1589 8
    public function diffReverse(array $array = []): self
1590
    {
1591 8
        return static::create(
1592 8
            \array_diff($array, $this->getArray()),
1593 8
            $this->iteratorClass,
1594 8
            false
1595
        );
1596
    }
1597
1598
    /**
1599
     * Divide an array into two arrays. One with keys and the other with values.
1600
     *
1601
     * @return static
1602
     *                <p>(Immutable)</p>
1603
     *
1604
     * @psalm-return static<TKey,T>
1605
     */
1606 1
    public function divide(): self
1607
    {
1608 1
        return static::create(
1609
            [
1610 1
                $this->keys(),
1611 1
                $this->values(),
1612
            ],
1613 1
            $this->iteratorClass,
1614 1
            false
1615
        );
1616
    }
1617
1618
    /**
1619
     * Iterate over the current array and modify the array's value.
1620
     *
1621
     * @param \Closure $closure
1622
     *
1623
     * @return static
1624
     *                <p>(Immutable)</p>
1625
     *
1626
     * @psalm-return static<TKey,T>
1627
     */
1628 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...
1629
    {
1630
        // init
1631 5
        $array = [];
1632
1633 5
        foreach ($this->getGenerator() as $key => $value) {
1634 5
            $array[$key] = $closure($value, $key);
1635
        }
1636
1637 5
        return static::create(
1638 5
            $array,
1639 5
            $this->iteratorClass,
1640 5
            false
1641
        );
1642
    }
1643
1644
    /**
1645
     * Sets the internal iterator to the last element in the array and returns this element.
1646
     *
1647
     * @return mixed
1648
     */
1649
    public function end()
1650
    {
1651
        return \end($this->array);
1652
    }
1653
1654
    /**
1655
     * Check if a value is in the current array using a closure.
1656
     *
1657
     * @param \Closure $closure
1658
     *
1659
     * @return bool
1660
     *              <p>Returns true if the given value is found, false otherwise.</p>
1661
     */
1662 4
    public function exists(\Closure $closure): bool
1663
    {
1664
        // init
1665 4
        $isExists = false;
1666
1667 4
        foreach ($this->getGenerator() as $key => $value) {
1668 3
            if ($closure($value, $key)) {
1669 1
                $isExists = true;
1670
1671 1
                break;
1672
            }
1673
        }
1674
1675 4
        return $isExists;
1676
    }
1677
1678
    /**
1679
     * Fill the array until "$num" with "$default" values.
1680
     *
1681
     * @param int   $num
1682
     * @param mixed $default
1683
     *
1684
     * @return static
1685
     *                <p>(Immutable)</p>
1686
     *
1687
     * @psalm-return static<TKey,T>
1688
     */
1689 8
    public function fillWithDefaults(int $num, $default = null): self
1690
    {
1691 8
        if ($num < 0) {
1692 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1693
        }
1694
1695 7
        $this->generatorToArray();
1696
1697 7
        $tmpArray = $this->array;
1698
1699 7
        $count = \count($tmpArray);
1700
1701 7
        while ($count < $num) {
1702 4
            $tmpArray[] = $default;
1703 4
            ++$count;
1704
        }
1705
1706 7
        return static::create(
1707 7
            $tmpArray,
1708 7
            $this->iteratorClass,
1709 7
            false
1710
        );
1711
    }
1712
1713
    /**
1714
     * Find all items in an array that pass the truth test.
1715
     *
1716
     * @param \Closure|null $closure [optional] <p>
1717
     *                               The callback function to use
1718
     *                               </p>
1719
     *                               <p>
1720
     *                               If no callback is supplied, all entries of
1721
     *                               input equal to false (see
1722
     *                               converting to
1723
     *                               boolean) will be removed.
1724
     *                               </p>
1725
     * @param int           $flag    [optional] <p>
1726
     *                               Flag determining what arguments are sent to <i>callback</i>:
1727
     *                               </p><ul>
1728
     *                               <li>
1729
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1730
     *                               to <i>callback</i> instead of the value</span>
1731
     *                               </li>
1732
     *                               <li>
1733
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1734
     *                               arguments to <i>callback</i> instead of the value</span>
1735
     *                               </li>
1736
     *                               </ul>
1737
     *
1738
     * @return static
1739
     *                <p>(Immutable)</p>
1740
     *
1741
     * @psalm-param \Closure(T=,TKey=):bool|\Closure(T=):bool $closure
1742
     * @psalm-return static<TKey,T>
1743
     */
1744 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1745
    {
1746 12
        if (!$closure) {
1747 1
            return $this->clean();
1748
        }
1749
1750 12
        return static::create(
1751 12
            \array_filter($this->getArray(), $closure, $flag),
1752 12
            $this->iteratorClass,
1753 12
            false
1754
        );
1755
    }
1756
1757
    /**
1758
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1759
     * property within that.
1760
     *
1761
     * @param string          $property
1762
     * @param string|string[] $value
1763
     * @param string          $comparisonOp
1764
     *                                      <p>
1765
     *                                      'eq' (equals),<br />
1766
     *                                      'gt' (greater),<br />
1767
     *                                      'gte' || 'ge' (greater or equals),<br />
1768
     *                                      'lt' (less),<br />
1769
     *                                      'lte' || 'le' (less or equals),<br />
1770
     *                                      'ne' (not equals),<br />
1771
     *                                      'contains',<br />
1772
     *                                      'notContains',<br />
1773
     *                                      'newer' (via strtotime),<br />
1774
     *                                      'older' (via strtotime),<br />
1775
     *                                      </p>
1776
     *
1777
     * @return static
1778
     *                <p>(Immutable)</p>
1779
     *
1780
     * @psalm-return static<TKey,T>
1781
     */
1782 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1783
    {
1784 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...
1785 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1786
        }
1787
1788
        $ops = [
1789
            'eq' => static function ($item, $prop, $value): bool {
1790 1
                return $item[$prop] === $value;
1791 1
            },
1792
            'gt' => static function ($item, $prop, $value): bool {
1793
                return $item[$prop] > $value;
1794 1
            },
1795
            'ge' => static function ($item, $prop, $value): bool {
1796
                return $item[$prop] >= $value;
1797 1
            },
1798
            'gte' => static function ($item, $prop, $value): bool {
1799
                return $item[$prop] >= $value;
1800 1
            },
1801
            'lt' => static function ($item, $prop, $value): bool {
1802 1
                return $item[$prop] < $value;
1803 1
            },
1804
            'le' => static function ($item, $prop, $value): bool {
1805
                return $item[$prop] <= $value;
1806 1
            },
1807
            'lte' => static function ($item, $prop, $value): bool {
1808
                return $item[$prop] <= $value;
1809 1
            },
1810
            'ne' => static function ($item, $prop, $value): bool {
1811
                return $item[$prop] !== $value;
1812 1
            },
1813
            'contains' => static function ($item, $prop, $value): bool {
1814 1
                return \in_array($item[$prop], (array) $value, true);
1815 1
            },
1816
            'notContains' => static function ($item, $prop, $value): bool {
1817
                return !\in_array($item[$prop], (array) $value, true);
1818 1
            },
1819
            'newer' => static function ($item, $prop, $value): bool {
1820
                return \strtotime($item[$prop]) > \strtotime($value);
1821 1
            },
1822
            'older' => static function ($item, $prop, $value): bool {
1823
                return \strtotime($item[$prop]) < \strtotime($value);
1824 1
            },
1825
        ];
1826
1827 1
        $result = \array_values(
1828 1
            \array_filter(
1829 1
                $this->getArray(),
1830
                static function ($item) use (
1831 1
                    $property,
1832 1
                    $value,
1833 1
                    $ops,
1834 1
                    $comparisonOp
1835
                ) {
1836 1
                    $item = (array) $item;
1837 1
                    $itemArrayy = static::create($item);
1838 1
                    $item[$property] = $itemArrayy->get($property, []);
1839
1840 1
                    return $ops[$comparisonOp]($item, $property, $value);
1841 1
                }
1842
            )
1843
        );
1844
1845 1
        return static::create(
1846 1
            $result,
1847 1
            $this->iteratorClass,
1848 1
            false
1849
        );
1850
    }
1851
1852
    /**
1853
     * Find the first item in an array that passes the truth test,
1854
     *  otherwise return false
1855
     *
1856
     * @param \Closure $closure
1857
     *
1858
     * @return false|mixed
1859
     *                     <p>Return false if we did not find the value.</p>
1860
     */
1861 8 View Code Duplication
    public function find(\Closure $closure)
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...
1862
    {
1863 8
        foreach ($this->getGenerator() as $key => $value) {
1864 6
            if ($closure($value, $key)) {
1865 5
                return $value;
1866
            }
1867
        }
1868
1869 3
        return false;
1870
    }
1871
1872
    /**
1873
     * find by ...
1874
     *
1875
     * @param string          $property
1876
     * @param string|string[] $value
1877
     * @param string          $comparisonOp
1878
     *
1879
     * @return static
1880
     *                <p>(Immutable)</p>
1881
     *
1882
     * @psalm-return static<TKey,T>
1883
     */
1884 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1885
    {
1886 1
        return $this->filterBy($property, $value, $comparisonOp);
1887
    }
1888
1889
    /**
1890
     * Get the first value from the current array.
1891
     *
1892
     * @return mixed
1893
     *               <p>Return null if there wasn't a element.</p>
1894
     */
1895 21
    public function first()
1896
    {
1897 21
        $key_first = $this->firstKey();
1898 21
        if ($key_first === null) {
1899 3
            return null;
1900
        }
1901
1902 18
        return $this->get($key_first);
1903
    }
1904
1905
    /**
1906
     * Get the first key from the current array.
1907
     *
1908
     * @return mixed
1909
     *               <p>Return null if there wasn't a element.</p>
1910
     */
1911 28
    public function firstKey()
1912
    {
1913 28
        $this->generatorToArray();
1914
1915 28
        return \array_key_first($this->array);
1916
    }
1917
1918
    /**
1919
     * Get the first value(s) from the current array.
1920
     * And will return an empty array if there was no first entry.
1921
     *
1922
     * @param int|null $number <p>How many values you will take?</p>
1923
     *
1924
     * @return static
1925
     *                <p>(Immutable)</p>
1926
     *
1927
     * @psalm-return static<TKey,T>
1928
     */
1929 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...
1930
    {
1931 37
        $arrayTmp = $this->getArray();
1932
1933 37
        if ($number === null) {
1934 14
            $array = (array) \array_shift($arrayTmp);
1935
        } else {
1936 23
            $number = (int) $number;
1937 23
            $array = \array_splice($arrayTmp, 0, $number);
1938
        }
1939
1940 37
        return static::create(
1941 37
            $array,
1942 37
            $this->iteratorClass,
1943 37
            false
1944
        );
1945
    }
1946
1947
    /**
1948
     * Get the first value(s) from the current array.
1949
     * And will return an empty array if there was no first entry.
1950
     *
1951
     * @param int|null $number <p>How many values you will take?</p>
1952
     *
1953
     * @return static
1954
     *                <p>(Immutable)</p>
1955
     *
1956
     * @psalm-return static<TKey,T>
1957
     */
1958 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...
1959
    {
1960 3
        $arrayTmp = $this->keys()->getArray();
1961
1962 3
        if ($number === null) {
1963
            $array = (array) \array_shift($arrayTmp);
1964
        } else {
1965 3
            $number = (int) $number;
1966 3
            $array = \array_splice($arrayTmp, 0, $number);
1967
        }
1968
1969 3
        return static::create(
1970 3
            $array,
1971 3
            $this->iteratorClass,
1972 3
            false
1973
        );
1974
    }
1975
1976
    /**
1977
     * Get and rmove the first value(s) from the current array.
1978
     * And will return an empty array if there was no first entry.
1979
     *
1980
     * @param int|null $number <p>How many values you will take?</p>
1981
     *
1982
     * @return static
1983
     *                <p>(Mutable)</p>
1984
     *
1985
     * @psalm-return static<TKey,T>
1986
     */
1987 34
    public function firstsMutable(int $number = null): self
1988
    {
1989 34
        $this->generatorToArray();
1990
1991 34
        if ($number === null) {
1992 19
            $this->array = (array) \array_shift($this->array);
1993
        } else {
1994 15
            $number = (int) $number;
1995 15
            $this->array = \array_splice($this->array, 0, $number);
1996
        }
1997
1998 34
        return $this;
1999
    }
2000
2001
    /**
2002
     * Exchanges all keys with their associated values in an array.
2003
     *
2004
     * @return static
2005
     *                <p>(Immutable)</p>
2006
     *
2007
     * @psalm-return static<TKey,T>
2008
     */
2009 1
    public function flip(): self
2010
    {
2011 1
        return static::create(
2012 1
            \array_flip($this->getArray()),
2013 1
            $this->iteratorClass,
2014 1
            false
2015
        );
2016
    }
2017
2018
    /**
2019
     * Get a value from an array (optional using dot-notation).
2020
     *
2021
     * @param mixed $key      <p>The key to look for.</p>
2022
     * @param mixed $fallback <p>Value to fallback to.</p>
2023
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
2024
     *                        class.</p>
2025
     *
2026
     * @return mixed|static
2027
     *
2028
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2029
     */
2030 172
    public function get($key, $fallback = null, array $array = null)
2031
    {
2032 172
        if ($array !== null) {
2033 4
            $usedArray = $array;
2034
        } else {
2035 169
            $this->generatorToArray();
2036
2037 169
            $usedArray = $this->array;
2038
        }
2039
2040 172
        if ($key === null) {
2041 1
            return static::create(
2042 1
                $usedArray,
2043 1
                $this->iteratorClass,
2044 1
                false
2045
            );
2046
        }
2047
2048
        // php cast "bool"-index into "int"-index
2049 172
        if ((bool) $key === $key) {
2050 3
            $key = (int) $key;
2051
        }
2052
2053 172
        if (\array_key_exists($key, $usedArray) === true) {
2054 162
            if (\is_array($usedArray[$key]) === true) {
2055 11
                return static::create(
2056 11
                    $usedArray[$key],
2057 11
                    $this->iteratorClass,
2058 11
                    false
2059
                );
2060
            }
2061
2062 154
            return $usedArray[$key];
2063
        }
2064
2065
        // crawl through array, get key according to object or not
2066 24
        $usePath = false;
2067
        if (
2068 24
            $this->pathSeparator
2069
            &&
2070 24
            (string) $key === $key
2071
            &&
2072 24
            \strpos($key, $this->pathSeparator) !== false
2073
        ) {
2074 7
            $segments = \explode($this->pathSeparator, (string) $key);
2075 7
            if ($segments !== false) {
2076 7
                $usePath = true;
2077
2078 7
                foreach ($segments as $segment) {
2079
                    if (
2080
                        (
2081 7
                            \is_array($usedArray) === true
2082
                            ||
2083 7
                            $usedArray instanceof \ArrayAccess
2084
                        )
2085
                        &&
2086 7
                        isset($usedArray[$segment])
2087
                    ) {
2088 7
                        $usedArray = $usedArray[$segment];
2089
2090 7
                        continue;
2091
                    }
2092
2093
                    if (
2094 6
                        \is_object($usedArray) === true
2095
                        &&
2096 6
                        \property_exists($usedArray, $segment)
2097
                    ) {
2098 1
                        $usedArray = $usedArray->{$segment};
2099
2100 1
                        continue;
2101
                    }
2102
2103 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2104
                }
2105
            }
2106
        }
2107
2108 24
        if (!$usePath && !isset($usedArray[$key])) {
2109 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2110
        }
2111
2112 7
        if (\is_array($usedArray) === true) {
2113 1
            return static::create(
2114 1
                $usedArray,
2115 1
                $this->iteratorClass,
2116 1
                false
2117
            );
2118
        }
2119
2120 7
        return $usedArray;
2121
    }
2122
2123
    /**
2124
     * alias: for "Arrayy->getArray()"
2125
     *
2126
     * @return array
2127
     *
2128
     * @see          Arrayy::getArray()
2129
     *
2130
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2131
     */
2132 1
    public function getAll(): array
2133
    {
2134 1
        return $this->getArray();
2135
    }
2136
2137
    /**
2138
     * Get the current array from the "Arrayy"-object.
2139
     *
2140
     * @param bool $convertAllArrayyElements
2141
     *
2142
     * @return array
2143
     *
2144
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2145
     */
2146 859
    public function getArray($convertAllArrayyElements = false): array
2147
    {
2148
        // init
2149 859
        $array = [];
2150
2151 859
        if ($convertAllArrayyElements) {
2152 1
            foreach ($this->getGenerator() as $key => $value) {
2153 1
                if ($value instanceof self) {
2154 1
                    $value = $value->getArray(true);
2155
                }
2156
2157 1
                $array[$key] = $value;
2158
            }
2159
        } else {
2160 859
            foreach ($this->getGenerator() as $key => $value) {
2161 752
                $array[$key] = $value;
2162
            }
2163
        }
2164
2165 859
        return $array;
2166
    }
2167
2168
    /**
2169
     * Returns the values from a single column of the input array, identified by
2170
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2171
     *
2172
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2173
     * array by the values from the $indexKey column in the input array.
2174
     *
2175
     * @param mixed $columnKey
2176
     * @param mixed $indexKey
2177
     *
2178
     * @return static
2179
     *                <p>(Immutable)</p>
2180
     *
2181
     * @psalm-return static<TKey,T>
2182
     */
2183 1
    public function getColumn($columnKey = null, $indexKey = null): self
2184
    {
2185 1
        return static::create(
2186 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2187 1
            $this->iteratorClass,
2188 1
            false
2189
        );
2190
    }
2191
2192
    /**
2193
     * Get the current array from the "Arrayy"-object as generator.
2194
     *
2195
     * @return \Generator
2196
     *
2197
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2198
     */
2199 942
    public function getGenerator(): \Generator
2200
    {
2201 942
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2202 40
            yield from $this->generator;
2203
        }
2204
2205 942
        yield from $this->array;
2206 898
    }
2207
2208
    /**
2209
     * alias: for "Arrayy->keys()"
2210
     *
2211
     * @return static
2212
     *                <p>(Immutable)</p>
2213
     *
2214
     * @see          Arrayy::keys()
2215
     *
2216
     * @psalm-return static<TKey,T>
2217
     */
2218 2
    public function getKeys()
2219
    {
2220 2
        return $this->keys();
2221
    }
2222
2223
    /**
2224
     * Get the current array from the "Arrayy"-object as object.
2225
     *
2226
     * @return \stdClass
2227
     */
2228 4
    public function getObject(): \stdClass
2229
    {
2230 4
        return self::arrayToObject($this->getArray());
2231
    }
2232
2233
    /**
2234
     * alias: for "Arrayy->randomImmutable()"
2235
     *
2236
     * @return static
2237
     *                <p>(Immutable)</p>
2238
     *
2239
     * @see          Arrayy::randomImmutable()
2240
     *
2241
     * @psalm-return static<TKey,T>
2242
     */
2243 4
    public function getRandom(): self
2244
    {
2245 4
        return $this->randomImmutable();
2246
    }
2247
2248
    /**
2249
     * alias: for "Arrayy->randomKey()"
2250
     *
2251
     * @return mixed
2252
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2253
     *
2254
     * @see Arrayy::randomKey()
2255
     */
2256 3
    public function getRandomKey()
2257
    {
2258 3
        return $this->randomKey();
2259
    }
2260
2261
    /**
2262
     * alias: for "Arrayy->randomKeys()"
2263
     *
2264
     * @param int $number
2265
     *
2266
     * @return static
2267
     *                <p>(Immutable)</p>
2268
     *
2269
     * @see          Arrayy::randomKeys()
2270
     *
2271
     * @psalm-return static<TKey,T>
2272
     */
2273 8
    public function getRandomKeys(int $number): self
2274
    {
2275 8
        return $this->randomKeys($number);
2276
    }
2277
2278
    /**
2279
     * alias: for "Arrayy->randomValue()"
2280
     *
2281
     * @return mixed
2282
     *               <p>Get a random value or null if there wasn't a value.</p>
2283
     *
2284
     * @see Arrayy::randomValue()
2285
     */
2286 3
    public function getRandomValue()
2287
    {
2288 3
        return $this->randomValue();
2289
    }
2290
2291
    /**
2292
     * alias: for "Arrayy->randomValues()"
2293
     *
2294
     * @param int $number
2295
     *
2296
     * @return static
2297
     *                <p>(Immutable)</p>
2298
     *
2299
     * @see          Arrayy::randomValues()
2300
     *                            *
2301
     * @psalm-return static<TKey,T>
2302
     */
2303 6
    public function getRandomValues(int $number): self
2304
    {
2305 6
        return $this->randomValues($number);
2306
    }
2307
2308
    /**
2309
     * Gets all values.
2310
     *
2311
     * @return static
2312
     *                <p>The values of all elements in this array, in the order they
2313
     *                appear in the array.</p>
2314
     *
2315
     * @psalm-return static<TKey,T>
2316
     */
2317 4
    public function getValues()
2318
    {
2319 4
        $this->generatorToArray();
2320
2321 4
        return static::create(
2322 4
            \array_values($this->array),
2323 4
            $this->iteratorClass,
2324 4
            false
2325
        );
2326
    }
2327
2328
    /**
2329
     * Gets all values via Generator.
2330
     *
2331
     * @return \Generator
2332
     *                    <p>The values of all elements in this array, in the order they
2333
     *                    appear in the array as Generator.</p>
2334
     *
2335
     * @psalm-return \Generator<TKey,T>
2336
     */
2337 4
    public function getValuesYield(): \Generator
2338
    {
2339 4
        yield from $this->getGenerator();
2340 4
    }
2341
2342
    /**
2343
     * Group values from a array according to the results of a closure.
2344
     *
2345
     * @param callable|string $grouper  <p>A callable function name.</p>
2346
     * @param bool            $saveKeys
2347
     *
2348
     * @return static
2349
     *                <p>(Immutable)</p>
2350
     *
2351
     * @psalm-return static<TKey,T>
2352
     */
2353 4
    public function group($grouper, bool $saveKeys = false): self
2354
    {
2355
        // init
2356 4
        $result = [];
2357
2358
        // Iterate over values, group by property/results from closure.
2359 4
        foreach ($this->getGenerator() as $key => $value) {
2360 4
            if (\is_callable($grouper) === true) {
2361 3
                $groupKey = $grouper($value, $key);
2362
            } else {
2363 1
                $groupKey = $this->get($grouper);
2364
            }
2365
2366 4
            $newValue = $this->get($groupKey, null, $result);
2367
2368 4
            if ($groupKey instanceof self) {
2369
                $groupKey = $groupKey->getArray();
2370
            }
2371
2372 4
            if ($newValue instanceof self) {
2373 4
                $newValue = $newValue->getArray();
2374
            }
2375
2376
            // Add to results.
2377 4
            if ($groupKey !== null) {
2378 3
                if ($saveKeys) {
2379 2
                    $result[$groupKey] = $newValue;
2380 2
                    $result[$groupKey][$key] = $value;
2381
                } else {
2382 1
                    $result[$groupKey] = $newValue;
2383 1
                    $result[$groupKey][] = $value;
2384
                }
2385
            }
2386
        }
2387
2388 4
        return static::create(
2389 4
            $result,
2390 4
            $this->iteratorClass,
2391 4
            false
2392
        );
2393
    }
2394
2395
    /**
2396
     * Check if an array has a given key.
2397
     *
2398
     * @param mixed $key
2399
     *
2400
     * @return bool
2401
     */
2402 23
    public function has($key): bool
2403
    {
2404 23
        static $UN_FOUND = null;
2405
2406 23
        if ($UN_FOUND === null) {
2407
            // Generate unique string to use as marker.
2408 1
            $UN_FOUND = \uniqid('arrayy', true);
2409
        }
2410
2411 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2412
    }
2413
2414
    /**
2415
     * Check if an array has a given value.
2416
     *
2417
     * INFO: if you need to search recursive please use ```contains()```
2418
     *
2419
     * @param mixed $value
2420
     *
2421
     * @return bool
2422
     */
2423 1
    public function hasValue($value): bool
2424
    {
2425 1
        return $this->contains($value);
2426
    }
2427
2428
    /**
2429
     * Implodes the values of this array.
2430
     *
2431
     * @param string $glue
2432
     *
2433
     * @return string
2434
     */
2435 28
    public function implode(string $glue = ''): string
2436
    {
2437 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2438
    }
2439
2440
    /**
2441
     * Implodes the keys of this array.
2442
     *
2443
     * @param string $glue
2444
     *
2445
     * @return string
2446
     */
2447 8
    public function implodeKeys(string $glue = ''): string
2448
    {
2449 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2450
    }
2451
2452
    /**
2453
     * Given a list and an iterate-function that returns
2454
     * a key for each element in the list (or a property name),
2455
     * returns an object with an index of each item.
2456
     *
2457
     * @param mixed $key
2458
     *
2459
     * @return static
2460
     *                <p>(Immutable)</p>
2461
     *
2462
     * @psalm-return static<TKey,T>
2463
     */
2464 4
    public function indexBy($key): self
2465
    {
2466
        // init
2467 4
        $results = [];
2468
2469 4
        foreach ($this->getGenerator() as $a) {
2470 4
            if (\array_key_exists($key, $a) === true) {
2471 3
                $results[$a[$key]] = $a;
2472
            }
2473
        }
2474
2475 4
        return static::create(
2476 4
            $results,
2477 4
            $this->iteratorClass,
2478 4
            false
2479
        );
2480
    }
2481
2482
    /**
2483
     * alias: for "Arrayy->searchIndex()"
2484
     *
2485
     * @param mixed $value <p>The value to search for.</p>
2486
     *
2487
     * @return false|mixed
2488
     *
2489
     * @see Arrayy::searchIndex()
2490
     */
2491 4
    public function indexOf($value)
2492
    {
2493 4
        return $this->searchIndex($value);
2494
    }
2495
2496
    /**
2497
     * Get everything but the last..$to items.
2498
     *
2499
     * @param int $to
2500
     *
2501
     * @return static
2502
     *                <p>(Immutable)</p>
2503
     *
2504
     * @psalm-return static<TKey,T>
2505
     */
2506 12
    public function initial(int $to = 1): self
2507
    {
2508 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2509
    }
2510
2511
    /**
2512
     * Return an array with all elements found in input array.
2513
     *
2514
     * @param array $search
2515
     * @param bool  $keepKeys
2516
     *
2517
     * @return static
2518
     *                <p>(Immutable)</p>
2519
     *
2520
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
2521
     * @psalm-return static<TKey,T>
2522
     */
2523 4
    public function intersection(array $search, bool $keepKeys = false): self
2524
    {
2525 4
        if ($keepKeys) {
2526 1
            return static::create(
2527 1
                \array_uintersect(
2528 1
                    $this->getArray(),
2529 1
                    $search,
2530
                    static function ($a, $b) {
2531 1
                        return $a === $b ? 0 : -1;
2532 1
                    }
2533
                ),
2534 1
                $this->iteratorClass,
2535 1
                false
2536
            );
2537
        }
2538
2539 3
        return static::create(
2540 3
            \array_values(\array_intersect($this->getArray(), $search)),
2541 3
            $this->iteratorClass,
2542 3
            false
2543
        );
2544
    }
2545
2546
    /**
2547
     * Return an array with all elements found in input array.
2548
     *
2549
     * @param array ...$array
2550
     *
2551
     * @return static
2552
     *                <p>(Immutable)</p>
2553
     *
2554
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2555
     * @psalm-return static<TKey,T>
2556
     */
2557 1
    public function intersectionMulti(...$array): self
2558
    {
2559 1
        return static::create(
2560 1
            \array_values(\array_intersect($this->getArray(), ...$array)),
2561 1
            $this->iteratorClass,
2562 1
            false
2563
        );
2564
    }
2565
2566
    /**
2567
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2568
     *
2569
     * @param array $search
2570
     *
2571
     * @return bool
2572
     *
2573
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2574
     */
2575 1
    public function intersects(array $search): bool
2576
    {
2577 1
        return $this->intersection($search)->count() > 0;
2578
    }
2579
2580
    /**
2581
     * Invoke a function on all of an array's values.
2582
     *
2583
     * @param callable $callable
2584
     * @param mixed    $arguments
2585
     *
2586
     * @return static
2587
     *                <p>(Immutable)</p>
2588
     *
2589
     * @psalm-param  callable(T=,mixed):mixed $callable
2590
     * @psalm-return static<TKey,T>
2591
     */
2592 1
    public function invoke($callable, $arguments = []): self
2593
    {
2594
        // If one argument given for each iteration, create an array for it.
2595 1
        if (\is_array($arguments) === false) {
2596 1
            $arguments = \array_fill(
2597 1
                0,
2598 1
                $this->count(),
2599 1
                $arguments
2600
            );
2601
        }
2602
2603
        // If the callable has arguments, pass them.
2604 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...
2605 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2606
        } else {
2607 1
            $array = $this->map($callable);
2608
        }
2609
2610 1
        return static::create(
2611 1
            $array,
2612 1
            $this->iteratorClass,
2613 1
            false
2614
        );
2615
    }
2616
2617
    /**
2618
     * Check whether array is associative or not.
2619
     *
2620
     * @param bool $recursive
2621
     *
2622
     * @return bool
2623
     *              <p>Returns true if associative, false otherwise.</p>
2624
     */
2625 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...
2626
    {
2627 15
        if ($this->isEmpty()) {
2628 3
            return false;
2629
        }
2630
2631 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2632 13
            if ((string) $key !== $key) {
2633 11
                return false;
2634
            }
2635
        }
2636
2637 3
        return true;
2638
    }
2639
2640
    /**
2641
     * Check if a given key or keys are empty.
2642
     *
2643
     * @param int|int[]|string|string[]|null $keys
2644
     *
2645
     * @return bool
2646
     *              <p>Returns true if empty, false otherwise.</p>
2647
     */
2648 38
    public function isEmpty($keys = null): bool
2649
    {
2650 38
        if ($this->generator) {
2651
            return $this->getArray() === [];
2652
        }
2653
2654 38
        if ($keys === null) {
2655 38
            return $this->array === [];
2656
        }
2657
2658
        foreach ((array) $keys as $key) {
2659
            if (!empty($this->get($key))) {
2660
                return false;
2661
            }
2662
        }
2663
2664
        return true;
2665
    }
2666
2667
    /**
2668
     * Check if the current array is equal to the given "$array" or not.
2669
     *
2670
     * @param array $array
2671
     *
2672
     * @return bool
2673
     *
2674
     * @psalm-param array<mixed,mixed> $array
2675
     */
2676 1
    public function isEqual(array $array): bool
2677
    {
2678 1
        return $this->getArray() === $array;
2679
    }
2680
2681
    /**
2682
     * Check if the current array is a multi-array.
2683
     *
2684
     * @return bool
2685
     */
2686 22
    public function isMultiArray(): bool
2687
    {
2688
        return !(
2689 22
            \count($this->getArray(), \COUNT_NORMAL)
2690
            ===
2691 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2692
        );
2693
    }
2694
2695
    /**
2696
     * Check whether array is numeric or not.
2697
     *
2698
     * @return bool
2699
     *              <p>Returns true if numeric, false otherwise.</p>
2700
     */
2701 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...
2702
    {
2703 5
        if ($this->isEmpty()) {
2704 2
            return false;
2705
        }
2706
2707 4
        foreach ($this->keys()->getGenerator() as $key) {
2708 4
            if ((int) $key !== $key) {
2709 2
                return false;
2710
            }
2711
        }
2712
2713 2
        return true;
2714
    }
2715
2716
    /**
2717
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2718
     *
2719
     * @param bool $recursive
2720
     *
2721
     * @return bool
2722
     */
2723 9
    public function isSequential(bool $recursive = false): bool
2724
    {
2725
2726
        // recursive
2727
2728 9
        if ($recursive === true) {
2729
            return $this->array_keys_recursive($this->getArray())
2730
                   ===
2731
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2732
        }
2733
2734
        // non recursive
2735
2736 9
        return \array_keys($this->getArray())
2737
               ===
2738 9
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2739
    }
2740
2741
    /**
2742
     * @return array
2743
     *
2744
     * @psalm-return array<TKey,T>
2745
     */
2746
    public function jsonSerialize(): array
2747
    {
2748
        return $this->getArray();
2749
    }
2750
2751
    /**
2752
     * Gets the key/index of the element at the current internal iterator position.
2753
     *
2754
     * @return int|string|null
2755
     */
2756
    public function key()
2757
    {
2758
        return \key($this->array);
2759
    }
2760
2761
    /**
2762
     * Checks if the given key exists in the provided array.
2763
     *
2764
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2765
     *       then you need to use "Arrayy->offsetExists()".
2766
     *
2767
     * @param int|string $key the key to look for
2768
     *
2769
     * @return bool
2770
     */
2771 127
    public function keyExists($key): bool
2772
    {
2773 127
        return \array_key_exists($key, $this->array);
2774
    }
2775
2776
    /**
2777
     * Get all keys from the current array.
2778
     *
2779
     * @param bool       $recursive     [optional] <p>
2780
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2781
     *                                  </p>
2782
     * @param mixed|null $search_values [optional] <p>
2783
     *                                  If specified, then only keys containing these values are returned.
2784
     *                                  </p>
2785
     * @param bool       $strict        [optional] <p>
2786
     *                                  Determines if strict comparison (===) should be used during the search.
2787
     *                                  </p>
2788
     *
2789
     * @return static
2790
     *                <p>(Immutable) An array of all the keys in input.</p>
2791
     *
2792
     * @psalm-return static<TKey,T>
2793
     */
2794 29
    public function keys(
2795
        bool $recursive = false,
2796
        $search_values = null,
2797
        bool $strict = true
2798
    ): self {
2799
2800
        // recursive
2801
2802 29
        if ($recursive === true) {
2803 4
            $array = $this->array_keys_recursive(
2804 4
                null,
2805 4
                $search_values,
2806 4
                $strict
2807
            );
2808
2809 4
            return static::create(
2810 4
                $array,
2811 4
                $this->iteratorClass,
2812 4
                false
2813
            );
2814
        }
2815
2816
        // non recursive
2817
2818 28
        if ($search_values === null) {
2819
            $arrayFunction = function (): \Generator {
2820 28
                foreach ($this->getGenerator() as $key => $value) {
2821 26
                    yield $key;
2822
                }
2823 28
            };
2824
        } else {
2825
            $arrayFunction = function () use ($search_values, $strict): \Generator {
2826 1
                $is_array_tmp = \is_array($search_values);
2827
2828 1
                foreach ($this->getGenerator() as $key => $value) {
2829 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...
2830
                        (
2831 1
                            $is_array_tmp === false
2832
                            &&
2833 1
                            $strict === true
2834
                            &&
2835 1
                            $search_values === $value
2836
                        )
2837
                        ||
2838
                        (
2839 1
                            $is_array_tmp === false
2840
                            &&
2841 1
                            $strict === false
2842
                            &&
2843 1
                            $search_values == $value
2844
                        )
2845
                        ||
2846
                        (
2847 1
                            $is_array_tmp === true
2848
                            &&
2849 1
                            \in_array($value, $search_values, $strict)
2850
                        )
2851
                    ) {
2852 1
                        yield $key;
2853
                    }
2854
                }
2855 1
            };
2856
        }
2857
2858 28
        return static::create(
2859 28
            $arrayFunction,
2860 28
            $this->iteratorClass,
2861 28
            false
2862
        );
2863
    }
2864
2865
    /**
2866
     * Sort an array by key in reverse order.
2867
     *
2868
     * @param int $sort_flags [optional] <p>
2869
     *                        You may modify the behavior of the sort using the optional
2870
     *                        parameter sort_flags, for details
2871
     *                        see sort.
2872
     *                        </p>
2873
     *
2874
     * @return static
2875
     *                <p>(Mutable) Return this Arrayy object.</p>
2876
     *
2877
     * @psalm-return static<TKey,T>
2878
     */
2879 4
    public function krsort(int $sort_flags = 0): self
2880
    {
2881 4
        $this->generatorToArray();
2882
2883 4
        \krsort($this->array, $sort_flags);
2884
2885 4
        return $this;
2886
    }
2887
2888
    /**
2889
     * Get the last value from the current array.
2890
     *
2891
     * @return mixed
2892
     *               <p>Return null if there wasn't a element.</p>
2893
     */
2894 17
    public function last()
2895
    {
2896 17
        $key_last = $this->lastKey();
2897 17
        if ($key_last === null) {
2898 2
            return null;
2899
        }
2900
2901 15
        return $this->get($key_last);
2902
    }
2903
2904
    /**
2905
     * Get the last key from the current array.
2906
     *
2907
     * @return mixed
2908
     *               <p>Return null if there wasn't a element.</p>
2909
     */
2910 21
    public function lastKey()
2911
    {
2912 21
        $this->generatorToArray();
2913
2914 21
        return \array_key_last($this->array);
2915
    }
2916
2917
    /**
2918
     * Get the last value(s) from the current array.
2919
     *
2920
     * @param int|null $number
2921
     *
2922
     * @return static
2923
     *                <p>(Immutable)</p>
2924
     *
2925
     * @psalm-return static<TKey,T>
2926
     */
2927 13
    public function lastsImmutable(int $number = null): self
2928
    {
2929 13
        if ($this->isEmpty()) {
2930 1
            return static::create(
2931 1
                [],
2932 1
                $this->iteratorClass,
2933 1
                false
2934
            );
2935
        }
2936
2937 12
        if ($number === null) {
2938 8
            $poppedValue = $this->last();
2939
2940 8
            if ($poppedValue === null) {
2941 1
                $poppedValue = [$poppedValue];
2942
            } else {
2943 7
                $poppedValue = (array) $poppedValue;
2944
            }
2945
2946 8
            $arrayy = static::create(
2947 8
                $poppedValue,
2948 8
                $this->iteratorClass,
2949 8
                false
2950
            );
2951
        } else {
2952 4
            $number = (int) $number;
2953 4
            $arrayy = $this->rest(-$number);
2954
        }
2955
2956 12
        return $arrayy;
2957
    }
2958
2959
    /**
2960
     * Get the last value(s) from the current array.
2961
     *
2962
     * @param int|null $number
2963
     *
2964
     * @return static
2965
     *                <p>(Mutable)</p>
2966
     *
2967
     * @psalm-return static<TKey,T>
2968
     */
2969 13
    public function lastsMutable(int $number = null): self
2970
    {
2971 13
        if ($this->isEmpty()) {
2972 1
            return $this;
2973
        }
2974
2975 12
        if ($number === null) {
2976 8
            $poppedValue = $this->last();
2977
2978 8
            if ($poppedValue === null) {
2979 1
                $poppedValue = [$poppedValue];
2980
            } else {
2981 7
                $poppedValue = (array) $poppedValue;
2982
            }
2983
2984 8
            $this->array = static::create(
2985 8
                $poppedValue,
2986 8
                $this->iteratorClass,
2987 8
                false
2988 8
            )->getArray();
2989
        } else {
2990 4
            $number = (int) $number;
2991 4
            $this->array = $this->rest(-$number)->getArray();
2992
        }
2993
2994 12
        $this->generator = null;
2995
2996 12
        return $this;
2997
    }
2998
2999
    /**
3000
     * Count the values from the current array.
3001
     *
3002
     * alias: for "Arrayy->count()"
3003
     *
3004
     * @param int $mode
3005
     *
3006
     * @return int
3007
     *
3008
     * @see Arrayy::count()
3009
     */
3010 20
    public function length(int $mode = \COUNT_NORMAL): int
3011
    {
3012 20
        return $this->count($mode);
3013
    }
3014
3015
    /**
3016
     * Apply the given function to the every element of the array,
3017
     * collecting the results.
3018
     *
3019
     * @param callable $callable
3020
     * @param bool     $useKeyAsSecondParameter
3021
     * @param mixed    ...$arguments
3022
     *
3023
     * @return static
3024
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3025
     *
3026
     * @psalm-param  callable(T=,TKey=,mixed):mixed|callable(T=,mixed):mixed $callable
3027
     * @psalm-return static<TKey,T>
3028
     */
3029 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments)
3030
    {
3031 5
        $useArguments = \func_num_args() > 2;
3032
3033 5
        return static::create(
3034
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3035 5
                foreach ($this->getGenerator() as $key => $value) {
3036 4
                    if ($useArguments) {
3037 3
                        if ($useKeyAsSecondParameter) {
3038
                            yield $key => $callable($value, $key, ...$arguments);
3039
                        } else {
3040 3
                            yield $key => $callable($value, ...$arguments);
3041
                        }
3042
                    } else {
3043
                        /** @noinspection NestedPositiveIfStatementsInspection */
3044 4
                        if ($useKeyAsSecondParameter) {
3045
                            yield $key => $callable($value, $key);
3046
                        } else {
3047 4
                            yield $key => $callable($value);
3048
                        }
3049
                    }
3050
                }
3051 5
            },
3052 5
            $this->iteratorClass,
3053 5
            false
3054
        );
3055
    }
3056
3057
    /**
3058
     * Check if all items in current array match a truth test.
3059
     *
3060
     * @param \Closure $closure
3061
     *
3062
     * @return bool
3063
     */
3064 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...
3065
    {
3066 15
        if ($this->count() === 0) {
3067 2
            return false;
3068
        }
3069
3070 13
        foreach ($this->getGenerator() as $key => $value) {
3071 13
            $value = $closure($value, $key);
3072
3073 13
            if ($value === false) {
3074 7
                return false;
3075
            }
3076
        }
3077
3078 7
        return true;
3079
    }
3080
3081
    /**
3082
     * Check if any item in the current array matches a truth test.
3083
     *
3084
     * @param \Closure $closure
3085
     *
3086
     * @return bool
3087
     */
3088 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...
3089
    {
3090 14
        if ($this->count() === 0) {
3091 2
            return false;
3092
        }
3093
3094 12
        foreach ($this->getGenerator() as $key => $value) {
3095 12
            $value = $closure($value, $key);
3096
3097 12
            if ($value === true) {
3098 9
                return true;
3099
            }
3100
        }
3101
3102 4
        return false;
3103
    }
3104
3105
    /**
3106
     * Get the max value from an array.
3107
     *
3108
     * @return mixed
3109
     */
3110 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...
3111
    {
3112 10
        if ($this->count() === 0) {
3113 1
            return false;
3114
        }
3115
3116 9
        $max = false;
3117 9
        foreach ($this->getGenerator() as $value) {
3118
            if (
3119 9
                $max === false
3120
                ||
3121 9
                $value > $max
3122
            ) {
3123 9
                $max = $value;
3124
            }
3125
        }
3126
3127 9
        return $max;
3128
    }
3129
3130
    /**
3131
     * Merge the new $array into the current array.
3132
     *
3133
     * - keep key,value from the current array, also if the index is in the new $array
3134
     *
3135
     * @param array $array
3136
     * @param bool  $recursive
3137
     *
3138
     * @return static
3139
     *                <p>(Immutable)</p>
3140
     *
3141
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3142
     * @psalm-return static<TKey,T>
3143
     */
3144 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...
3145
    {
3146 25
        if ($recursive === true) {
3147 4
            $result = \array_replace_recursive($this->getArray(), $array);
3148
        } else {
3149 21
            $result = \array_replace($this->getArray(), $array);
3150
        }
3151
3152 25
        return static::create(
3153 25
            $result,
3154 25
            $this->iteratorClass,
3155 25
            false
3156
        );
3157
    }
3158
3159
    /**
3160
     * Merge the new $array into the current array.
3161
     *
3162
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3163
     * - create new indexes
3164
     *
3165
     * @param array $array
3166
     * @param bool  $recursive
3167
     *
3168
     * @return static
3169
     *                <p>(Immutable)</p>
3170
     *
3171
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3172
     * @psalm-return static<TKey,T>
3173
     */
3174 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...
3175
    {
3176 16
        if ($recursive === true) {
3177 4
            $result = \array_merge_recursive($this->getArray(), $array);
3178
        } else {
3179 12
            $result = \array_merge($this->getArray(), $array);
3180
        }
3181
3182 16
        return static::create(
3183 16
            $result,
3184 16
            $this->iteratorClass,
3185 16
            false
3186
        );
3187
    }
3188
3189
    /**
3190
     * Merge the the current array into the $array.
3191
     *
3192
     * - use key,value from the new $array, also if the index is in the current array
3193
     *
3194
     * @param array $array
3195
     * @param bool  $recursive
3196
     *
3197
     * @return static
3198
     *                <p>(Immutable)</p>
3199
     *
3200
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3201
     * @psalm-return static<TKey,T>
3202
     */
3203 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...
3204
    {
3205 16
        if ($recursive === true) {
3206 4
            $result = \array_replace_recursive($array, $this->getArray());
3207
        } else {
3208 12
            $result = \array_replace($array, $this->getArray());
3209
        }
3210
3211 16
        return static::create(
3212 16
            $result,
3213 16
            $this->iteratorClass,
3214 16
            false
3215
        );
3216
    }
3217
3218
    /**
3219
     * Merge the current array into the new $array.
3220
     *
3221
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3222
     * - create new indexes
3223
     *
3224
     * @param array $array
3225
     * @param bool  $recursive
3226
     *
3227
     * @return static
3228
     *                <p>(Immutable)</p>
3229
     *
3230
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3231
     * @psalm-return static<TKey,T>
3232
     */
3233 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...
3234
    {
3235 17
        if ($recursive === true) {
3236 4
            $result = \array_merge_recursive($array, $this->getArray());
3237
        } else {
3238 13
            $result = \array_merge($array, $this->getArray());
3239
        }
3240
3241 17
        return static::create(
3242 17
            $result,
3243 17
            $this->iteratorClass,
3244 17
            false
3245
        );
3246
    }
3247
3248
    /**
3249
     * @return ArrayyMeta|static
3250
     */
3251 15
    public static function meta()
3252
    {
3253 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3254
    }
3255
3256
    /**
3257
     * Get the min value from an array.
3258
     *
3259
     * @return mixed
3260
     */
3261 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...
3262
    {
3263 10
        if ($this->count() === 0) {
3264 1
            return false;
3265
        }
3266
3267 9
        $min = false;
3268 9
        foreach ($this->getGenerator() as $value) {
3269
            if (
3270 9
                $min === false
3271
                ||
3272 9
                $value < $min
3273
            ) {
3274 9
                $min = $value;
3275
            }
3276
        }
3277
3278 9
        return $min;
3279
    }
3280
3281
    /**
3282
     * Get the most used value from the array.
3283
     *
3284
     * @return mixed
3285
     *               <p>Return null if there wasn't a element.</p>
3286
     */
3287 3
    public function mostUsedValue()
3288
    {
3289 3
        return $this->countValues()->arsort()->firstKey();
3290
    }
3291
3292
    /**
3293
     * Get the most used value from the array.
3294
     *
3295
     * @param int|null $number <p>How many values you will take?</p>
3296
     *
3297
     * @return static
3298
     *                <p>(Immutable)</p>
3299
     *
3300
     * @psalm-return static<TKey,T>
3301
     */
3302 3
    public function mostUsedValues(int $number = null): self
3303
    {
3304 3
        return $this->countValues()->arsort()->firstsKeys($number);
3305
    }
3306
3307
    /**
3308
     * Move an array element to a new index.
3309
     *
3310
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3311
     *
3312
     * @param int|string $from
3313
     * @param int        $to
3314
     *
3315
     * @return static
3316
     *                <p>(Immutable)</p>
3317
     *
3318
     * @psalm-return static<TKey,T>
3319
     */
3320 1
    public function moveElement($from, $to): self
3321
    {
3322 1
        $array = $this->getArray();
3323
3324 1
        if ((int) $from === $from) {
3325 1
            $tmp = \array_splice($array, $from, 1);
3326 1
            \array_splice($array, (int) $to, 0, $tmp);
3327 1
            $output = $array;
3328 1
        } elseif ((string) $from === $from) {
3329 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3330 1
            $itemToMove = $array[$from];
3331 1
            if ($indexToMove !== false) {
3332 1
                \array_splice($array, $indexToMove, 1);
3333
            }
3334 1
            $i = 0;
3335 1
            $output = [];
3336 1
            foreach ($array as $key => $item) {
3337 1
                if ($i === $to) {
3338 1
                    $output[$from] = $itemToMove;
3339
                }
3340 1
                $output[$key] = $item;
3341 1
                ++$i;
3342
            }
3343
        } else {
3344
            $output = [];
3345
        }
3346
3347 1
        return static::create(
3348 1
            $output,
3349 1
            $this->iteratorClass,
3350 1
            false
3351
        );
3352
    }
3353
3354
    /**
3355
     * Move an array element to the first place.
3356
     *
3357
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3358
     *       loss the keys of an indexed array.
3359
     *
3360
     * @param int|string $key
3361
     *
3362
     * @return static
3363
     *                <p>(Immutable)</p>
3364
     *
3365
     * @psalm-return static<TKey,T>
3366
     */
3367 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...
3368
    {
3369 1
        $array = $this->getArray();
3370
3371 1
        if ($this->offsetExists($key)) {
3372 1
            $tmpValue = $this->get($key);
3373 1
            unset($array[$key]);
3374 1
            $array = [$key => $tmpValue] + $array;
3375
        }
3376
3377 1
        return static::create(
3378 1
            $array,
3379 1
            $this->iteratorClass,
3380 1
            false
3381
        );
3382
    }
3383
3384
    /**
3385
     * Move an array element to the last place.
3386
     *
3387
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3388
     *       loss the keys of an indexed array.
3389
     *
3390
     * @param int|string $key
3391
     *
3392
     * @return static
3393
     *                <p>(Immutable)</p>
3394
     *
3395
     * @psalm-return static<TKey,T>
3396
     */
3397 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...
3398
    {
3399 1
        $array = $this->getArray();
3400
3401 1
        if ($this->offsetExists($key)) {
3402 1
            $tmpValue = $this->get($key);
3403 1
            unset($array[$key]);
3404 1
            $array += [$key => $tmpValue];
3405
        }
3406
3407 1
        return static::create(
3408 1
            $array,
3409 1
            $this->iteratorClass,
3410 1
            false
3411
        );
3412
    }
3413
3414
    /**
3415
     * Moves the internal iterator position to the next element and returns this element.
3416
     *
3417
     * @return mixed
3418
     */
3419
    public function next()
3420
    {
3421
        return \next($this->array);
3422
    }
3423
3424
    /**
3425
     * Get the next nth keys and values from the array.
3426
     *
3427
     * @param int $step
3428
     * @param int $offset
3429
     *
3430
     * @return static
3431
     *                <p>(Immutable)</p>
3432
     *
3433
     * @psalm-return static<TKey,T>
3434
     */
3435 1
    public function nth(int $step, int $offset = 0): self
3436
    {
3437
        $arrayFunction = function () use ($step, $offset): \Generator {
3438 1
            $position = 0;
3439 1
            foreach ($this->getGenerator() as $key => $value) {
3440 1
                if ($position++ % $step !== $offset) {
3441 1
                    continue;
3442
                }
3443
3444 1
                yield $key => $value;
3445
            }
3446 1
        };
3447
3448 1
        return static::create(
3449 1
            $arrayFunction,
3450 1
            $this->iteratorClass,
3451 1
            false
3452
        );
3453
    }
3454
3455
    /**
3456
     * Get a subset of the items from the given array.
3457
     *
3458
     * @param mixed[] $keys
3459
     *
3460
     * @return static
3461
     *                <p>(Immutable)</p>
3462
     *
3463
     * @psalm-return static<TKey,T>
3464
     */
3465 1
    public function only(array $keys): self
3466
    {
3467 1
        $array = $this->getArray();
3468
3469 1
        return static::create(
3470 1
            \array_intersect_key($array, \array_flip($keys)),
3471 1
            $this->iteratorClass,
3472 1
            false
3473
        );
3474
    }
3475
3476
    /**
3477
     * Pad array to the specified size with a given value.
3478
     *
3479
     * @param int   $size  <p>Size of the result array.</p>
3480
     * @param mixed $value <p>Empty value by default.</p>
3481
     *
3482
     * @return static
3483
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3484
     *
3485
     * @psalm-return static<TKey,T>
3486
     */
3487 5
    public function pad(int $size, $value): self
3488
    {
3489 5
        return static::create(
3490 5
            \array_pad($this->getArray(), $size, $value),
3491 5
            $this->iteratorClass,
3492 5
            false
3493
        );
3494
    }
3495
3496
    /**
3497
     * Partitions this array in two array according to a predicate.
3498
     * Keys are preserved in the resulting array.
3499
     *
3500
     * @param \Closure $closure
3501
     *                          <p>The predicate on which to partition.</p>
3502
     *
3503
     * @return array<int, static>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (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...
3504
     *                    <p>An array with two elements. The first element contains the array
3505
     *                    of elements where the predicate returned TRUE, the second element
3506
     *                    contains the array of elements where the predicate returned FALSE.</p>
3507
     *
3508
     * @psalm-return array<int, static<mixed,T>>
3509
     */
3510 1
    public function partition(\Closure $closure): array
3511
    {
3512
        // init
3513 1
        $matches = [];
3514 1
        $noMatches = [];
3515
3516 1
        foreach ($this->array as $key => $value) {
3517 1
            if ($closure($value, $key)) {
3518 1
                $matches[$key] = $value;
3519
            } else {
3520 1
                $noMatches[$key] = $value;
3521
            }
3522
        }
3523
3524 1
        return [self::create($matches), self::create($noMatches)];
3525
    }
3526
3527
    /**
3528
     * Pop a specified value off the end of the current array.
3529
     *
3530
     * @return mixed
3531
     *               <p>(Mutable) The popped element from the current array.</p>
3532
     */
3533 5
    public function pop()
3534
    {
3535 5
        $this->generatorToArray();
3536
3537 5
        return \array_pop($this->array);
3538
    }
3539
3540
    /**
3541
     * Prepend a (key) + value to the current array.
3542
     *
3543
     * @param mixed $value
3544
     * @param mixed $key
3545
     *
3546
     * @return static
3547
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3548
     *
3549
     * @psalm-return static<TKey,T>
3550
     */
3551 11
    public function prepend($value, $key = null)
3552
    {
3553 11
        $this->generatorToArray();
3554
3555 11
        if ($this->properties !== []) {
3556 3
            $this->checkType($key, $value);
3557
        }
3558
3559 9
        if ($key === null) {
3560 8
            \array_unshift($this->array, $value);
3561
        } else {
3562 2
            $this->array = [$key => $value] + $this->array;
3563
        }
3564
3565 9
        return $this;
3566
    }
3567
3568
    /**
3569
     * Add a suffix to each key.
3570
     *
3571
     * @param mixed $suffix
3572
     *
3573
     * @return static
3574
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3575
     *
3576
     * @psalm-return static<TKey,T>
3577
     */
3578 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...
3579
    {
3580
        // init
3581 10
        $result = [];
3582
3583 10
        foreach ($this->getGenerator() as $key => $item) {
3584 9
            if ($item instanceof self) {
3585
                $result[$key] = $item->prependToEachKey($suffix);
3586 9
            } elseif (\is_array($item) === true) {
3587
                $result[$key] = self::create(
3588
                    $item,
3589
                    $this->iteratorClass,
3590
                    false
3591
                )->prependToEachKey($suffix)
3592
                    ->toArray();
3593
            } else {
3594 9
                $result[$key . $suffix] = $item;
3595
            }
3596
        }
3597
3598 10
        return self::create(
3599 10
            $result,
3600 10
            $this->iteratorClass,
3601 10
            false
3602
        );
3603
    }
3604
3605
    /**
3606
     * Add a suffix to each value.
3607
     *
3608
     * @param mixed $suffix
3609
     *
3610
     * @return static
3611
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3612
     *
3613
     * @psalm-return static<TKey,T>
3614
     */
3615 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...
3616
    {
3617
        // init
3618 10
        $result = [];
3619
3620 10
        foreach ($this->getGenerator() as $key => $item) {
3621 9
            if ($item instanceof self) {
3622
                $result[$key] = $item->prependToEachValue($suffix);
3623 9
            } elseif (\is_array($item) === true) {
3624
                $result[$key] = self::create(
3625
                    $item,
3626
                    $this->iteratorClass,
3627
                    false
3628
                )->prependToEachValue($suffix)
3629
                    ->toArray();
3630 9
            } elseif (\is_object($item) === true) {
3631 1
                $result[$key] = $item;
3632
            } else {
3633 8
                $result[$key] = $item . $suffix;
3634
            }
3635
        }
3636
3637 10
        return self::create(
3638 10
            $result,
3639 10
            $this->iteratorClass,
3640 10
            false
3641
        );
3642
    }
3643
3644
    /**
3645
     * Return the value of a given key and
3646
     * delete the key.
3647
     *
3648
     * @param int|int[]|string|string[]|null $keyOrKeys
3649
     * @param mixed                          $fallback
3650
     *
3651
     * @return mixed
3652
     */
3653 1
    public function pull($keyOrKeys = null, $fallback = null)
3654
    {
3655 1
        if ($keyOrKeys === null) {
3656
            $array = $this->getArray();
3657
            $this->clear();
3658
3659
            return $array;
3660
        }
3661
3662 1
        if (\is_array($keyOrKeys) === true) {
3663 1
            $valueOrValues = [];
3664 1
            foreach ($keyOrKeys as $key) {
3665 1
                $valueOrValues[] = $this->get($key, $fallback);
3666 1
                $this->offsetUnset($key);
3667
            }
3668
        } else {
3669 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3670 1
            $this->offsetUnset($keyOrKeys);
3671
        }
3672
3673 1
        return $valueOrValues;
3674
    }
3675
3676
    /**
3677
     * Push one or more values onto the end of array at once.
3678
     *
3679
     * @param array ...$args
3680
     *
3681
     * @return static
3682
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3683
     *
3684
     * @noinspection ReturnTypeCanBeDeclaredInspection
3685
     *
3686
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
3687
     * @psalm-return static<TKey,T>
3688
     */
3689 5
    public function push(...$args)
3690
    {
3691 5
        $this->generatorToArray();
3692
3693
        if (
3694 5
            $this->checkPropertyTypes
3695
            &&
3696 5
            $this->properties !== []
3697
        ) {
3698 1
            foreach ($args as $key => $value) {
3699 1
                $this->checkType($key, $value);
3700
            }
3701
        }
3702
3703 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...
3704
3705 5
        return $this;
3706
    }
3707
3708
    /**
3709
     * Get a random value from the current array.
3710
     *
3711
     * @param int|null $number <p>How many values you will take?</p>
3712
     *
3713
     * @return static
3714
     *                <p>(Immutable)</p>
3715
     *
3716
     * @psalm-return static<TKey,T>
3717
     */
3718 19
    public function randomImmutable(int $number = null): self
3719
    {
3720 19
        $this->generatorToArray();
3721
3722 19
        if ($this->count() === 0) {
3723 1
            return static::create(
3724 1
                [],
3725 1
                $this->iteratorClass,
3726 1
                false
3727
            );
3728
        }
3729
3730 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...
3731
            /** @noinspection NonSecureArrayRandUsageInspection */
3732 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3733
3734 13
            return static::create(
3735 13
                $arrayRandValue,
3736 13
                $this->iteratorClass,
3737 13
                false
3738
            );
3739
        }
3740
3741 6
        $arrayTmp = $this->array;
3742
        /** @noinspection NonSecureShuffleUsageInspection */
3743 6
        \shuffle($arrayTmp);
3744
3745 6
        return static::create(
3746 6
            $arrayTmp,
3747 6
            $this->iteratorClass,
3748 6
            false
3749 6
        )->firstsImmutable($number);
3750
    }
3751
3752
    /**
3753
     * Pick a random key/index from the keys of this array.
3754
     *
3755
     * @throws \RangeException If array is empty
3756
     *
3757
     * @return mixed
3758
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3759
     */
3760 4
    public function randomKey()
3761
    {
3762 4
        $result = $this->randomKeys(1);
3763
3764 4
        if (!isset($result[0])) {
3765
            $result[0] = null;
3766
        }
3767
3768 4
        return $result[0];
3769
    }
3770
3771
    /**
3772
     * Pick a given number of random keys/indexes out of this array.
3773
     *
3774
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3775
     *
3776
     * @throws \RangeException If array is empty
3777
     *
3778
     * @return static
3779
     *                <p>(Immutable)</p>
3780
     *
3781
     * @psalm-return static<TKey,T>
3782
     */
3783 13
    public function randomKeys(int $number): self
3784
    {
3785 13
        $this->generatorToArray();
3786
3787 13
        $count = $this->count();
3788
3789
        if (
3790 13
            $number === 0
3791
            ||
3792 13
            $number > $count
3793
        ) {
3794 2
            throw new \RangeException(
3795 2
                \sprintf(
3796 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3797 2
                    $number,
3798 2
                    $count
3799
                )
3800
            );
3801
        }
3802
3803 11
        $result = (array) \array_rand($this->array, $number);
3804
3805 11
        return static::create(
3806 11
            $result,
3807 11
            $this->iteratorClass,
3808 11
            false
3809
        );
3810
    }
3811
3812
    /**
3813
     * Get a random value from the current array.
3814
     *
3815
     * @param int|null $number <p>How many values you will take?</p>
3816
     *
3817
     * @return static
3818
     *                <p>(Mutable)</p>
3819
     *
3820
     * @psalm-return static<TKey,T>
3821
     */
3822 17
    public function randomMutable(int $number = null): self
3823
    {
3824 17
        $this->generatorToArray();
3825
3826 17
        if ($this->count() === 0) {
3827
            return static::create(
3828
                [],
3829
                $this->iteratorClass,
3830
                false
3831
            );
3832
        }
3833
3834 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...
3835
            /** @noinspection NonSecureArrayRandUsageInspection */
3836 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3837 7
            $this->array = $arrayRandValue;
3838
3839 7
            return $this;
3840
        }
3841
3842
        /** @noinspection NonSecureShuffleUsageInspection */
3843 11
        \shuffle($this->array);
3844
3845 11
        return $this->firstsMutable($number);
3846
    }
3847
3848
    /**
3849
     * Pick a random value from the values of this array.
3850
     *
3851
     * @return mixed
3852
     *               <p>Get a random value or null if there wasn't a value.</p>
3853
     */
3854 4
    public function randomValue()
3855
    {
3856 4
        $result = $this->randomImmutable();
3857
3858 4
        if (!isset($result[0])) {
3859
            $result[0] = null;
3860
        }
3861
3862 4
        return $result[0];
3863
    }
3864
3865
    /**
3866
     * Pick a given number of random values out of this array.
3867
     *
3868
     * @param int $number
3869
     *
3870
     * @return static
3871
     *                <p>(Mutable)</p>
3872
     *
3873
     * @psalm-return static<TKey,T>
3874
     */
3875 7
    public function randomValues(int $number): self
3876
    {
3877 7
        return $this->randomMutable($number);
3878
    }
3879
3880
    /**
3881
     * Get a random value from an array, with the ability to skew the results.
3882
     *
3883
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3884
     *
3885
     * @param array    $array
3886
     * @param int|null $number <p>How many values you will take?</p>
3887
     *
3888
     * @return static
3889
     *                <p>(Immutable)</p>
3890
     *
3891
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
3892
     * @psalm-return static<TKey,T>
3893
     */
3894 9
    public function randomWeighted(array $array, int $number = null): self
3895
    {
3896
        // init
3897 9
        $options = [];
3898
3899 9
        foreach ($array as $option => $weight) {
3900 9
            if ($this->searchIndex($option) !== false) {
3901 2
                for ($i = 0; $i < $weight; ++$i) {
3902 1
                    $options[] = $option;
3903
                }
3904
            }
3905
        }
3906
3907 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3908
    }
3909
3910
    /**
3911
     * Reduce the current array via callable e.g. anonymous-function.
3912
     *
3913
     * @param callable $callable
3914
     * @param mixed    $init
3915
     *
3916
     * @return static
3917
     *                <p>(Immutable)</p>
3918
     *
3919
     * @psalm-return static<TKey,T>
3920
     */
3921 18
    public function reduce($callable, $init = []): self
3922
    {
3923 18
        if ($this->generator) {
3924 1
            $result = $init;
3925
3926 1
            foreach ($this->getGenerator() as $value) {
3927 1
                $result = $callable($result, $value);
3928
            }
3929
3930 1
            return static::create(
3931 1
                $result,
3932 1
                $this->iteratorClass,
3933 1
                false
3934
            );
3935
        }
3936
3937 18
        $result = \array_reduce($this->array, $callable, $init);
3938
3939 18
        if ($result === null) {
3940
            $this->array = [];
3941
        } else {
3942 18
            $this->array = (array) $result;
3943
        }
3944
3945 18
        return static::create(
3946 18
            $this->array,
3947 18
            $this->iteratorClass,
3948 18
            false
3949
        );
3950
    }
3951
3952
    /**
3953
     * @param bool $unique
3954
     *
3955
     * @return static
3956
     *                <p>(Immutable)</p>
3957
     *
3958
     * @psalm-return static<TKey,T>
3959
     */
3960 14
    public function reduce_dimension(bool $unique = true): self
3961
    {
3962
        // init
3963 14
        $result = [];
3964
3965 14
        foreach ($this->getGenerator() as $val) {
3966 12
            if (\is_array($val) === true) {
3967 5
                $result[] = (new static($val))->reduce_dimension($unique)->getArray();
3968
            } else {
3969 12
                $result[] = [$val];
3970
            }
3971
        }
3972
3973 14
        $result = $result === [] ? [] : \array_merge(...$result);
3974
3975 14
        $resultArrayy = new static($result);
3976
3977 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3978
    }
3979
3980
    /**
3981
     * Create a numerically re-indexed Arrayy object.
3982
     *
3983
     * @return static
3984
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3985
     *
3986
     * @psalm-return static<TKey,T>
3987
     */
3988 9
    public function reindex(): self
3989
    {
3990 9
        $this->generatorToArray();
3991
3992 9
        $this->array = \array_values($this->array);
3993
3994 9
        return $this;
3995
    }
3996
3997
    /**
3998
     * Return all items that fail the truth test.
3999
     *
4000
     * @param \Closure $closure
4001
     *
4002
     * @return static
4003
     *                <p>(Immutable)</p>
4004
     *
4005
     * @psalm-return static<TKey,T>
4006
     */
4007 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...
4008
    {
4009
        // init
4010 1
        $filtered = [];
4011
4012 1
        foreach ($this->getGenerator() as $key => $value) {
4013 1
            if (!$closure($value, $key)) {
4014 1
                $filtered[$key] = $value;
4015
            }
4016
        }
4017
4018 1
        return static::create(
4019 1
            $filtered,
4020 1
            $this->iteratorClass,
4021 1
            false
4022
        );
4023
    }
4024
4025
    /**
4026
     * Remove a value from the current array (optional using dot-notation).
4027
     *
4028
     * @param mixed $key
4029
     *
4030
     * @return static
4031
     *                <p>(Mutable)</p>
4032
     *
4033
     * @psalm-param  TKey $key
4034
     * @psalm-return static<TKey,T>
4035
     */
4036 18
    public function remove($key)
4037
    {
4038
        // recursive call
4039 18
        if (\is_array($key) === true) {
4040
            foreach ($key as $k) {
4041
                $this->internalRemove($k);
4042
            }
4043
4044
            return static::create(
4045
                $this->getArray(),
4046
                $this->iteratorClass,
4047
                false
4048
            );
4049
        }
4050
4051 18
        $this->internalRemove($key);
4052
4053 18
        return static::create(
4054 18
            $this->getArray(),
4055 18
            $this->iteratorClass,
4056 18
            false
4057
        );
4058
    }
4059
4060
    /**
4061
     * alias: for "Arrayy->removeValue()"
4062
     *
4063
     * @param mixed $element
4064
     *
4065
     * @return static
4066
     *                <p>(Immutable)</p>
4067
     *
4068
     * @psalm-param  T $element
4069
     * @psalm-return static<TKey,T>
4070
     */
4071 8
    public function removeElement($element)
4072
    {
4073 8
        return $this->removeValue($element);
4074
    }
4075
4076
    /**
4077
     * Remove the first value from the current array.
4078
     *
4079
     * @return static
4080
     *                <p>(Immutable)</p>
4081
     *
4082
     * @psalm-return static<TKey,T>
4083
     */
4084 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...
4085
    {
4086 7
        $tmpArray = $this->getArray();
4087
4088 7
        \array_shift($tmpArray);
4089
4090 7
        return static::create(
4091 7
            $tmpArray,
4092 7
            $this->iteratorClass,
4093 7
            false
4094
        );
4095
    }
4096
4097
    /**
4098
     * Remove the last value from the current array.
4099
     *
4100
     * @return static
4101
     *                <p>(Immutable)</p>
4102
     *
4103
     * @psalm-return static<TKey,T>
4104
     */
4105 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...
4106
    {
4107 7
        $tmpArray = $this->getArray();
4108
4109 7
        \array_pop($tmpArray);
4110
4111 7
        return static::create(
4112 7
            $tmpArray,
4113 7
            $this->iteratorClass,
4114 7
            false
4115
        );
4116
    }
4117
4118
    /**
4119
     * Removes a particular value from an array (numeric or associative).
4120
     *
4121
     * @param mixed $value
4122
     *
4123
     * @return static
4124
     *                <p>(Immutable)</p>
4125
     *
4126
     * @psalm-param  T $value
4127
     * @psalm-return static<TKey,T>
4128
     */
4129 8
    public function removeValue($value): self
4130
    {
4131 8
        $this->generatorToArray();
4132
4133
        // init
4134 8
        $isSequentialArray = $this->isSequential();
4135
4136 8
        foreach ($this->array as $key => $item) {
4137 7
            if ($item === $value) {
4138 7
                unset($this->array[$key]);
4139
            }
4140
        }
4141
4142 8
        if ($isSequentialArray) {
4143 6
            $this->array = \array_values($this->array);
4144
        }
4145
4146 8
        return static::create(
4147 8
            $this->array,
4148 8
            $this->iteratorClass,
4149 8
            false
4150
        );
4151
    }
4152
4153
    /**
4154
     * Generate array of repeated arrays.
4155
     *
4156
     * @param int $times <p>How many times has to be repeated.</p>
4157
     *
4158
     * @return static
4159
     *                <p>(Immutable)</p>
4160
     *
4161
     * @psalm-return static<TKey,T>
4162
     */
4163 1
    public function repeat($times): self
4164
    {
4165 1
        if ($times === 0) {
4166 1
            return static::create([], $this->iteratorClass);
4167
        }
4168
4169 1
        return static::create(
4170 1
            \array_fill(0, (int) $times, $this->getArray()),
4171 1
            $this->iteratorClass,
4172 1
            false
4173
        );
4174
    }
4175
4176
    /**
4177
     * Replace a key with a new key/value pair.
4178
     *
4179
     * @param mixed $replace
4180
     * @param mixed $key
4181
     * @param mixed $value
4182
     *
4183
     * @return static
4184
     *                <p>(Immutable)</p>
4185
     *
4186
     * @psalm-return static<TKey,T>
4187
     */
4188 2
    public function replace($replace, $key, $value): self
4189
    {
4190 2
        $that = clone $this;
4191
4192 2
        return $that->remove($replace)
4193 2
            ->set($key, $value);
4194
    }
4195
4196
    /**
4197
     * Create an array using the current array as values and the other array as keys.
4198
     *
4199
     * @param array $keys <p>An array of keys.</p>
4200
     *
4201
     * @return static
4202
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4203
     *
4204
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4205
     * @psalm-return static<TKey,T>
4206
     */
4207 2
    public function replaceAllKeys(array $keys): self
4208
    {
4209 2
        return static::create(
4210 2
            \array_combine($keys, $this->getArray()),
4211 2
            $this->iteratorClass,
4212 2
            false
4213
        );
4214
    }
4215
4216
    /**
4217
     * Create an array using the current array as keys and the other array as values.
4218
     *
4219
     * @param array $array <p>An array o values.</p>
4220
     *
4221
     * @return static
4222
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4223
     *
4224
     * @psalm-param  array<mixed,T> $array
4225
     * @psalm-return static<TKey,T>
4226
     */
4227 2
    public function replaceAllValues(array $array): self
4228
    {
4229 2
        return static::create(
4230 2
            \array_combine($this->array, $array),
4231 2
            $this->iteratorClass,
4232 2
            false
4233
        );
4234
    }
4235
4236
    /**
4237
     * Replace the keys in an array with another set.
4238
     *
4239
     * @param array $keys <p>An array of keys matching the array's size</p>
4240
     *
4241
     * @return static
4242
     *                <p>(Immutable)</p>
4243
     *
4244
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
4245
     * @psalm-return static<TKey,T>
4246
     */
4247 1
    public function replaceKeys(array $keys): self
4248
    {
4249 1
        $values = \array_values($this->getArray());
4250 1
        $result = \array_combine($keys, $values);
4251
4252 1
        return static::create(
4253 1
            $result,
4254 1
            $this->iteratorClass,
4255 1
            false
4256
        );
4257
    }
4258
4259
    /**
4260
     * Replace the first matched value in an array.
4261
     *
4262
     * @param mixed $search      <p>The value to replace.</p>
4263
     * @param mixed $replacement <p>The value to replace.</p>
4264
     *
4265
     * @return static
4266
     *                <p>(Immutable)</p>
4267
     *
4268
     * @psalm-return static<TKey,T>
4269
     */
4270 3
    public function replaceOneValue($search, $replacement = ''): self
4271
    {
4272 3
        $array = $this->getArray();
4273 3
        $key = \array_search($search, $array, true);
4274
4275 3
        if ($key !== false) {
4276 3
            $array[$key] = $replacement;
4277
        }
4278
4279 3
        return static::create(
4280 3
            $array,
4281 3
            $this->iteratorClass,
4282 3
            false
4283
        );
4284
    }
4285
4286
    /**
4287
     * Replace values in the current array.
4288
     *
4289
     * @param mixed $search      <p>The value to replace.</p>
4290
     * @param mixed $replacement <p>What to replace it with.</p>
4291
     *
4292
     * @return static
4293
     *                <p>(Immutable)</p>
4294
     *
4295
     * @psalm-return static<TKey,T>
4296
     */
4297 1
    public function replaceValues($search, $replacement = ''): self
4298
    {
4299 1
        return $this->each(
4300
            static function ($value) use ($search, $replacement) {
4301 1
                return \str_replace($search, $replacement, $value);
4302 1
            }
4303
        );
4304
    }
4305
4306
    /**
4307
     * Get the last elements from index $from until the end of this array.
4308
     *
4309
     * @param int $from
4310
     *
4311
     * @return static
4312
     *                <p>(Immutable)</p>
4313
     *
4314
     * @psalm-return static<TKey,T>
4315
     */
4316 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...
4317
    {
4318 15
        $tmpArray = $this->getArray();
4319
4320 15
        return static::create(
4321 15
            \array_splice($tmpArray, $from),
4322 15
            $this->iteratorClass,
4323 15
            false
4324
        );
4325
    }
4326
4327
    /**
4328
     * Return the array in the reverse order.
4329
     *
4330
     * @return static
4331
     *                <p>(Mutable) Return this Arrayy object.</p>
4332
     *
4333
     * @psalm-return static<TKey,T>
4334
     */
4335 9
    public function reverse(): self
4336
    {
4337 9
        $this->generatorToArray();
4338
4339 9
        $this->array = \array_reverse($this->array);
4340
4341 9
        return $this;
4342
    }
4343
4344
    /**
4345
     * Sort an array in reverse order.
4346
     *
4347
     * @param int $sort_flags [optional] <p>
4348
     *                        You may modify the behavior of the sort using the optional
4349
     *                        parameter sort_flags, for details
4350
     *                        see sort.
4351
     *                        </p>
4352
     *
4353
     * @return static
4354
     *                <p>(Mutable) Return this Arrayy object.</p>
4355
     *
4356
     * @psalm-return static<TKey,T>
4357
     */
4358 4
    public function rsort(int $sort_flags = 0): self
4359
    {
4360 4
        $this->generatorToArray();
4361
4362 4
        \rsort($this->array, $sort_flags);
4363
4364 4
        return $this;
4365
    }
4366
4367
    /**
4368
     * Search for the first index of the current array via $value.
4369
     *
4370
     * @param mixed $value
4371
     *
4372
     * @return false|float|int|string
4373
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4374
     */
4375 21
    public function searchIndex($value)
4376
    {
4377 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4378 20
            if ($value === $valueFromArray) {
4379 10
                return $keyFromArray;
4380
            }
4381
        }
4382
4383 11
        return false;
4384
    }
4385
4386
    /**
4387
     * Search for the value of the current array via $index.
4388
     *
4389
     * @param mixed $index
4390
     *
4391
     * @return static
4392
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4393
     *
4394
     * @psalm-return static<TKey,T>
4395
     */
4396 9
    public function searchValue($index): self
4397
    {
4398 9
        $this->generatorToArray();
4399
4400
        // init
4401 9
        $return = [];
4402
4403 9
        if ($this->array === []) {
4404
            return static::create(
4405
                [],
4406
                $this->iteratorClass,
4407
                false
4408
            );
4409
        }
4410
4411
        // php cast "bool"-index into "int"-index
4412 9
        if ((bool) $index === $index) {
4413 1
            $index = (int) $index;
4414
        }
4415
4416 9
        if ($this->offsetExists($index)) {
4417 7
            $return = [$this->array[$index]];
4418
        }
4419
4420 9
        return static::create(
4421 9
            $return,
4422 9
            $this->iteratorClass,
4423 9
            false
4424
        );
4425
    }
4426
4427
    /**
4428
     * Set a value for the current array (optional using dot-notation).
4429
     *
4430
     * @param string $key   <p>The key to set.</p>
4431
     * @param mixed  $value <p>Its value.</p>
4432
     *
4433
     * @return static
4434
     *                <p>(Mutable)</p>
4435
     *
4436
     * @psalm-param  TKey $key
4437
     * @psalm-param  T $value
4438
     * @psalm-return static<TKey,T>
4439
     */
4440 18
    public function set($key, $value): self
4441
    {
4442 18
        $this->generatorToArray();
4443
4444 18
        $this->internalSet($key, $value);
4445
4446 18
        return $this;
4447
    }
4448
4449
    /**
4450
     * Get a value from a array and set it if it was not.
4451
     *
4452
     * WARNING: this method only set the value, if the $key is not already set
4453
     *
4454
     * @param mixed $key      <p>The key</p>
4455
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4456
     *
4457
     * @return mixed
4458
     *               <p>(Mutable)</p>
4459
     */
4460 11
    public function setAndGet($key, $fallback = null)
4461
    {
4462 11
        $this->generatorToArray();
4463
4464
        // If the key doesn't exist, set it.
4465 11
        if (!$this->has($key)) {
4466 4
            $this->array = $this->set($key, $fallback)->getArray();
4467
        }
4468
4469 11
        return $this->get($key);
4470
    }
4471
4472
    /**
4473
     * Shifts a specified value off the beginning of array.
4474
     *
4475
     * @return mixed
4476
     *               <p>(Mutable) A shifted element from the current array.</p>
4477
     */
4478 5
    public function shift()
4479
    {
4480 5
        $this->generatorToArray();
4481
4482 5
        return \array_shift($this->array);
4483
    }
4484
4485
    /**
4486
     * Shuffle the current array.
4487
     *
4488
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4489
     * @param array $array  [optional]
4490
     *
4491
     * @return static
4492
     *                <p>(Immutable)</p>
4493
     *
4494
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4495
     * @psalm-return static<TKey,T>
4496
     */
4497 2
    public function shuffle(bool $secure = false, array $array = null): self
4498
    {
4499 2
        if ($array === null) {
4500 2
            $array = $this->getArray();
4501
        }
4502
4503 2
        if ($secure !== true) {
4504
            /** @noinspection NonSecureShuffleUsageInspection */
4505 2
            \shuffle($array);
4506
        } else {
4507 1
            $size = \count($array, \COUNT_NORMAL);
4508 1
            $keys = \array_keys($array);
4509 1
            for ($i = $size - 1; $i > 0; --$i) {
4510
                try {
4511 1
                    $r = \random_int(0, $i);
4512
                } catch (\Exception $e) {
4513
                    /** @noinspection RandomApiMigrationInspection */
4514
                    $r = \mt_rand(0, $i);
4515
                }
4516 1
                if ($r !== $i) {
4517
                    $temp = $array[$keys[$r]];
4518
                    $array[$keys[$r]] = $array[$keys[$i]];
4519
                    $array[$keys[$i]] = $temp;
4520
                }
4521
            }
4522
4523
            // reset indices
4524 1
            $array = \array_values($array);
4525
        }
4526
4527 2
        foreach ($array as $key => $value) {
4528
            // check if recursive is needed
4529 2
            if (\is_array($value) === true) {
4530
                $array[$key] = $this->shuffle($secure, $value);
4531
            }
4532
        }
4533
4534 2
        return static::create(
4535 2
            $array,
4536 2
            $this->iteratorClass,
4537 2
            false
4538
        );
4539
    }
4540
4541
    /**
4542
     * Count the values from the current array.
4543
     *
4544
     * alias: for "Arrayy->count()"
4545
     *
4546
     * @param int $mode
4547
     *
4548
     * @return int
4549
     */
4550 20
    public function size(int $mode = \COUNT_NORMAL): int
4551
    {
4552 20
        return $this->count($mode);
4553
    }
4554
4555
    /**
4556
     * Checks whether array has exactly $size items.
4557
     *
4558
     * @param int $size
4559
     *
4560
     * @return bool
4561
     */
4562 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...
4563
    {
4564
        // init
4565 1
        $itemsTempCount = 0;
4566
4567 1
        foreach ($this->getGenerator() as $key => $value) {
4568 1
            ++$itemsTempCount;
4569 1
            if ($itemsTempCount > $size) {
4570 1
                return false;
4571
            }
4572
        }
4573
4574 1
        return $itemsTempCount === $size;
4575
    }
4576
4577
    /**
4578
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4579
     * smaller than $fromSize.
4580
     *
4581
     * @param int $fromSize
4582
     * @param int $toSize
4583
     *
4584
     * @return bool
4585
     */
4586 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4587
    {
4588 1
        if ($fromSize > $toSize) {
4589 1
            $tmp = $toSize;
4590 1
            $toSize = $fromSize;
4591 1
            $fromSize = $tmp;
4592
        }
4593
4594
        // init
4595 1
        $itemsTempCount = 0;
4596
4597 1
        foreach ($this->getGenerator() as $key => $value) {
4598 1
            ++$itemsTempCount;
4599 1
            if ($itemsTempCount > $toSize) {
4600 1
                return false;
4601
            }
4602
        }
4603
4604 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4605
    }
4606
4607
    /**
4608
     * Checks whether array has more than $size items.
4609
     *
4610
     * @param int $size
4611
     *
4612
     * @return bool
4613
     */
4614 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...
4615
    {
4616
        // init
4617 1
        $itemsTempCount = 0;
4618
4619 1
        foreach ($this->getGenerator() as $key => $value) {
4620 1
            ++$itemsTempCount;
4621 1
            if ($itemsTempCount > $size) {
4622 1
                return true;
4623
            }
4624
        }
4625
4626 1
        return $itemsTempCount > $size;
4627
    }
4628
4629
    /**
4630
     * Checks whether array has less than $size items.
4631
     *
4632
     * @param int $size
4633
     *
4634
     * @return bool
4635
     */
4636 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...
4637
    {
4638
        // init
4639 1
        $itemsTempCount = 0;
4640
4641 1
        foreach ($this->getGenerator() as $key => $value) {
4642 1
            ++$itemsTempCount;
4643 1
            if ($itemsTempCount > $size) {
4644 1
                return false;
4645
            }
4646
        }
4647
4648 1
        return $itemsTempCount < $size;
4649
    }
4650
4651
    /**
4652
     * Counts all elements in an array, or something in an object.
4653
     *
4654
     * <p>
4655
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4656
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4657
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4658
     * implemented and used in PHP.
4659
     * </p>
4660
     *
4661
     * @return int
4662
     *             <p>
4663
     *             The number of elements in var, which is
4664
     *             typically an array, since anything else will have one
4665
     *             element.
4666
     *             </p>
4667
     *             <p>
4668
     *             If var is not an array or an object with
4669
     *             implemented Countable interface,
4670
     *             1 will be returned.
4671
     *             There is one exception, if var is &null;,
4672
     *             0 will be returned.
4673
     *             </p>
4674
     *             <p>
4675
     *             Caution: count may return 0 for a variable that isn't set,
4676
     *             but it may also return 0 for a variable that has been initialized with an
4677
     *             empty array. Use isset to test if a variable is set.
4678
     *             </p>
4679
     */
4680 10
    public function sizeRecursive(): int
4681
    {
4682 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4683
    }
4684
4685
    /**
4686
     * Extract a slice of the array.
4687
     *
4688
     * @param int      $offset       <p>Slice begin index.</p>
4689
     * @param int|null $length       <p>Length of the slice.</p>
4690
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4691
     *
4692
     * @return static
4693
     *                <p>(Immutable) A slice of the original array with length $length.</p>
4694
     *
4695
     * @psalm-return static<TKey,T>
4696
     */
4697 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
4698
    {
4699 5
        return static::create(
4700 5
            \array_slice(
4701 5
                $this->getArray(),
4702 5
                $offset,
4703 5
                $length,
4704 5
                $preserveKeys
4705
            ),
4706 5
            $this->iteratorClass,
4707 5
            false
4708
        );
4709
    }
4710
4711
    /**
4712
     * Sort the current array and optional you can keep the keys.
4713
     *
4714
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4715
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4716
     *                              <strong>SORT_NATURAL</strong></p>
4717
     * @param bool       $keepKeys
4718
     *
4719
     * @return static
4720
     *                <p>(Mutable) Return this Arrayy object.</p>
4721
     *
4722
     * @psalm-return static<TKey,T>
4723
     */
4724 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4725
    {
4726 20
        $this->generatorToArray();
4727
4728 20
        return $this->sorting(
4729 20
            $this->array,
4730 20
            $direction,
4731 20
            $strategy,
4732 20
            $keepKeys
4733
        );
4734
    }
4735
4736
    /**
4737
     * Sort the current array by key.
4738
     *
4739
     * @see          http://php.net/manual/en/function.ksort.php
4740
     * @see          http://php.net/manual/en/function.krsort.php
4741
     *
4742
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4743
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4744
     *                              <strong>SORT_NATURAL</strong></p>
4745
     *
4746
     * @return static
4747
     *                <p>(Mutable) Return this Arrayy object.</p>
4748
     *
4749
     * @psalm-return static<TKey,T>
4750
     */
4751 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4752
    {
4753 18
        $this->generatorToArray();
4754
4755 18
        $this->sorterKeys($this->array, $direction, $strategy);
4756
4757 18
        return $this;
4758
    }
4759
4760
    /**
4761
     * Sort the current array by value.
4762
     *
4763
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4764
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4765
     *                              <strong>SORT_NATURAL</strong></p>
4766
     *
4767
     * @return static
4768
     *                <p>(Mutable)</p>
4769
     *
4770
     * @psalm-return static<TKey,T>
4771
     */
4772 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4773
    {
4774 1
        return $this->sort($direction, $strategy, true);
4775
    }
4776
4777
    /**
4778
     * Sort the current array by value.
4779
     *
4780
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4781
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4782
     *                              <strong>SORT_NATURAL</strong></p>
4783
     *
4784
     * @return static
4785
     *                <p>(Mutable)</p>
4786
     *
4787
     * @psalm-return static<TKey,T>
4788
     */
4789 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4790
    {
4791 1
        return $this->sort($direction, $strategy, false);
4792
    }
4793
4794
    /**
4795
     * Sort a array by value, by a closure or by a property.
4796
     *
4797
     * - If the sorter is null, the array is sorted naturally.
4798
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4799
     *
4800
     * @param callable|string|null $sorter
4801
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4802
     *                                        <strong>SORT_DESC</strong></p>
4803
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4804
     *                                        <strong>SORT_NATURAL</strong></p>
4805
     *
4806
     * @return static
4807
     *                <p>(Immutable)</p>
4808
     *
4809
     * @psalm-return static<TKey,T>
4810
     */
4811 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4812
    {
4813 1
        $array = $this->getArray();
4814 1
        $direction = $this->getDirection($direction);
4815
4816
        // Transform all values into their results.
4817 1
        if ($sorter) {
4818 1
            $arrayy = static::create(
4819 1
                $array,
4820 1
                $this->iteratorClass,
4821 1
                false
4822
            );
4823
4824 1
            $results = $arrayy->each(
4825
                function ($value) use ($sorter) {
4826 1
                    if (\is_callable($sorter) === true) {
4827 1
                        return $sorter($value);
4828
                    }
4829
4830 1
                    return $this->get($sorter);
4831 1
                }
4832
            );
4833
4834 1
            $results = $results->getArray();
4835
        } else {
4836 1
            $results = $array;
4837
        }
4838
4839
        // Sort by the results and replace by original values
4840 1
        \array_multisort($results, $direction, $strategy, $array);
4841
4842 1
        return static::create(
4843 1
            $array,
4844 1
            $this->iteratorClass,
4845 1
            false
4846
        );
4847
    }
4848
4849
    /**
4850
     * @param int      $offset
4851
     * @param int|null $length
4852
     * @param array    $replacement
4853
     *
4854
     * @return static
4855
     *                <p>(Immutable)</p>
4856
     *
4857
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
4858
     * @psalm-return static<TKey,T>
4859
     */
4860 1
    public function splice(int $offset, int $length = null, $replacement = []): self
4861
    {
4862 1
        $tmpArray = $this->getArray();
4863
4864 1
        \array_splice(
4865 1
            $tmpArray,
4866 1
            $offset,
4867 1
            $length ?? $this->count(),
4868 1
            $replacement
4869
        );
4870
4871 1
        return static::create(
4872 1
            $tmpArray,
4873 1
            $this->iteratorClass,
4874 1
            false
4875
        );
4876
    }
4877
4878
    /**
4879
     * Split an array in the given amount of pieces.
4880
     *
4881
     * @param int  $numberOfPieces
4882
     * @param bool $keepKeys
4883
     *
4884
     * @return static
4885
     *                <p>(Immutable)</p>
4886
     *
4887
     * @psalm-return static<TKey,T>
4888
     */
4889 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4890
    {
4891 1
        $this->generatorToArray();
4892
4893 1
        $count = $this->count();
4894
4895 1
        if ($count === 0) {
4896 1
            $result = [];
4897
        } else {
4898 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
4899 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4900
        }
4901
4902 1
        return static::create(
4903 1
            $result,
4904 1
            $this->iteratorClass,
4905 1
            false
4906
        );
4907
    }
4908
4909
    /**
4910
     * Stripe all empty items.
4911
     *
4912
     * @return static
4913
     *                <p>(Immutable)</p>
4914
     *
4915
     * @psalm-return static<TKey,T>
4916
     */
4917 1
    public function stripEmpty(): self
4918
    {
4919 1
        return $this->filter(
4920
            static function ($item) {
4921 1
                if ($item === null) {
4922 1
                    return false;
4923
                }
4924
4925 1
                return (bool) \trim((string) $item);
4926 1
            }
4927
        );
4928
    }
4929
4930
    /**
4931
     * Swap two values between positions by key.
4932
     *
4933
     * @param int|string $swapA <p>a key in the array</p>
4934
     * @param int|string $swapB <p>a key in the array</p>
4935
     *
4936
     * @return static
4937
     *                <p>(Immutable)</p>
4938
     *
4939
     * @psalm-return static<TKey,T>
4940
     */
4941 1
    public function swap($swapA, $swapB): self
4942
    {
4943 1
        $array = $this->getArray();
4944
4945 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4946
4947 1
        return static::create(
4948 1
            $array,
4949 1
            $this->iteratorClass,
4950 1
            false
4951
        );
4952
    }
4953
4954
    /**
4955
     * alias: for "Arrayy->getArray()"
4956
     *
4957
     * @param bool $convertAllArrayyElements
4958
     *
4959
     * @return array
4960
     *
4961
     * @see          Arrayy::getArray()
4962
     *
4963
     * @psalm-return array<array-key,mixed>
4964
     */
4965 241
    public function toArray(bool $convertAllArrayyElements = false): array
4966
    {
4967 241
        return $this->getArray($convertAllArrayyElements);
4968
    }
4969
4970
    /**
4971
     * Convert the current array to JSON.
4972
     *
4973
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4974
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4975
     *
4976
     * @return string
4977
     */
4978 7
    public function toJson(int $options = 0, int $depth = 512): string
4979
    {
4980 7
        $return = \json_encode($this->getArray(), $options, $depth);
4981 7
        if ($return === false) {
4982
            return '';
4983
        }
4984
4985 7
        return $return;
4986
    }
4987
4988
    /**
4989
     * @param string[]|null $items
4990
     * @param string[]      $helper
4991
     *
4992
     * @return static
4993
     *
4994
     * @psalm-return static<mixed,T>
4995
     */
4996 1
    public function toPermutation(array $items = null, array $helper = []): self
4997
    {
4998
        // init
4999 1
        $return = [];
5000
5001 1
        if ($items === null) {
5002 1
            $items = $this->getArray();
5003
        }
5004
5005 1
        if (empty($items)) {
5006 1
            $return[] = $helper;
5007
        } else {
5008 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
5009 1
                $new_items = $items;
5010 1
                $new_helper = $helper;
5011 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
5012
                /** @noinspection PhpSillyAssignmentInspection */
5013
                /** @var string[] $new_items */
5014 1
                $new_items = $new_items;
0 ignored issues
show
Bug introduced by
Why assign $new_items to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
5015 1
                \array_unshift($new_helper, $tmp_helper);
5016
                /** @noinspection SlowArrayOperationsInLoopInspection */
5017 1
                $return = \array_merge(
5018 1
                    $return,
5019 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
5020
                );
5021
            }
5022
        }
5023
5024 1
        return static::create(
5025 1
            $return,
5026 1
            $this->iteratorClass,
5027 1
            false
5028
        );
5029
    }
5030
5031
    /**
5032
     * Implodes array to a string with specified separator.
5033
     *
5034
     * @param string $separator [optional] <p>The element's separator.</p>
5035
     *
5036
     * @return string
5037
     *                <p>The string representation of array, separated by ",".</p>
5038
     */
5039 19
    public function toString(string $separator = ','): string
5040
    {
5041 19
        return $this->implode($separator);
5042
    }
5043
5044
    /**
5045
     * Return a duplicate free copy of the current array.
5046
     *
5047
     * @return static
5048
     *                <p>(Mutable)</p>
5049
     *
5050
     * @psalm-return static<TKey,T>
5051
     */
5052 13
    public function unique(): self
5053
    {
5054
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5055
5056 13
        $this->array = $this->reduce(
5057
            static function ($resultArray, $value) {
5058 12
                if (!\in_array($value, $resultArray, true)) {
5059 12
                    $resultArray[] = $value;
5060
                }
5061
5062 12
                return $resultArray;
5063 13
            },
5064 13
            []
5065 13
        )->getArray();
5066 13
        $this->generator = null;
5067
5068 13
        return $this;
5069
    }
5070
5071
    /**
5072
     * Return a duplicate free copy of the current array. (with the old keys)
5073
     *
5074
     * @return static
5075
     *                <p>(Mutable)</p>
5076
     *
5077
     * @psalm-return static<TKey,T>
5078
     */
5079 11
    public function uniqueKeepIndex(): self
5080
    {
5081
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
5082
5083
        // init
5084 11
        $array = $this->getArray();
5085
5086 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...
5087 11
            \array_keys($array),
5088
            static function ($resultArray, $key) use ($array) {
5089 10
                if (!\in_array($array[$key], $resultArray, true)) {
5090 10
                    $resultArray[$key] = $array[$key];
5091
                }
5092
5093 10
                return $resultArray;
5094 11
            },
5095 11
            []
5096
        );
5097 11
        $this->generator = null;
5098
5099 11
        if ($this->array === null) {
5100
            $this->array = [];
5101
        } else {
5102 11
            $this->array = (array) $this->array;
5103
        }
5104
5105 11
        return $this;
5106
    }
5107
5108
    /**
5109
     * alias: for "Arrayy->unique()"
5110
     *
5111
     * @return static
5112
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
5113
     *
5114
     * @see          Arrayy::unique()
5115
     *
5116
     * @psalm-return static<TKey,T>
5117
     */
5118 10
    public function uniqueNewIndex(): self
5119
    {
5120 10
        return $this->unique();
5121
    }
5122
5123
    /**
5124
     * Prepends one or more values to the beginning of array at once.
5125
     *
5126
     * @param array ...$args
5127
     *
5128
     * @return static
5129
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
5130
     *
5131
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
5132
     * @psalm-return static<TKey,T>
5133
     */
5134 4
    public function unshift(...$args): self
5135
    {
5136 4
        $this->generatorToArray();
5137
5138 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...
5139
5140 4
        return $this;
5141
    }
5142
5143
    /**
5144
     * Tests whether the given closure retrun something valid for all elements of this array.
5145
     *
5146
     * @param \Closure $closure the predicate
5147
     *
5148
     * @return bool
5149
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
5150
     */
5151 1 View Code Duplication
    public function validate(\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...
5152
    {
5153 1
        foreach ($this->getGenerator() as $key => $value) {
5154 1
            if (!$closure($value, $key)) {
5155 1
                return false;
5156
            }
5157
        }
5158
5159 1
        return true;
5160
    }
5161
5162
    /**
5163
     * Get all values from a array.
5164
     *
5165
     * @return static
5166
     *                <p>(Immutable)</p>
5167
     *
5168
     * @psalm-return static<mixed,T>
5169
     */
5170 2
    public function values(): self
5171
    {
5172 2
        return static::create(
5173
            function () {
5174
                /** @noinspection YieldFromCanBeUsedInspection */
5175 2
                foreach ($this->getGenerator() as $value) {
5176 2
                    yield $value;
5177
                }
5178 2
            },
5179 2
            $this->iteratorClass,
5180 2
            false
5181
        );
5182
    }
5183
5184
    /**
5185
     * Apply the given function to every element in the array, discarding the results.
5186
     *
5187
     * @param callable $callable
5188
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
5189
     *
5190
     * @return static
5191
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
5192
     *
5193
     * @psalm-return static<TKey,T>
5194
     */
5195 12
    public function walk($callable, bool $recursive = false): self
5196
    {
5197 12
        $this->generatorToArray();
5198
5199 12
        if ($recursive === true) {
5200 6
            \array_walk_recursive($this->array, $callable);
5201
        } else {
5202 6
            \array_walk($this->array, $callable);
5203
        }
5204
5205 12
        return $this;
5206
    }
5207
5208
    /**
5209
     * Returns a collection of matching items.
5210
     *
5211
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
5212
     * @param mixed  $value                 the value to match
5213
     *
5214
     * @throws \InvalidArgumentException if property or method is not defined
5215
     *
5216
     * @return static
5217
     *
5218
     * @psalm-param  T $value
5219
     * @psalm-return static<TKey,T>
5220
     */
5221 1
    public function where(string $keyOrPropertyOrMethod, $value): self
5222
    {
5223 1
        return $this->filter(
5224
            function ($item) use ($keyOrPropertyOrMethod, $value) {
5225 1
                $accessorValue = $this->extractValue(
5226 1
                    $item,
5227 1
                    $keyOrPropertyOrMethod
5228
                );
5229
5230 1
                return $accessorValue === $value;
5231 1
            }
5232
        );
5233
    }
5234
5235
    /**
5236
     * Convert an array into a object.
5237
     *
5238
     * @param array $array
5239
     *
5240
     * @return \stdClass
5241
     *
5242
     * @psalm-param array<mixed,mixed> $array
5243
     */
5244 4
    final protected static function arrayToObject(array $array = []): \stdClass
5245
    {
5246
        // init
5247 4
        $object = new \stdClass();
5248
5249 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5250 1
            return $object;
5251
        }
5252
5253 3
        foreach ($array as $name => $value) {
5254 3
            if (\is_array($value) === true) {
5255 1
                $object->{$name} = static::arrayToObject($value);
5256
            } else {
5257 3
                $object->{$name} = $value;
5258
            }
5259
        }
5260
5261 3
        return $object;
5262
    }
5263
5264
    /**
5265
     * @param array|\Generator|null $input         <p>
5266
     *                                             An array containing keys to return.
5267
     *                                             </p>
5268
     * @param mixed|null            $search_values [optional] <p>
5269
     *                                             If specified, then only keys containing these values are returned.
5270
     *                                             </p>
5271
     * @param bool                  $strict        [optional] <p>
5272
     *                                             Determines if strict comparison (===) should be used during the
5273
     *                                             search.
5274
     *                                             </p>
5275
     *
5276
     * @return array
5277
     *               <p>an array of all the keys in input</p>
5278
     *
5279
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5280
     * @psalm-return array<TKey|mixed>
5281
     */
5282 11
    protected function array_keys_recursive(
5283
        $input = null,
5284
        $search_values = null,
5285
        bool $strict = true
5286
    ): array {
5287
        // init
5288 11
        $keys = [];
5289 11
        $keysTmp = [];
5290
5291 11
        if ($input === null) {
5292 4
            $input = $this->getGenerator();
5293
        }
5294
5295 11
        if ($search_values === null) {
5296 11
            foreach ($input as $key => $value) {
5297 11
                $keys[] = $key;
5298
5299
                // check if recursive is needed
5300 11
                if (\is_array($value) === true) {
5301 4
                    $keysTmp[] = $this->array_keys_recursive($value);
5302
                }
5303
            }
5304
        } else {
5305 1
            $is_array_tmp = \is_array($search_values);
5306
5307 1
            foreach ($input as $key => $value) {
5308 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...
5309
                    (
5310 1
                        $is_array_tmp === false
5311
                        &&
5312 1
                        $strict === true
5313
                        &&
5314 1
                        $search_values === $value
5315
                    )
5316
                    ||
5317
                    (
5318 1
                        $is_array_tmp === false
5319
                        &&
5320 1
                        $strict === false
5321
                        &&
5322 1
                        $search_values == $value
5323
                    )
5324
                    ||
5325
                    (
5326 1
                        $is_array_tmp === true
5327
                        &&
5328 1
                        \in_array($value, $search_values, $strict)
5329
                    )
5330
                ) {
5331 1
                    $keys[] = $key;
5332
                }
5333
5334
                // check if recursive is needed
5335 1
                if (\is_array($value) === true) {
5336 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5337
                }
5338
            }
5339
        }
5340
5341 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5342
    }
5343
5344
    /**
5345
     * @param mixed      $path
5346
     * @param callable   $callable
5347
     * @param array|null $currentOffset
5348
     *
5349
     * @return void
5350
     *
5351
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5352
     */
5353 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5354
    {
5355 4
        $this->generatorToArray();
5356
5357 4
        if ($currentOffset === null) {
5358 4
            $currentOffset = &$this->array;
5359
        }
5360
5361 4
        $explodedPath = \explode($this->pathSeparator, $path);
5362 4
        if ($explodedPath === false) {
5363
            return;
5364
        }
5365
5366 4
        $nextPath = \array_shift($explodedPath);
5367
5368 4
        if (!isset($currentOffset[$nextPath])) {
5369
            return;
5370
        }
5371
5372 4
        if (!empty($explodedPath)) {
5373 1
            $this->callAtPath(
5374 1
                \implode($this->pathSeparator, $explodedPath),
5375 1
                $callable,
5376 1
                $currentOffset[$nextPath]
5377
            );
5378
        } else {
5379 4
            $callable($currentOffset[$nextPath]);
5380
        }
5381 4
    }
5382
5383
    /**
5384
     * Extracts the value of the given property or method from the object.
5385
     *
5386
     * @param static $object                <p>The object to extract the value from.</p>
5387
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5388
     *                                      value should be extracted.</p>
5389
     *
5390
     * @throws \InvalidArgumentException if the method or property is not defined
5391
     *
5392
     * @return mixed
5393
     *               <p>The value extracted from the specified property or method.</p>
5394
     *
5395
     * @psalm-param self<TKey,T> $object
5396
     */
5397 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5398
    {
5399 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5400 2
            $return = $object->get($keyOrPropertyOrMethod);
5401
5402 2
            if ($return instanceof self) {
5403 1
                return $return->getArray();
5404
            }
5405
5406 1
            return $return;
5407
        }
5408
5409
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5410
            return $object->{$keyOrPropertyOrMethod};
5411
        }
5412
5413
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5414
            return $object->{$keyOrPropertyOrMethod}();
5415
        }
5416
5417
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
5418
    }
5419
5420
    /**
5421
     * create a fallback for array
5422
     *
5423
     * 1. use the current array, if it's a array
5424
     * 2. fallback to empty array, if there is nothing
5425
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
5426
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
5427
     * 5. call "__toArray()" on object, if the method exists
5428
     * 6. cast a string or object with "__toString()" into an array
5429
     * 7. throw a "InvalidArgumentException"-Exception
5430
     *
5431
     * @param mixed $data
5432
     *
5433
     * @throws \InvalidArgumentException
5434
     *
5435
     * @return array
5436
     *
5437
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5438
     */
5439 1066
    protected function fallbackForArray(&$data): array
5440
    {
5441 1066
        $data = $this->internalGetArray($data);
5442
5443 1066
        if ($data === null) {
5444 2
            throw new \InvalidArgumentException('Passed value should be a array');
5445
        }
5446
5447 1064
        return $data;
5448
    }
5449
5450
    /**
5451
     * @return bool
5452
     *
5453
     * @noinspection ReturnTypeCanBeDeclaredInspection
5454
     */
5455 982
    protected function generatorToArray()
5456
    {
5457 982
        if ($this->generator) {
5458 2
            $this->array = $this->getArray();
5459 2
            $this->generator = null;
5460
5461 2
            return true;
5462
        }
5463
5464 982
        return false;
5465
    }
5466
5467
    /**
5468
     * Get correct PHP constant for direction.
5469
     *
5470
     * @param int|string $direction
5471
     *
5472
     * @return int
5473
     */
5474 39
    protected function getDirection($direction): int
5475
    {
5476 39
        if ((string) $direction === $direction) {
5477 10
            $direction = \strtolower($direction);
5478
5479 10
            if ($direction === 'desc') {
5480 2
                $direction = \SORT_DESC;
5481
            } else {
5482 8
                $direction = \SORT_ASC;
5483
            }
5484
        }
5485
5486
        if (
5487 39
            $direction !== \SORT_DESC
5488
            &&
5489 39
            $direction !== \SORT_ASC
5490
        ) {
5491
            $direction = \SORT_ASC;
5492
        }
5493
5494 39
        return $direction;
5495
    }
5496
5497
    /**
5498
     * @return TypeCheckPhpDoc[]
5499
     *
5500
     * @noinspection ReturnTypeCanBeDeclaredInspection
5501
     */
5502 16
    protected function getPropertiesFromPhpDoc()
5503
    {
5504 16
        static $PROPERTY_CACHE = [];
5505 16
        $cacheKey = 'Class::' . static::class;
5506
5507 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5508 15
            return $PROPERTY_CACHE[$cacheKey];
5509
        }
5510
5511
        // init
5512 2
        $properties = [];
5513
5514 2
        $reflector = new \ReflectionClass($this);
5515 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5516 2
        $docComment = $reflector->getDocComment();
5517 2
        if ($docComment) {
5518 2
            $docblock = $factory->create($docComment);
5519
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5520 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5521 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
5522
            }
5523
        }
5524
5525 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5526
    }
5527
5528
    /**
5529
     * @param mixed $glue
5530
     * @param mixed $pieces
5531
     * @param bool  $useKeys
5532
     *
5533
     * @return string
5534
     */
5535 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
5536
    {
5537 36
        if ($pieces instanceof self) {
5538 1
            $pieces = $pieces->getArray();
5539
        }
5540
5541 36
        if (\is_array($pieces) === true) {
5542 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
5543 36
            $pieces_count_not_zero = $pieces_count > 0;
5544
5545 36
            return \implode(
5546 36
                $glue,
5547 36
                \array_map(
5548 36
                    [$this, 'implode_recursive'],
5549 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
5550 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
5551
                )
5552
            );
5553
        }
5554
5555
        if (
5556 36
            \is_scalar($pieces) === true
5557
            ||
5558 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
5559
        ) {
5560 32
            return (string) $pieces;
5561
        }
5562
5563 8
        return '';
5564
    }
5565
5566
    /**
5567
     * @param mixed                 $needle   <p>
5568
     *                                        The searched value.
5569
     *                                        </p>
5570
     *                                        <p>
5571
     *                                        If needle is a string, the comparison is done
5572
     *                                        in a case-sensitive manner.
5573
     *                                        </p>
5574
     * @param array|\Generator|null $haystack <p>
5575
     *                                        The array.
5576
     *                                        </p>
5577
     * @param bool                  $strict   [optional] <p>
5578
     *                                        If the third parameter strict is set to true
5579
     *                                        then the in_array function will also check the
5580
     *                                        types of the
5581
     *                                        needle in the haystack.
5582
     *                                        </p>
5583
     *
5584
     * @return bool
5585
     *              <p>true if needle is found in the array, false otherwise</p>
5586
     *
5587
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
5588
     */
5589 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
5590
    {
5591 18
        if ($haystack === null) {
5592
            $haystack = $this->getGenerator();
5593
        }
5594
5595 18
        foreach ($haystack as $item) {
5596 14
            if (\is_array($item) === true) {
5597 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
5598
            } else {
5599
                /** @noinspection NestedPositiveIfStatementsInspection */
5600 14
                if ($strict === true) {
5601 14
                    $returnTmp = $item === $needle;
5602
                } else {
5603
                    $returnTmp = $item == $needle;
5604
                }
5605
            }
5606
5607 14
            if ($returnTmp === true) {
5608 10
                return true;
5609
            }
5610
        }
5611
5612 8
        return false;
5613
    }
5614
5615
    /**
5616
     * @param mixed $data
5617
     *
5618
     * @return array|null
5619
     *
5620
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
5621
     */
5622 1066
    protected function internalGetArray(&$data)
5623
    {
5624 1066
        if (\is_array($data) === true) {
5625 1063
            return $data;
5626
        }
5627
5628 52
        if (!$data) {
5629 6
            return [];
5630
        }
5631
5632 51
        if (\is_object($data) === true) {
5633 46
            if ($data instanceof \ArrayObject) {
5634 4
                return $data->getArrayCopy();
5635
            }
5636
5637 43
            if ($data instanceof \Generator) {
5638
                return static::createFromGeneratorImmutable($data)->getArray();
5639
            }
5640
5641 43
            if ($data instanceof \Traversable) {
5642
                return static::createFromObject($data)->getArray();
5643
            }
5644
5645 43
            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...
5646
                return (array) $data->jsonSerialize();
5647
            }
5648
5649 43
            if (\method_exists($data, '__toArray')) {
5650
                return (array) $data->__toArray();
5651
            }
5652
5653 43
            if (\method_exists($data, '__toString')) {
5654
                return [(string) $data];
5655
            }
5656
        }
5657
5658 48
        if (\is_callable($data)) {
5659 41
            $this->generator = new ArrayyRewindableGenerator($data);
5660
5661 41
            return [];
5662
        }
5663
5664 9
        if (\is_scalar($data)) {
5665 7
            return [$data];
5666
        }
5667
5668 2
        return null;
5669
    }
5670
5671
    /**
5672
     * Internal mechanics of remove method.
5673
     *
5674
     * @param mixed $key
5675
     *
5676
     * @return bool
5677
     */
5678 18
    protected function internalRemove($key): bool
5679
    {
5680 18
        $this->generatorToArray();
5681
5682
        if (
5683 18
            $this->pathSeparator
5684
            &&
5685 18
            (string) $key === $key
5686
            &&
5687 18
            \strpos($key, $this->pathSeparator) !== false
5688
        ) {
5689
            $path = \explode($this->pathSeparator, (string) $key);
5690
5691
            if ($path !== false) {
5692
                // crawl though the keys
5693
                while (\count($path, \COUNT_NORMAL) > 1) {
5694
                    $key = \array_shift($path);
5695
5696
                    if (!$this->has($key)) {
5697
                        return false;
5698
                    }
5699
5700
                    $this->array = &$this->array[$key];
5701
                }
5702
5703
                $key = \array_shift($path);
5704
            }
5705
        }
5706
5707 18
        unset($this->array[$key]);
5708
5709 18
        return true;
5710
    }
5711
5712
    /**
5713
     * Internal mechanic of set method.
5714
     *
5715
     * @param int|string|null $key
5716
     * @param mixed           $value
5717
     * @param bool            $checkProperties
5718
     *
5719
     * @return bool
5720
     */
5721 938
    protected function internalSet(
5722
        $key,
5723
        &$value,
5724
        bool $checkProperties = true
5725
    ): bool {
5726
        if (
5727 938
            $checkProperties === true
5728
            &&
5729 938
            $this->properties !== []
5730
        ) {
5731 87
            $this->checkType($key, $value);
5732
        }
5733
5734 936
        if ($key === null) {
5735
            return false;
5736
        }
5737
5738 936
        $this->generatorToArray();
5739
5740 936
        $array = &$this->array;
5741
5742
        if (
5743 936
            $this->pathSeparator
5744
            &&
5745 936
            (string) $key === $key
5746
            &&
5747 936
            \strpos($key, $this->pathSeparator) !== false
5748
        ) {
5749 3
            $path = \explode($this->pathSeparator, (string) $key);
5750
5751 3
            if ($path !== false) {
5752
                // crawl through the keys
5753 3
                while (\count($path, \COUNT_NORMAL) > 1) {
5754 3
                    $key = \array_shift($path);
5755
5756 3
                    $array = &$array[$key];
5757
                }
5758
5759 3
                $key = \array_shift($path);
5760
            }
5761
        }
5762
5763 936
        $array[$key] = $value;
5764
5765 936
        return true;
5766
    }
5767
5768
    /**
5769
     * Convert a object into an array.
5770
     *
5771
     * @param object $object
5772
     *
5773
     * @return mixed
5774
     */
5775 5
    protected static function objectToArray($object)
5776
    {
5777 5
        if (!\is_object($object)) {
5778 4
            return $object;
5779
        }
5780
5781 5
        if (\is_object($object)) {
5782 5
            $object = \get_object_vars($object);
5783
        }
5784
5785 5
        return \array_map(['static', 'objectToArray'], $object);
5786
    }
5787
5788
    /**
5789
     * @param array $data
5790
     * @param bool  $checkPropertiesInConstructor
5791
     *
5792
     * @return void
5793
     *
5794
     * @psalm-param array<mixed,T> $data
5795
     */
5796 1064
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
5797
    {
5798 1064
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
5799
                                        &&
5800 1064
                                        $checkPropertiesInConstructor === true;
5801
5802 1064
        if ($this->properties !== []) {
5803 78
            foreach ($data as $key => &$valueInner) {
5804 78
                $this->internalSet(
5805 78
                    $key,
5806 78
                    $valueInner,
5807 78
                    $checkPropertiesInConstructor
5808
                );
5809
            }
5810
        } else {
5811
            if (
5812 997
                $this->checkPropertyTypes === true
5813
                ||
5814 997
                $checkPropertiesInConstructor === true
5815
            ) {
5816 16
                $this->properties = $this->getPropertiesFromPhpDoc();
5817
            }
5818
5819
            if (
5820 997
                $this->checkPropertiesMismatchInConstructor === true
5821
                &&
5822 997
                \count($data) !== 0
5823
                &&
5824 997
                \count(\array_diff_key($this->properties, $data)) > 0
5825
            ) {
5826 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...
5827
            }
5828
5829 996
            foreach ($data as $key => &$valueInner) {
5830 864
                $this->internalSet(
5831 864
                    $key,
5832 864
                    $valueInner,
5833 864
                    $checkPropertiesInConstructor
5834
                );
5835
            }
5836
        }
5837 1057
    }
5838
5839
    /**
5840
     * sorting keys
5841
     *
5842
     * @param array      $elements
5843
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5844
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5845
     *                              <strong>SORT_NATURAL</strong></p>
5846
     *
5847
     * @return static
5848
     *                <p>(Mutable) Return this Arrayy object.</p>
5849
     *
5850
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
5851
     * @psalm-return static<mixed|TKey,T>
5852
     */
5853 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5854
    {
5855 18
        $direction = $this->getDirection($direction);
5856
5857 18
        switch ($direction) {
5858 18
            case 'desc':
5859
            case \SORT_DESC:
5860 6
                \krsort($elements, $strategy);
5861
5862 6
                break;
5863 13
            case 'asc':
5864 13
            case \SORT_ASC:
5865
            default:
5866 13
                \ksort($elements, $strategy);
5867
        }
5868
5869 18
        return $this;
5870
    }
5871
5872
    /**
5873
     * @param array      $elements  <p>Warning: used as reference</p>
5874
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5875
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5876
     *                              <strong>SORT_NATURAL</strong></p>
5877
     * @param bool       $keepKeys
5878
     *
5879
     * @return static
5880
     *                <p>(Mutable) Return this Arrayy object.</p>
5881
     *
5882
     * @psalm-param  array<mixed> $elements
5883
     * @psalm-return static<mixed|TKey,T>
5884
     */
5885 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5886
    {
5887 20
        $direction = $this->getDirection($direction);
5888
5889 20
        if (!$strategy) {
5890 20
            $strategy = \SORT_REGULAR;
5891
        }
5892
5893 20
        switch ($direction) {
5894 20
            case 'desc':
5895
            case \SORT_DESC:
5896 9
                if ($keepKeys) {
5897 5
                    \arsort($elements, $strategy);
5898
                } else {
5899 4
                    \rsort($elements, $strategy);
5900
                }
5901
5902 9
                break;
5903 11
            case 'asc':
5904 11
            case \SORT_ASC:
5905
            default:
5906 11
                if ($keepKeys) {
5907 4
                    \asort($elements, $strategy);
5908
                } else {
5909 7
                    \sort($elements, $strategy);
5910
                }
5911
        }
5912
5913 20
        return $this;
5914
    }
5915
5916
    /**
5917
     * @param int|string|null $key
5918
     * @param mixed           $value
5919
     *
5920
     * @return void
5921
     */
5922 87
    private function checkType($key, $value)
5923
    {
5924
        if (
5925 87
            $key !== null
5926
            &&
5927 87
            isset($this->properties[$key]) === false
5928
            &&
5929 87
            $this->checkPropertiesMismatch === true
5930
        ) {
5931
            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...
5932
        }
5933
5934 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
5935 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
5936 17
        } elseif ($key !== null && isset($this->properties[$key])) {
5937 17
            $this->properties[$key]->checkType($value);
5938
        }
5939 85
    }
5940
}
5941