Completed
Push — master ( 3f6dc1...16ccd5 )
by Lars
01:39
created

Arrayy::customSortValuesImmutable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 11
ccs 4
cts 4
cp 1
crap 1
rs 9.9
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\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 1205
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1205
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1203
        parent::__construct([], 0, $iteratorClass);
116
117 1203
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1195
        $this->setIteratorClass($iteratorClass);
120 1195
    }
121
122
    /**
123
     * @return void
124
     */
125 51
    public function __clone()
126
    {
127 51
        if (!\is_array($this->properties)) {
128
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 128
    public function &__get($key)
209
    {
210 128
        $return = $this->get($key, null, null, true);
211
212 128
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 128
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     *
231
     * @psalm-mutation-free
232
     */
233 13
    public function add($value, $key = null)
234
    {
235 13
        if ($key !== null) {
236 5
            $get = $this->get($key);
237 5
            if ($get !== null) {
238 1
                $value = \array_merge_recursive(
239 1
                    !$get instanceof self ? [$get] : $get->getArray(),
240 1
                    !\is_array($value) ? [$value] : $value
241
                );
242
            }
243
244 5
            $this->internalSet($key, $value);
245
246 4
            return $this;
247
        }
248
249 8
        return $this->append($value);
250
    }
251
252
    /**
253
     * Append a (key) + value to the current array.
254
     *
255
     * EXAMPLE: <code>
256
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
257
     * </code>
258
     *
259
     * @param mixed $value
260
     * @param mixed $key
261
     *
262
     * @return $this
263
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
264
     *
265
     * @psalm-return static<TKey,T>
266
     */
267 20
    public function append($value, $key = null): self
268
    {
269 20
        $this->generatorToArray();
270
271 20
        if ($this->properties !== []) {
272 6
            $this->checkType($key, $value);
273
        }
274
275 19
        if ($key !== null) {
276
            if (
277 2
                isset($this->array[$key])
278
                &&
279 2
                \is_array($this->array[$key])
280
            ) {
281
                $this->array[$key][] = $value;
282
            } else {
283 2
                $this->array[$key] = $value;
284
            }
285
        } else {
286 17
            $this->array[] = $value;
287
        }
288
289 19
        return $this;
290
    }
291
292
    /**
293
     * Append a (key) + value to the current array.
294
     *
295
     * EXAMPLE: <code>
296
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
297
     * </code>
298
     *
299
     * @param mixed $value
300
     * @param mixed $key
301
     *
302
     * @return $this
303
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
304
     *
305
     * @psalm-return static<TKey,T>
306
     * @psalm-mutation-free
307
     */
308 1 View Code Duplication
    public function appendImmutable($value, $key = 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...
309
    {
310
        $generator = function () use ($key, $value): \Generator {
311 1
            if ($this->properties !== []) {
312
                $this->checkType($key, $value);
313
            }
314
315
            /** @noinspection YieldFromCanBeUsedInspection - FP */
316 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
317 1
                yield $keyOld => $itemOld;
318
            }
319
320 1
            if ($key !== null) {
321
                yield $key => $value;
322
            } else {
323 1
                yield $value;
324
            }
325 1
        };
326
327 1
        return static::create(
328 1
            $generator,
329 1
            $this->iteratorClass,
330 1
            false
331
        );
332
    }
333
334
    /**
335
     * Sort the entries by value.
336
     *
337
     * @param int $sort_flags [optional] <p>
338
     *                        You may modify the behavior of the sort using the optional
339
     *                        parameter sort_flags, for details
340
     *                        see sort.
341
     *                        </p>
342
     *
343
     * @return $this
344
     *               <p>(Mutable) Return this Arrayy object.</p>
345
     *
346
     * @psalm-return static<TKey,T>
347
     */
348 4
    public function asort(int $sort_flags = 0): self
349
    {
350 4
        $this->generatorToArray();
351
352 4
        \asort($this->array, $sort_flags);
353
354 4
        return $this;
355
    }
356
357
    /**
358
     * Sort the entries by value.
359
     *
360
     * @param int $sort_flags [optional] <p>
361
     *                        You may modify the behavior of the sort using the optional
362
     *                        parameter sort_flags, for details
363
     *                        see sort.
364
     *                        </p>
365
     *
366
     * @return $this
367
     *               <p>(Immutable) Return this Arrayy object.</p>
368
     *
369
     * @psalm-return static<TKey,T>
370
     * @psalm-mutation-free
371
     */
372 4
    public function asortImmutable(int $sort_flags = 0): self
373
    {
374 4
        $that = clone $this;
375
376
        /**
377
         * @psalm-suppress ImpureMethodCall - object is already cloned
378
         */
379 4
        $that->asort($sort_flags);
380
381 4
        return $that;
382
    }
383
384
    /**
385
     * Counts all elements in an array, or something in an object.
386
     *
387
     * EXAMPLE: <code>
388
     * a([-9, -8, -7, 1.32])->count(); // 4
389
     * </code>
390
     *
391
     * <p>
392
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
393
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
394
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
395
     * implemented and used in PHP.
396
     * </p>
397
     *
398
     * @see http://php.net/manual/en/function.count.php
399
     *
400
     * @param int $mode [optional] If the optional mode parameter is set to
401
     *                  COUNT_RECURSIVE (or 1), count
402
     *                  will recursively count the array. This is particularly useful for
403
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
404
     *
405
     * @return int
406
     *             <p>
407
     *             The number of elements in var, which is
408
     *             typically an array, since anything else will have one
409
     *             element.
410
     *             </p>
411
     *             <p>
412
     *             If var is not an array or an object with
413
     *             implemented Countable interface,
414
     *             1 will be returned.
415
     *             There is one exception, if var is &null;,
416
     *             0 will be returned.
417
     *             </p>
418
     *             <p>
419
     *             Caution: count may return 0 for a variable that isn't set,
420
     *             but it may also return 0 for a variable that has been initialized with an
421
     *             empty array. Use isset to test if a variable is set.
422
     *             </p>
423
     * @psalm-mutation-free
424
     */
425 147
    public function count(int $mode = \COUNT_NORMAL): int
426
    {
427
        if (
428 147
            $this->generator
429
            &&
430 147
            $mode === \COUNT_NORMAL
431
        ) {
432 4
            return \iterator_count($this->generator);
433
        }
434
435 143
        return \count($this->toArray(), $mode);
436
    }
437
438
    /**
439
     * Exchange the array for another one.
440
     *
441
     * @param array|static $data
442
     *
443
     * @return array
444
     *
445
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
446
     * @psalm-return array<mixed,mixed>|array<TKey,T>
447
     */
448 1
    public function exchangeArray($data): array
449
    {
450 1
        $this->array = $this->fallbackForArray($data);
451
452 1
        return $this->array;
453
    }
454
455
    /**
456
     * Creates a copy of the ArrayyObject.
457
     *
458
     * @return array
459
     *
460
     * @psalm-return array<mixed,mixed>|array<TKey,T>
461
     */
462 6
    public function getArrayCopy(): array
463
    {
464 6
        $this->generatorToArray();
465
466 6
        return $this->array;
467
    }
468
469
    /**
470
     * Returns a new iterator, thus implementing the \Iterator interface.
471
     *
472
     * EXAMPLE: <code>
473
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
474
     * </code>
475
     *
476
     * @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...
477
     *                          <p>An iterator for the values in the array.</p>
478
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
479
     */
480 27
    public function getIterator(): \Iterator
481
    {
482 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
483 1
            return $this->generator;
484
        }
485
486 26
        $iterator = $this->getIteratorClass();
487
488 26
        if ($iterator === ArrayyIterator::class) {
489 26
            return new $iterator($this->toArray(), 0, static::class);
490
        }
491
492
        $return = new $iterator($this->toArray());
493
        \assert($return instanceof \Iterator);
494
495
        return $return;
496
    }
497
498
    /**
499
     * Gets the iterator classname for the ArrayObject.
500
     *
501
     * @return string
502
     *
503
     * @psalm-return class-string
504
     */
505 26
    public function getIteratorClass(): string
506
    {
507 26
        return $this->iteratorClass;
508
    }
509
510
    /**
511
     * Sort the entries by key.
512
     *
513
     * @param int $sort_flags [optional] <p>
514
     *                        You may modify the behavior of the sort using the optional
515
     *                        parameter sort_flags, for details
516
     *                        see sort.
517
     *                        </p>
518
     *
519
     * @return $this
520
     *               <p>(Mutable) Return this Arrayy object.</p>
521
     *
522
     * @psalm-return static<TKey,T>
523
     */
524 4
    public function ksort(int $sort_flags = 0): self
525
    {
526 4
        $this->generatorToArray();
527
528 4
        \ksort($this->array, $sort_flags);
529
530 4
        return $this;
531
    }
532
533
    /**
534
     * Sort the entries by key.
535
     *
536
     * @param int $sort_flags [optional] <p>
537
     *                        You may modify the behavior of the sort using the optional
538
     *                        parameter sort_flags, for details
539
     *                        see sort.
540
     *                        </p>
541
     *
542
     * @return $this
543
     *               <p>(Immutable) Return this Arrayy object.</p>
544
     *
545
     * @psalm-return static<TKey,T>
546
     */
547 4
    public function ksortImmutable(int $sort_flags = 0): self
548
    {
549 4
        $that = clone $this;
550
551
        /**
552
         * @psalm-suppress ImpureMethodCall - object is already cloned
553
         */
554 4
        $that->ksort($sort_flags);
555
556 4
        return $that;
557
    }
558
559
    /**
560
     * Sort an array using a case insensitive "natural order" algorithm.
561
     *
562
     * @return $this
563
     *               <p>(Mutable) Return this Arrayy object.</p>
564
     *
565
     * @psalm-return static<TKey,T>
566
     */
567 8
    public function natcasesort(): self
568
    {
569 8
        $this->generatorToArray();
570
571 8
        \natcasesort($this->array);
572
573 8
        return $this;
574
    }
575
576
    /**
577
     * Sort an array using a case insensitive "natural order" algorithm.
578
     *
579
     * @return $this
580
     *               <p>(Immutable) Return this Arrayy object.</p>
581
     *
582
     * @psalm-return static<TKey,T>
583
     * @psalm-mutation-free
584
     */
585 4
    public function natcasesortImmutable(): self
586
    {
587 4
        $that = clone $this;
588
589
        /**
590
         * @psalm-suppress ImpureMethodCall - object is already cloned
591
         */
592 4
        $that->natcasesort();
593
594 4
        return $that;
595
    }
596
597
    /**
598
     * Sort entries using a "natural order" algorithm.
599
     *
600
     * @return $this
601
     *               <p>(Mutable) Return this Arrayy object.</p>
602
     *
603
     * @psalm-return static<TKey,T>
604
     */
605 10
    public function natsort(): self
606
    {
607 10
        $this->generatorToArray();
608
609 10
        \natsort($this->array);
610
611 10
        return $this;
612
    }
613
614
    /**
615
     * Sort entries using a "natural order" algorithm.
616
     *
617
     * @return $this
618
     *               <p>(Immutable) Return this Arrayy object.</p>
619
     *
620
     * @psalm-return static<TKey,T>
621
     * @psalm-mutation-free
622
     */
623 4
    public function natsortImmutable(): self
624
    {
625 4
        $that = clone $this;
626
627
        /**
628
         * @psalm-suppress ImpureMethodCall - object is already cloned
629
         */
630 4
        $that->natsort();
631
632 4
        return $that;
633
    }
634
635
    /**
636
     * Whether or not an offset exists.
637
     *
638
     * @param bool|int|string $offset
639
     *
640
     * @return bool
641
     *
642
     * @noinspection PhpSillyAssignmentInspection
643
     *
644
     * @psalm-mutation-free
645
     */
646 159
    public function offsetExists($offset): bool
647
    {
648 159
        $this->generatorToArray();
649
650 159
        if ($this->array === []) {
651 8
            return false;
652
        }
653
654
        // php cast "bool"-index into "int"-index
655 153
        if ((bool) $offset === $offset) {
656 1
            $offset = (int) $offset;
657
        }
658
659
        /** @var int|string $offset - hint for phpstan */
660 153
        $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...
661
662 153
        $tmpReturn = $this->keyExists($offset);
663
664
        if (
665 153
            $tmpReturn === true
666
            ||
667 153
            \strpos((string) $offset, $this->pathSeparator) === false
668
        ) {
669 150
            return $tmpReturn;
670
        }
671
672 4
        $offsetExists = false;
673
674
        /**
675
         * https://github.com/vimeo/psalm/issues/2536
676
         *
677
         * @psalm-suppress PossiblyInvalidArgument
678
         * @psalm-suppress InvalidScalarArgument
679
         */
680 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...
681 4
            $this->pathSeparator
682
            &&
683 4
            (string) $offset === $offset
684
            &&
685 4
            \strpos($offset, $this->pathSeparator) !== false
686
        ) {
687 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
688 4
            if ($explodedPath !== false) {
689
                /** @var string $lastOffset - helper for phpstan */
690 4
                $lastOffset = \array_pop($explodedPath);
691 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
692
693
                /**
694
                 * @psalm-suppress MissingClosureReturnType
695
                 * @psalm-suppress MissingClosureParamType
696
                 */
697 4
                $this->callAtPath(
698 4
                    $containerPath,
699
                    static function ($container) use ($lastOffset, &$offsetExists) {
700 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
701 4
                    }
702
                );
703
            }
704
        }
705
706 4
        return $offsetExists;
707
    }
708
709
    /**
710
     * Returns the value at specified offset.
711
     *
712
     * @param int|string $offset
713
     *
714
     * @return mixed
715
     *               <p>Will return null if the offset did not exists.</p>
716
     */
717 128
    public function &offsetGet($offset)
718
    {
719
        // init
720 128
        $value = null;
721
722 128
        if ($this->offsetExists($offset)) {
723 126
            $value = &$this->__get($offset);
724
        }
725
726 128
        return $value;
727
    }
728
729
    /**
730
     * Assigns a value to the specified offset + check the type.
731
     *
732
     * @param int|string|null $offset
733
     * @param mixed           $value
734
     *
735
     * @return void
736
     */
737 28
    public function offsetSet($offset, $value)
738
    {
739 28
        $this->generatorToArray();
740
741 28
        if ($offset === null) {
742 7
            if ($this->properties !== []) {
743 2
                $this->checkType(null, $value);
744
            }
745
746 6
            $this->array[] = $value;
747
        } else {
748 21
            $this->internalSet(
749 21
                $offset,
750 21
                $value,
751 21
                true
752
            );
753
        }
754 27
    }
755
756
    /**
757
     * Unset an offset.
758
     *
759
     * @param int|string $offset
760
     *
761
     * @return void
762
     *              <p>(Mutable) Return nothing.</p>
763
     */
764 25
    public function offsetUnset($offset)
765
    {
766 25
        $this->generatorToArray();
767
768 25
        if ($this->array === []) {
769 6
            return;
770
        }
771
772 20
        if ($this->keyExists($offset)) {
773 13
            unset($this->array[$offset]);
774
775 13
            return;
776
        }
777
778
        /**
779
         * https://github.com/vimeo/psalm/issues/2536
780
         *
781
         * @psalm-suppress PossiblyInvalidArgument
782
         * @psalm-suppress InvalidScalarArgument
783
         */
784 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...
785 10
            $this->pathSeparator
786
            &&
787 10
            (string) $offset === $offset
788
            &&
789 10
            \strpos($offset, $this->pathSeparator) !== false
790
        ) {
791 7
            $path = \explode($this->pathSeparator, (string) $offset);
792
793 7
            if ($path !== false) {
794 7
                $pathToUnset = \array_pop($path);
795
796
                /**
797
                 * @psalm-suppress MissingClosureReturnType
798
                 * @psalm-suppress MissingClosureParamType
799
                 */
800 7
                $this->callAtPath(
801 7
                    \implode($this->pathSeparator, $path),
802
                    static function (&$offset) use ($pathToUnset) {
803 6
                        if (\is_array($offset)) {
804 5
                            unset($offset[$pathToUnset]);
805
                        } else {
806 1
                            $offset = null;
807
                        }
808 7
                    }
809
                );
810
            }
811
        }
812
813 10
        unset($this->array[$offset]);
814 10
    }
815
816
    /**
817
     * Serialize the current "Arrayy"-object.
818
     *
819
     * EXAMPLE: <code>
820
     * a([1, 4, 7])->serialize();
821
     * </code>
822
     *
823
     * @return string
824
     */
825 2
    public function serialize(): string
826
    {
827 2
        $this->generatorToArray();
828
829 2
        if (\PHP_VERSION_ID < 70400) {
830 2
            return parent::serialize();
831
        }
832
833
        return \serialize($this);
834
    }
835
836
    /**
837
     * Sets the iterator classname for the current "Arrayy"-object.
838
     *
839
     * @param string $iteratorClass
840
     *
841
     * @throws \InvalidArgumentException
842
     *
843
     * @return void
844
     *
845
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
846
     */
847 1195
    public function setIteratorClass($iteratorClass)
848
    {
849 1195
        if (\class_exists($iteratorClass)) {
850 1195
            $this->iteratorClass = $iteratorClass;
851
852 1195
            return;
853
        }
854
855
        if (\strpos($iteratorClass, '\\') === 0) {
856
            $iteratorClass = '\\' . $iteratorClass;
857
            if (\class_exists($iteratorClass)) {
858
                /**
859
                 * @psalm-suppress PropertyTypeCoercion
860
                 */
861
                $this->iteratorClass = $iteratorClass;
862
863
                return;
864
            }
865
        }
866
867
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
868
    }
869
870
    /**
871
     * Sort the entries with a user-defined comparison function and maintain key association.
872
     *
873
     * @param callable $function
874
     *
875
     * @throws \InvalidArgumentException
876
     *
877
     * @return $this
878
     *               <p>(Mutable) Return this Arrayy object.</p>
879
     *
880
     * @psalm-return static<TKey,T>
881
     */
882 8 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...
883
    {
884 8
        if (!\is_callable($function)) {
885
            throw new \InvalidArgumentException('Passed function must be callable');
886
        }
887
888 8
        $this->generatorToArray();
889
890 8
        \uasort($this->array, $function);
891
892 8
        return $this;
893
    }
894
895
    /**
896
     * Sort the entries with a user-defined comparison function and maintain key association.
897
     *
898
     * @param callable $function
899
     *
900
     * @throws \InvalidArgumentException
901
     *
902
     * @return $this
903
     *               <p>(Immutable) Return this Arrayy object.</p>
904
     *
905
     * @psalm-return static<TKey,T>
906
     * @psalm-mutation-free
907
     */
908 4
    public function uasortImmutable($function): self
909
    {
910 4
        $that = clone $this;
911
912
        /**
913
         * @psalm-suppress ImpureMethodCall - object is already cloned
914
         */
915 4
        $that->uasort($function);
916
917 4
        return $that;
918
    }
919
920
    /**
921
     * Sort the entries by keys using a user-defined comparison function.
922
     *
923
     * @param callable $function
924
     *
925
     * @throws \InvalidArgumentException
926
     *
927
     * @return static
928
     *                <p>(Mutable) Return this Arrayy object.</p>
929
     *
930
     * @psalm-return static<TKey,T>
931
     */
932 5
    public function uksort($function): self
933
    {
934 5
        return $this->customSortKeys($function);
935
    }
936
937
    /**
938
     * Sort the entries by keys using a user-defined comparison function.
939
     *
940
     * @param callable $function
941
     *
942
     * @throws \InvalidArgumentException
943
     *
944
     * @return static
945
     *                <p>(Immutable) Return this Arrayy object.</p>
946
     *
947
     * @psalm-return static<TKey,T>
948
     * @psalm-mutation-free
949
     */
950 1
    public function uksortImmutable($function): self
951
    {
952 1
        return $this->customSortKeysImmutable($function);
953
    }
954
955
    /**
956
     * Unserialize an string and return the instance of the "Arrayy"-class.
957
     *
958
     * EXAMPLE: <code>
959
     * $serialized = a([1, 4, 7])->serialize();
960
     * a()->unserialize($serialized);
961
     * </code>
962
     *
963
     * @param string $string
964
     *
965
     * @return $this
966
     *
967
     * @psalm-return static<TKey,T>
968
     */
969 2
    public function unserialize($string): self
970
    {
971 2
        if (\PHP_VERSION_ID < 70400) {
972 2
            parent::unserialize($string);
973
974 2
            return $this;
975
        }
976
977
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
978
    }
979
980
    /**
981
     * Append a (key) + values to the current array.
982
     *
983
     * EXAMPLE: <code>
984
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
985
     * </code>
986
     *
987
     * @param array $values
988
     * @param mixed $key
989
     *
990
     * @return $this
991
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
992
     *
993
     * @psalm-param  array<mixed,T> $values
994
     * @psalm-param  TKey|null $key
995
     * @psalm-return static<TKey,T>
996
     */
997 1
    public function appendArrayValues(array $values, $key = null)
998
    {
999 1
        $this->generatorToArray();
1000
1001 1
        if ($key !== null) {
1002
            if (
1003 1
                isset($this->array[$key])
1004
                &&
1005 1
                \is_array($this->array[$key])
1006
            ) {
1007 1
                foreach ($values as $value) {
1008 1
                    $this->array[$key][] = $value;
1009
                }
1010
            } else {
1011
                foreach ($values as $value) {
1012 1
                    $this->array[$key] = $value;
1013
                }
1014
            }
1015
        } else {
1016
            foreach ($values as $value) {
1017
                $this->array[] = $value;
1018
            }
1019
        }
1020
1021 1
        return $this;
1022
    }
1023
1024
    /**
1025
     * Add a suffix to each key.
1026
     *
1027
     * @param mixed $prefix
1028
     *
1029
     * @return static
1030
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1031
     *
1032
     * @psalm-return static<TKey,T>
1033
     * @psalm-mutation-free
1034
     */
1035 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...
1036
    {
1037
        // init
1038 10
        $result = [];
1039
1040 10
        foreach ($this->getGenerator() as $key => $item) {
1041 9
            if ($item instanceof self) {
1042
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1043 9
            } elseif (\is_array($item)) {
1044
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1045
                    ->appendToEachKey($prefix)
1046
                    ->toArray();
1047
            } else {
1048 9
                $result[$prefix . $key] = $item;
1049
            }
1050
        }
1051
1052 10
        return self::create($result, $this->iteratorClass, false);
1053
    }
1054
1055
    /**
1056
     * Add a prefix to each value.
1057
     *
1058
     * @param mixed $prefix
1059
     *
1060
     * @return static
1061
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1062
     *
1063
     * @psalm-return static<TKey,T>
1064
     * @psalm-mutation-free
1065
     */
1066 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...
1067
    {
1068
        // init
1069 10
        $result = [];
1070
1071 10
        foreach ($this->getGenerator() as $key => $item) {
1072 9
            if ($item instanceof self) {
1073
                $result[$key] = $item->appendToEachValue($prefix);
1074 9
            } elseif (\is_array($item)) {
1075
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1076 9
            } elseif (\is_object($item) === true) {
1077 1
                $result[$key] = $item;
1078
            } else {
1079 9
                $result[$key] = $prefix . $item;
1080
            }
1081
        }
1082
1083 10
        return self::create($result, $this->iteratorClass, false);
1084
    }
1085
1086
    /**
1087
     * Sort an array in reverse order and maintain index association.
1088
     *
1089
     * @return $this
1090
     *               <p>(Mutable) Return this Arrayy object.</p>
1091
     *
1092
     * @psalm-return static<TKey,T>
1093
     */
1094 4
    public function arsort(): self
1095
    {
1096 4
        $this->generatorToArray();
1097
1098 4
        \arsort($this->array);
1099
1100 4
        return $this;
1101
    }
1102
1103
    /**
1104
     * Sort an array in reverse order and maintain index association.
1105
     *
1106
     * @return $this
1107
     *               <p>(Immutable) Return this Arrayy object.</p>
1108
     *
1109
     * @psalm-return static<TKey,T>
1110
     * @psalm-mutation-free
1111
     */
1112 10
    public function arsortImmutable(): self
1113
    {
1114 10
        $that = clone $this;
1115
1116 10
        $that->generatorToArray();
1117
1118 10
        \arsort($that->array);
1119
1120 10
        return $that;
1121
    }
1122
1123
    /**
1124
     * Iterate over the current array and execute a callback for each loop.
1125
     *
1126
     * EXAMPLE: <code>
1127
     * $result = A::create();
1128
     * $closure = function ($value, $key) use ($result) {
1129
     *     $result[$key] = ':' . $value . ':';
1130
     * };
1131
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1132
     * </code>
1133
     *
1134
     * @param \Closure $closure
1135
     *
1136
     * @return static
1137
     *                <p>(Immutable)</p>
1138
     *
1139
     * @psalm-return static<TKey,T>
1140
     * @psalm-mutation-free
1141
     */
1142 3
    public function at(\Closure $closure): self
1143
    {
1144 3
        $that = clone $this;
1145
1146 3
        foreach ($that->getGenerator() as $key => $value) {
1147 3
            $closure($value, $key);
1148
        }
1149
1150 3
        return static::create(
1151 3
            $that->toArray(),
1152 3
            $this->iteratorClass,
1153 3
            false
1154
        );
1155
    }
1156
1157
    /**
1158
     * Returns the average value of the current array.
1159
     *
1160
     * EXAMPLE: <code>
1161
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1162
     * </code>
1163
     *
1164
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1165
     *
1166
     * @return float|int
1167
     *                   <p>The average value.</p>
1168
     * @psalm-mutation-free
1169
     */
1170 10
    public function average($decimals = 0)
1171
    {
1172 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1173
1174 10
        if (!$count) {
1175 2
            return 0;
1176
        }
1177
1178 8
        if ((int) $decimals !== $decimals) {
1179 3
            $decimals = 0;
1180
        }
1181
1182 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1183
    }
1184
1185
    /**
1186
     * Changes all keys in an array.
1187
     *
1188
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1189
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1190
     *
1191
     * @return static
1192
     *                <p>(Immutable)</p>
1193
     *
1194
     * @psalm-return static<TKey,T>
1195
     * @psalm-mutation-free
1196
     */
1197 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1198
    {
1199
        if (
1200 1
            $case !== \CASE_LOWER
1201
            &&
1202 1
            $case !== \CASE_UPPER
1203
        ) {
1204
            $case = \CASE_LOWER;
1205
        }
1206
1207 1
        $return = [];
1208 1
        foreach ($this->getGenerator() as $key => $value) {
1209 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1210
1211 1
            if ($case === \CASE_LOWER) {
1212 1
                $key = \mb_strtolower((string) $key);
1213
            } else {
1214 1
                $key = \mb_strtoupper((string) $key);
1215
            }
1216
1217 1
            $return[$key] = $value;
1218
        }
1219
1220 1
        return static::create(
1221 1
            $return,
1222 1
            $this->iteratorClass,
1223 1
            false
1224
        );
1225
    }
1226
1227
    /**
1228
     * Change the path separator of the array wrapper.
1229
     *
1230
     * By default, the separator is: "."
1231
     *
1232
     * @param string $separator <p>Separator to set.</p>
1233
     *
1234
     * @return $this
1235
     *               <p>(Mutable) Return this Arrayy object.</p>
1236
     *
1237
     * @psalm-return static<TKey,T>
1238
     */
1239 11
    public function changeSeparator($separator): self
1240
    {
1241 11
        $this->pathSeparator = $separator;
1242
1243 11
        return $this;
1244
    }
1245
1246
    /**
1247
     * Create a chunked version of the current array.
1248
     *
1249
     * EXAMPLE: <code>
1250
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1251
     * </code>
1252
     *
1253
     * @param int  $size         <p>Size of each chunk.</p>
1254
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1255
     *
1256
     * @return static
1257
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1258
     *
1259
     * @psalm-return static<TKey,T>
1260
     * @psalm-mutation-free
1261
     */
1262 5
    public function chunk($size, $preserveKeys = false): self
1263
    {
1264 5
        return static::create(
1265 5
            \array_chunk($this->toArray(), $size, $preserveKeys),
1266 5
            $this->iteratorClass,
1267 5
            false
1268
        );
1269
    }
1270
1271
    /**
1272
     * Clean all falsy values from the current array.
1273
     *
1274
     * EXAMPLE: <code>
1275
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1276
     * </code>
1277
     *
1278
     * @return static
1279
     *                <p>(Immutable)</p>
1280
     *
1281
     * @psalm-return static<TKey,T>
1282
     * @psalm-mutation-free
1283
     */
1284 8
    public function clean(): self
1285
    {
1286 8
        return $this->filter(
1287
            static function ($value) {
1288 7
                return (bool) $value;
1289 8
            }
1290
        );
1291
    }
1292
1293
    /**
1294
     * WARNING!!! -> Clear the current full array or a $key of it.
1295
     *
1296
     * EXAMPLE: <code>
1297
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1298
     * </code>
1299
     *
1300
     * @param int|int[]|string|string[]|null $key
1301
     *
1302
     * @return $this
1303
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1304
     *
1305
     * @psalm-return static<TKey,T>
1306
     */
1307 10
    public function clear($key = null): self
1308
    {
1309 10
        if ($key !== null) {
1310 3
            if (\is_array($key)) {
1311 1
                foreach ($key as $keyTmp) {
1312 1
                    $this->offsetUnset($keyTmp);
1313
                }
1314
            } else {
1315 2
                $this->offsetUnset($key);
1316
            }
1317
1318 3
            return $this;
1319
        }
1320
1321 7
        $this->array = [];
1322 7
        $this->generator = null;
1323
1324 7
        return $this;
1325
    }
1326
1327
    /**
1328
     * Check if an item is in the current array.
1329
     *
1330
     * EXAMPLE: <code>
1331
     * a([1, true])->contains(true); // true
1332
     * </code>
1333
     *
1334
     * @param float|int|string $value
1335
     * @param bool             $recursive
1336
     * @param bool             $strict
1337
     *
1338
     * @return bool
1339
     * @psalm-mutation-free
1340
     */
1341 24
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1342
    {
1343 24
        if ($recursive === true) {
1344 19
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1345
        }
1346
1347
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1348 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1349 11
            if ($strict) {
1350 11
                if ($value === $valueFromArray) {
1351 11
                    return true;
1352
                }
1353
            } else {
1354
                /** @noinspection NestedPositiveIfStatementsInspection */
1355
                if ($value == $valueFromArray) {
1356 7
                    return true;
1357
                }
1358
            }
1359
        }
1360
1361 7
        return false;
1362
    }
1363
1364
    /**
1365
     * Check if an (case-insensitive) string is in the current array.
1366
     *
1367
     * EXAMPLE: <code>
1368
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1369
     * </code>
1370
     *
1371
     * @param mixed $value
1372
     * @param bool  $recursive
1373
     *
1374
     * @return bool
1375
     * @psalm-mutation-free
1376
     *
1377
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1378
     */
1379 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1380
    {
1381 26
        if ($value === null) {
1382 2
            return false;
1383
        }
1384
1385 24
        if ($recursive === true) {
1386
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1387 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1388 22
                if (\is_array($valueTmp)) {
1389 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1390 5
                    if ($return === true) {
1391 5
                        return $return;
1392
                    }
1393 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1394 22
                    return true;
1395
                }
1396
            }
1397
1398 8
            return false;
1399
        }
1400
1401
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1402 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1403 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1404 11
                return true;
1405
            }
1406
        }
1407
1408 4
        return false;
1409
    }
1410
1411
    /**
1412
     * Check if the given key/index exists in the array.
1413
     *
1414
     * EXAMPLE: <code>
1415
     * a([1 => true])->containsKey(1); // true
1416
     * </code>
1417
     *
1418
     * @param int|string $key <p>key/index to search for</p>
1419
     *
1420
     * @return bool
1421
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1422
     *
1423
     * @psalm-mutation-free
1424
     */
1425 4
    public function containsKey($key): bool
1426
    {
1427 4
        return $this->offsetExists($key);
1428
    }
1429
1430
    /**
1431
     * Check if all given needles are present in the array as key/index.
1432
     *
1433
     * EXAMPLE: <code>
1434
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1435
     * </code>
1436
     *
1437
     * @param array $needles   <p>The keys you are searching for.</p>
1438
     * @param bool  $recursive
1439
     *
1440
     * @return bool
1441
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1442
     *
1443
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1444
     * @psalm-mutation-free
1445
     */
1446 2
    public function containsKeys(array $needles, $recursive = false): bool
1447
    {
1448 2
        if ($recursive === true) {
1449
            return
1450 2
                \count(
1451 2
                    \array_intersect(
1452 2
                        $needles,
1453 2
                        $this->keys(true)->toArray()
1454
                    ),
1455 2
                    \COUNT_RECURSIVE
1456
                )
1457
                ===
1458 2
                \count(
1459 2
                    $needles,
1460 2
                    \COUNT_RECURSIVE
1461
                );
1462
        }
1463
1464 1
        return \count(
1465 1
            \array_intersect($needles, $this->keys()->toArray()),
1466 1
            \COUNT_NORMAL
1467
        )
1468
                ===
1469 1
                \count(
1470 1
                    $needles,
1471 1
                    \COUNT_NORMAL
1472
                );
1473
    }
1474
1475
    /**
1476
     * Check if all given needles are present in the array as key/index.
1477
     *
1478
     * @param array $needles <p>The keys you are searching for.</p>
1479
     *
1480
     * @return bool
1481
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1482
     *
1483
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1484
     * @psalm-mutation-free
1485
     */
1486 1
    public function containsKeysRecursive(array $needles): bool
1487
    {
1488 1
        return $this->containsKeys($needles, true);
1489
    }
1490
1491
    /**
1492
     * alias: for "Arrayy->contains()"
1493
     *
1494
     * @param float|int|string $value
1495
     *
1496
     * @return bool
1497
     *
1498
     * @see Arrayy::contains()
1499
     * @psalm-mutation-free
1500
     */
1501 9
    public function containsValue($value): bool
1502
    {
1503 9
        return $this->contains($value);
1504
    }
1505
1506
    /**
1507
     * alias: for "Arrayy->contains($value, true)"
1508
     *
1509
     * @param float|int|string $value
1510
     *
1511
     * @return bool
1512
     *
1513
     * @see Arrayy::contains()
1514
     * @psalm-mutation-free
1515
     */
1516 18
    public function containsValueRecursive($value): bool
1517
    {
1518 18
        return $this->contains($value, true);
1519
    }
1520
1521
    /**
1522
     * Check if all given needles are present in the array.
1523
     *
1524
     * EXAMPLE: <code>
1525
     * a([1, true])->containsValues(array(1, true)); // true
1526
     * </code>
1527
     *
1528
     * @param array $needles
1529
     *
1530
     * @return bool
1531
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1532
     *
1533
     * @psalm-param array<mixed>|array<T> $needles
1534
     * @psalm-mutation-free
1535
     */
1536 1
    public function containsValues(array $needles): bool
1537
    {
1538 1
        return \count(
1539 1
            \array_intersect(
1540 1
                $needles,
1541 1
                $this->toArray()
1542
            ),
1543 1
            \COUNT_NORMAL
1544
        )
1545
               ===
1546 1
               \count(
1547 1
                   $needles,
1548 1
                   \COUNT_NORMAL
1549
               );
1550
    }
1551
1552
    /**
1553
     * Counts all the values of an array
1554
     *
1555
     * @see          http://php.net/manual/en/function.array-count-values.php
1556
     *
1557
     * @return static
1558
     *                <p>
1559
     *                (Immutable)
1560
     *                An associative Arrayy-object of values from input as
1561
     *                keys and their count as value.
1562
     *                </p>
1563
     *
1564
     * @psalm-return static<TKey,T>
1565
     * @psalm-mutation-free
1566
     */
1567 7
    public function countValues(): self
1568
    {
1569 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1570
    }
1571
1572
    /**
1573
     * Creates an Arrayy object.
1574
     *
1575
     * @param mixed  $data
1576
     * @param string $iteratorClass
1577
     * @param bool   $checkPropertiesInConstructor
1578
     *
1579
     * @return static
1580
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1581
     *
1582
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1583
     *
1584
     * @psalm-mutation-free
1585
     */
1586 723
    public static function create(
1587
        $data = [],
1588
        string $iteratorClass = ArrayyIterator::class,
1589
        bool $checkPropertiesInConstructor = true
1590
    ) {
1591 723
        return new static(
1592 723
            $data,
1593 723
            $iteratorClass,
1594 723
            $checkPropertiesInConstructor
1595
        );
1596
    }
1597
1598
    /**
1599
     * Flatten an array with the given character as a key delimiter
1600
     *
1601
     * @param string     $delimiter
1602
     * @param string     $prepend
1603
     * @param array|null $items
1604
     *
1605
     * @return array
1606
     */
1607 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1608
    {
1609
        // init
1610 2
        $flatten = [];
1611
1612 2
        if ($items === null) {
1613 2
            $items = $this->array;
1614
        }
1615
1616 2
        foreach ($items as $key => $value) {
1617 2
            if (\is_array($value) && !empty($value)) {
1618 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1619
            } else {
1620 2
                $flatten[] = [$prepend . $key => $value];
1621
            }
1622
        }
1623
1624 2
        if (\count($flatten) === 0) {
1625
            return [];
1626
        }
1627
1628 2
        return \array_merge_recursive([], ...$flatten);
1629
    }
1630
1631
    /**
1632
     * WARNING: Creates an Arrayy object by reference.
1633
     *
1634
     * @param array $array
1635
     *
1636
     * @return $this
1637
     *               <p>(Mutable) Return this Arrayy object.</p>
1638
     *
1639
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1640
     */
1641 27
    public function createByReference(array &$array = []): self
1642
    {
1643 27
        $array = $this->fallbackForArray($array);
1644
1645 27
        $this->array = &$array;
1646
1647 27
        return $this;
1648
    }
1649
1650
    /**
1651
     * Create an new instance from a callable function which will return an Generator.
1652
     *
1653
     * @param callable $generatorFunction
1654
     *
1655
     * @return static
1656
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1657
     *
1658
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1659
     *
1660
     * @psalm-mutation-free
1661
     */
1662 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1663
    {
1664 7
        return self::create($generatorFunction);
1665
    }
1666
1667
    /**
1668
     * Create an new instance filled with a copy of values from a "Generator"-object.
1669
     *
1670
     * @param \Generator $generator
1671
     *
1672
     * @return static
1673
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1674
     *
1675
     * @psalm-param \Generator<array-key,mixed> $generator
1676
     *
1677
     * @psalm-mutation-free
1678
     */
1679 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1680
    {
1681 4
        return self::create(\iterator_to_array($generator, true));
1682
    }
1683
1684
    /**
1685
     * Create an new Arrayy object via JSON.
1686
     *
1687
     * @param string $json
1688
     *
1689
     * @return static
1690
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1691
     *
1692
     * @psalm-mutation-free
1693
     */
1694 5
    public static function createFromJson(string $json): self
1695
    {
1696 5
        return static::create(\json_decode($json, true));
1697
    }
1698
1699
    /**
1700
     * Create an new Arrayy object via JSON.
1701
     *
1702
     * @param array $array
1703
     *
1704
     * @return static
1705
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1706
     *
1707
     * @psalm-mutation-free
1708
     */
1709 1
    public static function createFromArray(array $array): self
1710
    {
1711 1
        return static::create($array);
1712
    }
1713
1714
    /**
1715
     * Create an new instance filled with values from an object that is iterable.
1716
     *
1717
     * @param \Traversable $object <p>iterable object</p>
1718
     *
1719
     * @return static
1720
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1721
     *
1722
     * @psalm-param \Traversable<array-key,mixed> $object
1723
     *
1724
     * @psalm-mutation-free
1725
     */
1726 4
    public static function createFromObject(\Traversable $object): self
1727
    {
1728
        // init
1729 4
        $arrayy = new static();
1730
1731 4
        if ($object instanceof self) {
1732 4
            $objectArray = $object->getGenerator();
1733
        } else {
1734
            $objectArray = $object;
1735
        }
1736
1737 4
        foreach ($objectArray as $key => $value) {
1738
            /**
1739
             * @psalm-suppress ImpureMethodCall - object is already re-created
1740
             */
1741 3
            $arrayy->internalSet($key, $value);
1742
        }
1743
1744 4
        return $arrayy;
1745
    }
1746
1747
    /**
1748
     * Create an new instance filled with values from an object.
1749
     *
1750
     * @param object $object
1751
     *
1752
     * @return static
1753
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1754
     *
1755
     * @psalm-mutation-free
1756
     */
1757 5
    public static function createFromObjectVars($object): self
1758
    {
1759 5
        return self::create(self::objectToArray($object));
1760
    }
1761
1762
    /**
1763
     * Create an new Arrayy object via string.
1764
     *
1765
     * @param string      $str       <p>The input string.</p>
1766
     * @param string|null $delimiter <p>The boundary string.</p>
1767
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1768
     *                               used.</p>
1769
     *
1770
     * @return static
1771
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1772
     *
1773
     * @psalm-mutation-free
1774
     */
1775 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1776
    {
1777 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...
1778 1
            \preg_match_all($regEx, $str, $array);
1779
1780 1
            if (!empty($array)) {
1781 1
                $array = $array[0];
1782
            }
1783
        } else {
1784
            /** @noinspection NestedPositiveIfStatementsInspection */
1785 9
            if ($delimiter !== null) {
1786 7
                $array = \explode($delimiter, $str);
1787
            } else {
1788 2
                $array = [$str];
1789
            }
1790
        }
1791
1792
        // trim all string in the array
1793
        /**
1794
         * @psalm-suppress MissingClosureParamType
1795
         */
1796 10
        \array_walk(
1797 10
            $array,
1798
            static function (&$val) {
1799 10
                if ((string) $val === $val) {
1800 10
                    $val = \trim($val);
1801
                }
1802 10
            }
1803
        );
1804
1805 10
        return static::create($array);
1806
    }
1807
1808
    /**
1809
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1810
     *
1811
     * @param \Traversable $traversable
1812
     *
1813
     * @return static
1814
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1815
     *
1816
     * @psalm-param \Traversable<array-key,mixed> $traversable
1817
     *
1818
     * @psalm-mutation-free
1819
     */
1820 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1821
    {
1822 1
        return self::create(\iterator_to_array($traversable, true));
1823
    }
1824
1825
    /**
1826
     * Create an new instance containing a range of elements.
1827
     *
1828
     * @param float|int|string $low  <p>First value of the sequence.</p>
1829
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1830
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1831
     *
1832
     * @return static
1833
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1834
     *
1835
     * @psalm-mutation-free
1836
     */
1837 2
    public static function createWithRange($low, $high, $step = 1): self
1838
    {
1839 2
        return static::create(\range($low, $high, $step));
1840
    }
1841
1842
    /**
1843
     * Gets the element of the array at the current internal iterator position.
1844
     *
1845
     * @return false|mixed
1846
     */
1847
    public function current()
1848
    {
1849
        return \current($this->array);
1850
    }
1851
1852
    /**
1853
     * Custom sort by index via "uksort".
1854
     *
1855
     * EXAMPLE: <code>
1856
     * $callable = function ($a, $b) {
1857
     *     if ($a == $b) {
1858
     *         return 0;
1859
     *     }
1860
     *     return ($a > $b) ? 1 : -1;
1861
     * };
1862
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1863
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1864
     * </code>
1865
     *
1866
     * @see          http://php.net/manual/en/function.uksort.php
1867
     *
1868
     * @param callable $function
1869
     *
1870
     * @throws \InvalidArgumentException
1871
     *
1872
     * @return $this
1873
     *               <p>(Mutable) Return this Arrayy object.</p>
1874
     *
1875
     * @psalm-return static<TKey,T>
1876
     */
1877 5
    public function customSortKeys(callable $function): self
1878
    {
1879 5
        $this->generatorToArray();
1880
1881 5
        \uksort($this->array, $function);
1882
1883 5
        return $this;
1884
    }
1885
1886
    /**
1887
     * Custom sort by index via "uksort".
1888
     *
1889
     * @see          http://php.net/manual/en/function.uksort.php
1890
     *
1891
     * @param callable $function
1892
     *
1893
     * @throws \InvalidArgumentException
1894
     *
1895
     * @return $this
1896
     *               <p>(Immutable) Return this Arrayy object.</p>
1897
     *
1898
     * @psalm-return static<TKey,T>
1899
     * @psalm-mutation-free
1900
     */
1901 1
    public function customSortKeysImmutable(callable $function): self
1902
    {
1903 1
        $that = clone $this;
1904
1905 1
        $that->generatorToArray();
1906
1907
        /**
1908
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1909
         */
1910 1
        \uksort($that->array, $function);
1911
1912 1
        return $that;
1913
    }
1914
1915
    /**
1916
     * Custom sort by value via "usort".
1917
     *
1918
     * EXAMPLE: <code>
1919
     * $callable = function ($a, $b) {
1920
     *     if ($a == $b) {
1921
     *         return 0;
1922
     *     }
1923
     *     return ($a > $b) ? 1 : -1;
1924
     * };
1925
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1926
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1927
     * </code>
1928
     *
1929
     * @see          http://php.net/manual/en/function.usort.php
1930
     *
1931
     * @param callable $function
1932
     *
1933
     * @throws \InvalidArgumentException
1934
     *
1935
     * @return $this
1936
     *               <p>(Mutable) Return this Arrayy object.</p>
1937
     *
1938
     * @psalm-return static<TKey,T>
1939
     */
1940 10 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...
1941
    {
1942 10
        if (\is_callable($function) === false) {
1943
            throw new \InvalidArgumentException('Passed function must be callable');
1944
        }
1945
1946 10
        $this->generatorToArray();
1947
1948 10
        \usort($this->array, $function);
1949
1950 10
        return $this;
1951
    }
1952
1953
    /**
1954
     * Custom sort by value via "usort".
1955
     *
1956
     * @see          http://php.net/manual/en/function.usort.php
1957
     *
1958
     * @param callable $function
1959
     *
1960
     * @throws \InvalidArgumentException
1961
     *
1962
     * @return $this
1963
     *               <p>(Immutable) Return this Arrayy object.</p>
1964
     *
1965
     * @psalm-return static<TKey,T>
1966
     * @psalm-mutation-free
1967
     */
1968 4
    public function customSortValuesImmutable($function): self
1969
    {
1970 4
        $that = clone $this;
1971
1972
        /**
1973
         * @psalm-suppress ImpureMethodCall - object is already cloned
1974
         */
1975 4
        $that->customSortValues($function);
1976
1977 4
        return $that;
1978
    }
1979
1980
    /**
1981
     * Delete the given key or keys.
1982
     *
1983
     * @param int|int[]|string|string[] $keyOrKeys
1984
     *
1985
     * @return void
1986
     */
1987 9
    public function delete($keyOrKeys)
1988
    {
1989 9
        $keyOrKeys = (array) $keyOrKeys;
1990
1991 9
        foreach ($keyOrKeys as $key) {
1992 9
            $this->offsetUnset($key);
1993
        }
1994 9
    }
1995
1996
    /**
1997
     * Return values that are only in the current array.
1998
     *
1999
     * EXAMPLE: <code>
2000
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
2001
     * </code>
2002
     *
2003
     * @param array ...$array
2004
     *
2005
     * @return static
2006
     *                <p>(Immutable)</p>
2007
     *
2008
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2009
     * @psalm-return static<TKey,T>
2010
     * @psalm-mutation-free
2011
     */
2012 13
    public function diff(...$array): self
2013
    {
2014 13
        return static::create(
2015 13
            \array_diff($this->toArray(), ...$array),
2016 13
            $this->iteratorClass,
2017 13
            false
2018
        );
2019
    }
2020
2021
    /**
2022
     * Return values that are only in the current array.
2023
     *
2024
     * @param array ...$array
2025
     *
2026
     * @return static
2027
     *                <p>(Immutable)</p>
2028
     *
2029
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2030
     * @psalm-return static<TKey,T>
2031
     * @psalm-mutation-free
2032
     */
2033 8
    public function diffKey(...$array): self
2034
    {
2035 8
        return static::create(
2036 8
            \array_diff_key($this->toArray(), ...$array),
2037 8
            $this->iteratorClass,
2038 8
            false
2039
        );
2040
    }
2041
2042
    /**
2043
     * Return values and Keys that are only in the current array.
2044
     *
2045
     * @param array $array
2046
     *
2047
     * @return static
2048
     *                <p>(Immutable)</p>
2049
     *
2050
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2051
     * @psalm-return static<TKey,T>
2052
     * @psalm-mutation-free
2053
     */
2054 8
    public function diffKeyAndValue(array $array = []): self
2055
    {
2056 8
        return static::create(
2057 8
            \array_diff_assoc($this->toArray(), $array),
2058 8
            $this->iteratorClass,
2059 8
            false
2060
        );
2061
    }
2062
2063
    /**
2064
     * Return values that are only in the current multi-dimensional array.
2065
     *
2066
     * EXAMPLE: <code>
2067
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2068
     * </code>
2069
     *
2070
     * @param array                 $array
2071
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2072
     *
2073
     * @return static
2074
     *                <p>(Immutable)</p>
2075
     *
2076
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2077
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2078
     * @psalm-return static<TKey,T>
2079
     * @psalm-mutation-free
2080
     */
2081 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2082
    {
2083
        // init
2084 1
        $result = [];
2085
2086
        if (
2087 1
            $helperVariableForRecursion !== null
2088
            &&
2089 1
            \is_array($helperVariableForRecursion)
2090
        ) {
2091
            $arrayForTheLoop = $helperVariableForRecursion;
2092
        } else {
2093 1
            $arrayForTheLoop = $this->getGenerator();
2094
        }
2095
2096 1
        foreach ($arrayForTheLoop as $key => $value) {
2097 1
            if ($value instanceof self) {
2098 1
                $value = $value->toArray();
2099
            }
2100
2101 1
            if (\array_key_exists($key, $array)) {
2102 1
                if ($value !== $array[$key]) {
2103 1
                    $result[$key] = $value;
2104
                }
2105
            } else {
2106 1
                $result[$key] = $value;
2107
            }
2108
        }
2109
2110 1
        return static::create(
2111 1
            $result,
2112 1
            $this->iteratorClass,
2113 1
            false
2114
        );
2115
    }
2116
2117
    /**
2118
     * Return values that are only in the new $array.
2119
     *
2120
     * EXAMPLE: <code>
2121
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2122
     * </code>
2123
     *
2124
     * @param array $array
2125
     *
2126
     * @return static
2127
     *                <p>(Immutable)</p>
2128
     *
2129
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2130
     * @psalm-return static<TKey,T>
2131
     * @psalm-mutation-free
2132
     */
2133 8
    public function diffReverse(array $array = []): self
2134
    {
2135 8
        return static::create(
2136 8
            \array_diff($array, $this->toArray()),
2137 8
            $this->iteratorClass,
2138 8
            false
2139
        );
2140
    }
2141
2142
    /**
2143
     * Divide an array into two arrays. One with keys and the other with values.
2144
     *
2145
     * EXAMPLE: <code>
2146
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2147
     * </code>
2148
     *
2149
     * @return static
2150
     *                <p>(Immutable)</p>
2151
     *
2152
     * @psalm-return static<TKey,T>
2153
     * @psalm-mutation-free
2154
     */
2155 1
    public function divide(): self
2156
    {
2157 1
        return static::create(
2158
            [
2159 1
                $this->keys(),
2160 1
                $this->values(),
2161
            ],
2162 1
            $this->iteratorClass,
2163 1
            false
2164
        );
2165
    }
2166
2167
    /**
2168
     * Iterate over the current array and modify the array's value.
2169
     *
2170
     * EXAMPLE: <code>
2171
     * $result = A::create();
2172
     * $closure = function ($value) {
2173
     *     return ':' . $value . ':';
2174
     * };
2175
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2176
     * </code>
2177
     *
2178
     * @param \Closure $closure
2179
     *
2180
     * @return static
2181
     *                <p>(Immutable)</p>
2182
     *
2183
     * @psalm-return static<TKey,T>
2184
     * @psalm-mutation-free
2185
     */
2186 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...
2187
    {
2188
        // init
2189 5
        $array = [];
2190
2191 5
        foreach ($this->getGenerator() as $key => $value) {
2192 5
            $array[$key] = $closure($value, $key);
2193
        }
2194
2195 5
        return static::create(
2196 5
            $array,
2197 5
            $this->iteratorClass,
2198 5
            false
2199
        );
2200
    }
2201
2202
    /**
2203
     * Sets the internal iterator to the last element in the array and returns this element.
2204
     *
2205
     * @return mixed
2206
     */
2207
    public function end()
2208
    {
2209
        return \end($this->array);
2210
    }
2211
2212
    /**
2213
     * Check if a value is in the current array using a closure.
2214
     *
2215
     * EXAMPLE: <code>
2216
     * $callable = function ($value, $key) {
2217
     *     return 2 === $key and 'two' === $value;
2218
     * };
2219
     * a(['foo', 2 => 'two'])->exists($callable); // true
2220
     * </code>
2221
     *
2222
     * @param \Closure $closure
2223
     *
2224
     * @return bool
2225
     *              <p>Returns true if the given value is found, false otherwise.</p>
2226
     */
2227 4
    public function exists(\Closure $closure): bool
2228
    {
2229
        // init
2230 4
        $isExists = false;
2231
2232 4
        foreach ($this->getGenerator() as $key => $value) {
2233 3
            if ($closure($value, $key)) {
2234 1
                $isExists = true;
2235
2236 3
                break;
2237
            }
2238
        }
2239
2240 4
        return $isExists;
2241
    }
2242
2243
    /**
2244
     * Fill the array until "$num" with "$default" values.
2245
     *
2246
     * EXAMPLE: <code>
2247
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2248
     * </code>
2249
     *
2250
     * @param int   $num
2251
     * @param mixed $default
2252
     *
2253
     * @return static
2254
     *                <p>(Immutable)</p>
2255
     *
2256
     * @psalm-return static<TKey,T>
2257
     * @psalm-mutation-free
2258
     */
2259 8
    public function fillWithDefaults(int $num, $default = null): self
2260
    {
2261 8
        if ($num < 0) {
2262 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2263
        }
2264
2265 7
        $this->generatorToArray();
2266
2267 7
        $tmpArray = $this->array;
2268
2269 7
        $count = \count($tmpArray);
2270
2271 7
        while ($count < $num) {
2272 4
            $tmpArray[] = $default;
2273 4
            ++$count;
2274
        }
2275
2276 7
        return static::create(
2277 7
            $tmpArray,
2278 7
            $this->iteratorClass,
2279 7
            false
2280
        );
2281
    }
2282
2283
    /**
2284
     * Find all items in an array that pass the truth test.
2285
     *
2286
     * EXAMPLE: <code>
2287
     * $closure = function ($value) {
2288
     *     return $value % 2 !== 0;
2289
     * }
2290
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2291
     * </code>
2292
     *
2293
     * @param \Closure|null $closure [optional] <p>
2294
     *                               The callback function to use
2295
     *                               </p>
2296
     *                               <p>
2297
     *                               If no callback is supplied, all entries of
2298
     *                               input equal to false (see
2299
     *                               converting to
2300
     *                               boolean) will be removed.
2301
     *                               </p>
2302
     * @param int           $flag    [optional] <p>
2303
     *                               Flag determining what arguments are sent to <i>callback</i>:
2304
     *                               </p>
2305
     *                               <ul>
2306
     *                               <li>
2307
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2308
     *                               to <i>callback</i> instead of the value
2309
     *                               </li>
2310
     *                               <li>
2311
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2312
     *                               arguments to <i>callback</i> instead of the value
2313
     *                               </li>
2314
     *                               </ul>
2315
     *
2316
     * @return static
2317
     *                <p>(Immutable)</p>
2318
     *
2319
     * @psalm-param null|\Closure(T=,TKey=):bool|\Closure(T=):bool||\Closure(TKey=):bool $closure
2320
     * @psalm-return static<TKey,T>
2321
     * @psalm-mutation-free
2322
     */
2323 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2324
    {
2325 12
        if (!$closure) {
2326 1
            return $this->clean();
2327
        }
2328
2329 12
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2330
            $generator = function () use ($closure) {
2331 1
                foreach ($this->getGenerator() as $key => $value) {
2332 1
                    if ($closure($key) === true) {
2333 1
                        yield $key => $value;
2334
                    }
2335
                }
2336 1
            };
2337 12
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2338
            $generator = function () use ($closure) {
2339 11
                foreach ($this->getGenerator() as $key => $value) {
2340 10
                    if ($closure($value, $key) === true) {
2341 10
                        yield $key => $value;
2342
                    }
2343
                }
2344 12
            };
2345
        } else {
2346 View Code Duplication
            $generator = function () use ($closure) {
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...
2347 1
                foreach ($this->getGenerator() as $key => $value) {
2348 1
                    if ($closure($value) === true) {
2349 1
                        yield $key => $value;
2350
                    }
2351
                }
2352 1
            };
2353
        }
2354
2355 12
        return static::create(
2356 12
            $generator,
2357 12
            $this->iteratorClass,
2358 12
            false
2359
        );
2360
    }
2361
2362
    /**
2363
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2364
     * property within that.
2365
     *
2366
     * @param string          $property
2367
     * @param string|string[] $value
2368
     * @param string          $comparisonOp
2369
     *                                      <p>
2370
     *                                      'eq' (equals),<br />
2371
     *                                      'gt' (greater),<br />
2372
     *                                      'gte' || 'ge' (greater or equals),<br />
2373
     *                                      'lt' (less),<br />
2374
     *                                      'lte' || 'le' (less or equals),<br />
2375
     *                                      'ne' (not equals),<br />
2376
     *                                      'contains',<br />
2377
     *                                      'notContains',<br />
2378
     *                                      'newer' (via strtotime),<br />
2379
     *                                      'older' (via strtotime),<br />
2380
     *                                      </p>
2381
     *
2382
     * @return static
2383
     *                <p>(Immutable)</p>
2384
     *
2385
     * @psalm-return static<TKey,T>
2386
     * @psalm-mutation-free
2387
     *
2388
     * @psalm-suppress MissingClosureReturnType
2389
     * @psalm-suppress MissingClosureParamType
2390
     */
2391 1
    public function filterBy(
2392
        string $property,
2393
        $value,
2394
        string $comparisonOp = null
2395
    ): self {
2396 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...
2397 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
2398
        }
2399
2400
        $ops = [
2401
            'eq' => static function ($item, $prop, $value): bool {
2402 1
                return $item[$prop] === $value;
2403 1
            },
2404
            'gt' => static function ($item, $prop, $value): bool {
2405
                return $item[$prop] > $value;
2406 1
            },
2407
            'ge' => static function ($item, $prop, $value): bool {
2408
                return $item[$prop] >= $value;
2409 1
            },
2410
            'gte' => static function ($item, $prop, $value): bool {
2411
                return $item[$prop] >= $value;
2412 1
            },
2413
            'lt' => static function ($item, $prop, $value): bool {
2414 1
                return $item[$prop] < $value;
2415 1
            },
2416
            'le' => static function ($item, $prop, $value): bool {
2417
                return $item[$prop] <= $value;
2418 1
            },
2419
            'lte' => static function ($item, $prop, $value): bool {
2420
                return $item[$prop] <= $value;
2421 1
            },
2422
            'ne' => static function ($item, $prop, $value): bool {
2423
                return $item[$prop] !== $value;
2424 1
            },
2425
            'contains' => static function ($item, $prop, $value): bool {
2426 1
                return \in_array($item[$prop], (array) $value, true);
2427 1
            },
2428
            'notContains' => static function ($item, $prop, $value): bool {
2429
                return !\in_array($item[$prop], (array) $value, true);
2430 1
            },
2431
            'newer' => static function ($item, $prop, $value): bool {
2432
                return \strtotime($item[$prop]) > \strtotime($value);
2433 1
            },
2434
            'older' => static function ($item, $prop, $value): bool {
2435
                return \strtotime($item[$prop]) < \strtotime($value);
2436 1
            },
2437
        ];
2438
2439 1
        $result = \array_values(
2440 1
            \array_filter(
2441 1
                $this->toArray(false, true),
2442
                static function ($item) use (
2443 1
                    $property,
2444 1
                    $value,
2445 1
                    $ops,
2446 1
                    $comparisonOp
2447
                ) {
2448 1
                    $item = (array) $item;
2449 1
                    $itemArrayy = static::create($item);
2450 1
                    $item[$property] = $itemArrayy->get($property, []);
2451
2452 1
                    return $ops[$comparisonOp]($item, $property, $value);
2453 1
                }
2454
            )
2455
        );
2456
2457 1
        return static::create(
2458 1
            $result,
2459 1
            $this->iteratorClass,
2460 1
            false
2461
        );
2462
    }
2463
2464
    /**
2465
     * Find the first item in an array that passes the truth test, otherwise return false.
2466
     *
2467
     * EXAMPLE: <code>
2468
     * $search = 'foo';
2469
     * $closure = function ($value, $key) use ($search) {
2470
     *     return $value === $search;
2471
     * };
2472
     * a(['foo', 'bar', 'lall'])->find($closure); // 'foo'
2473
     * </code>
2474
     *
2475
     * @param \Closure $closure
2476
     *
2477
     * @return false|mixed
2478
     *                     <p>Return false if we did not find the value.</p>
2479
     */
2480 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...
2481
    {
2482 8
        foreach ($this->getGenerator() as $key => $value) {
2483 6
            if ($closure($value, $key)) {
2484 6
                return $value;
2485
            }
2486
        }
2487
2488 3
        return false;
2489
    }
2490
2491
    /**
2492
     * find by ...
2493
     *
2494
     * EXAMPLE: <code>
2495
     * $array = [
2496
     *     0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01'],
2497
     *     1 => ['id' => 456, 'name' => 'bar', 'group' => 'primary', 'value' => 1468, 'when' => '2014-07-15'],
2498
     * ];
2499
     * a($array)->filterBy('name', 'foo'); // Arrayy[0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01']]
2500
     * </code>
2501
     *
2502
     * @param string          $property
2503
     * @param string|string[] $value
2504
     * @param string          $comparisonOp
2505
     *
2506
     * @return static
2507
     *                <p>(Immutable)</p>
2508
     *
2509
     * @psalm-return static<TKey,T>
2510
     * @psalm-mutation-free
2511
     */
2512 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2513
    {
2514 1
        return $this->filterBy($property, $value, $comparisonOp);
2515
    }
2516
2517
    /**
2518
     * Get the first value from the current array.
2519
     *
2520
     * EXAMPLE: <code>
2521
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->first(); // 'foo'
2522
     * </code>
2523
     *
2524
     * @return mixed
2525
     *               <p>Return null if there wasn't a element.</p>
2526
     */
2527 23
    public function first()
2528
    {
2529 23
        $key_first = $this->firstKey();
2530 23
        if ($key_first === null) {
2531 3
            return null;
2532
        }
2533
2534 20
        return $this->get($key_first);
2535
    }
2536
2537
    /**
2538
     * Get the first key from the current array.
2539
     *
2540
     * @return mixed
2541
     *               <p>Return null if there wasn't a element.</p>
2542
     * @psalm-mutation-free
2543
     */
2544 30
    public function firstKey()
2545
    {
2546 30
        $this->generatorToArray();
2547
2548 30
        return \array_key_first($this->array);
2549
    }
2550
2551
    /**
2552
     * Get the first value(s) from the current array.
2553
     * And will return an empty array if there was no first entry.
2554
     *
2555
     * EXAMPLE: <code>
2556
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsImmutable(2); // Arrayy[0 => 'foo', 1 => 'bar']
2557
     * </code>
2558
     *
2559
     * @param int|null $number <p>How many values you will take?</p>
2560
     *
2561
     * @return static
2562
     *                <p>(Immutable)</p>
2563
     *
2564
     * @psalm-return static<TKey,T>
2565
     * @psalm-mutation-free
2566
     */
2567 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...
2568
    {
2569 37
        $arrayTmp = $this->toArray();
2570
2571 37
        if ($number === null) {
2572 14
            $array = (array) \array_shift($arrayTmp);
2573
        } else {
2574 23
            $array = \array_splice($arrayTmp, 0, $number);
2575
        }
2576
2577 37
        return static::create(
2578 37
            $array,
2579 37
            $this->iteratorClass,
2580 37
            false
2581
        );
2582
    }
2583
2584
    /**
2585
     * Get the first value(s) from the current array.
2586
     * And will return an empty array if there was no first entry.
2587
     *
2588
     * @param int|null $number <p>How many values you will take?</p>
2589
     *
2590
     * @return static
2591
     *                <p>(Immutable)</p>
2592
     *
2593
     * @psalm-return static<TKey,T>
2594
     * @psalm-mutation-free
2595
     */
2596 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...
2597
    {
2598 3
        $arrayTmp = $this->keys()->toArray();
2599
2600 3
        if ($number === null) {
2601
            $array = (array) \array_shift($arrayTmp);
2602
        } else {
2603 3
            $array = \array_splice($arrayTmp, 0, $number);
2604
        }
2605
2606 3
        return static::create(
2607 3
            $array,
2608 3
            $this->iteratorClass,
2609 3
            false
2610
        );
2611
    }
2612
2613
    /**
2614
     * Get and remove the first value(s) from the current array.
2615
     * And will return an empty array if there was no first entry.
2616
     *
2617
     * EXAMPLE: <code>
2618
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsMutable(); // 'foo'
2619
     * </code>
2620
     *
2621
     * @param int|null $number <p>How many values you will take?</p>
2622
     *
2623
     * @return $this
2624
     *               <p>(Mutable)</p>
2625
     *
2626
     * @psalm-return static<TKey,T>
2627
     */
2628 34
    public function firstsMutable(int $number = null): self
2629
    {
2630 34
        $this->generatorToArray();
2631
2632 34
        if ($number === null) {
2633 19
            $this->array = (array) \array_shift($this->array);
2634
        } else {
2635 15
            $this->array = \array_splice($this->array, 0, $number);
2636
        }
2637
2638 34
        return $this;
2639
    }
2640
2641
    /**
2642
     * Exchanges all keys with their associated values in an array.
2643
     *
2644
     * EXAMPLE: <code>
2645
     * a([0 => 'foo', 1 => 'bar'])->flip(); // Arrayy['foo' => 0, 'bar' => 1]
2646
     * </code>
2647
     *
2648
     * @return static
2649
     *                <p>(Immutable)</p>
2650
     *
2651
     * @psalm-return static<array-key,TKey>
2652
     * @psalm-mutation-free
2653
     */
2654 1
    public function flip(): self
2655
    {
2656
        $generator = function (): \Generator {
2657 1
            foreach ($this->getGenerator() as $key => $value) {
2658 1
                yield (string) $value => $key;
2659
            }
2660 1
        };
2661
2662 1
        return static::create(
2663 1
            $generator,
2664 1
            $this->iteratorClass,
2665 1
            false
2666
        );
2667
    }
2668
2669
    /**
2670
     * Get a value from an array (optional using dot-notation).
2671
     *
2672
     * EXAMPLE: <code>
2673
     * $arrayy = a(['user' => ['lastname' => 'Moelleken']]);
2674
     * $arrayy->get('user.lastname'); // 'Moelleken'
2675
     * // ---
2676
     * $arrayy = new A();
2677
     * $arrayy['user'] = ['lastname' => 'Moelleken'];
2678
     * $arrayy['user.firstname'] = 'Lars';
2679
     * $arrayy['user']['lastname']; // Moelleken
2680
     * $arrayy['user.lastname']; // Moelleken
2681
     * $arrayy['user.firstname']; // Lars
2682
     * </code>
2683
     *
2684
     * @param mixed $key            <p>The key to look for.</p>
2685
     * @param mixed $fallback       <p>Value to fallback to.</p>
2686
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2687
     *                              class.</p>
2688
     * @param bool  $useByReference
2689
     *
2690
     * @return mixed|static
2691
     *
2692
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2693
     * @psalm-mutation-free
2694
     */
2695 243
    public function get(
2696
        $key,
2697
        $fallback = null,
2698
        array $array = null,
2699
        bool $useByReference = false
2700
    ) {
2701 243
        if ($array !== null) {
2702 4
            if ($useByReference) {
2703
                $usedArray = &$array;
2704
            } else {
2705 4
                $usedArray = $array;
2706
            }
2707
        } else {
2708 240
            $this->generatorToArray();
2709
2710 240
            if ($useByReference) {
2711 128
                $usedArray = &$this->array;
2712
            } else {
2713 130
                $usedArray = $this->array;
2714
            }
2715
        }
2716
2717 243
        if ($key === null) {
2718 1
            return static::create(
2719 1
                [],
2720 1
                $this->iteratorClass,
2721 1
                false
2722 1
            )->createByReference($usedArray);
2723
        }
2724
2725
        // php cast "bool"-index into "int"-index
2726 243
        if ((bool) $key === $key) {
2727 3
            $key = (int) $key;
2728
        }
2729
2730 243
        if (\array_key_exists($key, $usedArray) === true) {
2731 205
            if (\is_array($usedArray[$key])) {
2732 19
                return static::create(
2733 19
                    [],
2734 19
                    $this->iteratorClass,
2735 19
                    false
2736 19
                )->createByReference($usedArray[$key]);
2737
            }
2738
2739 191
            return $usedArray[$key];
2740
        }
2741
2742
        // crawl through array, get key according to object or not
2743 61
        $usePath = false;
2744
        if (
2745 61
            $this->pathSeparator
2746
            &&
2747 61
            (string) $key === $key
2748
            &&
2749 61
            \strpos($key, $this->pathSeparator) !== false
2750
        ) {
2751 31
            $segments = \explode($this->pathSeparator, (string) $key);
2752 31
            if ($segments !== false) {
2753 31
                $usePath = true;
2754 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2755
2756 31
                foreach ($segments as $segment) {
2757
                    if (
2758
                        (
2759 31
                            \is_array($usedArrayTmp)
2760
                            ||
2761 31
                            $usedArrayTmp instanceof \ArrayAccess
2762
                        )
2763
                        &&
2764 31
                        isset($usedArrayTmp[$segment])
2765
                    ) {
2766 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2767
2768 30
                        continue;
2769
                    }
2770
2771
                    if (
2772 14
                        \is_object($usedArrayTmp) === true
2773
                        &&
2774 14
                        \property_exists($usedArrayTmp, $segment)
2775
                    ) {
2776 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2777
2778 1
                        continue;
2779
                    }
2780
2781 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2782 1
                        $segmentsTmp = $segments;
2783 1
                        unset($segmentsTmp[0]);
2784 1
                        $keyTmp = \implode('.', $segmentsTmp);
2785 1
                        $returnTmp = static::create(
2786 1
                            [],
2787 1
                            $this->iteratorClass,
2788 1
                            false
2789
                        );
2790 1
                        foreach ($this->getAll() as $dataTmp) {
2791 1
                            if ($dataTmp instanceof self) {
2792
                                $returnTmp->add($dataTmp->get($keyTmp));
2793
2794
                                continue;
2795
                            }
2796
2797
                            if (
2798
                                (
2799 1
                                    \is_array($dataTmp)
2800
                                    ||
2801 1
                                    $dataTmp instanceof \ArrayAccess
2802
                                )
2803
                                &&
2804 1
                                isset($dataTmp[$keyTmp])
2805
                            ) {
2806
                                $returnTmp->add($dataTmp[$keyTmp]);
2807
2808
                                continue;
2809
                            }
2810
2811
                            if (
2812 1
                                \is_object($dataTmp) === true
2813
                                &&
2814 1
                                \property_exists($dataTmp, $keyTmp)
2815
                            ) {
2816 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2817
2818
                                /** @noinspection UnnecessaryContinueInspection */
2819 1
                                continue;
2820
                            }
2821
                        }
2822
2823 1
                        if ($returnTmp->count() > 0) {
2824 1
                            return $returnTmp;
2825
                        }
2826
                    }
2827
2828 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2829
                }
2830
            }
2831
        }
2832
2833 58
        if (isset($usedArrayTmp)) {
2834 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
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...
2835
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2836
            }
2837
2838 28
            if (\is_array($usedArrayTmp)) {
2839 6
                return static::create(
2840 6
                    [],
2841 6
                    $this->iteratorClass,
2842 6
                    false
2843 6
                )->createByReference($usedArrayTmp);
2844
            }
2845
2846 28
            return $usedArrayTmp;
2847
        }
2848
2849 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
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...
2850 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2851
        }
2852
2853
        return static::create(
2854
            [],
2855
            $this->iteratorClass,
2856
            false
2857
        )->createByReference($usedArray);
2858
    }
2859
2860
    /**
2861
     * alias: for "Arrayy->toArray()"
2862
     *
2863
     * @return array
2864
     *
2865
     * @see          Arrayy::getArray()
2866
     *
2867
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2868
     */
2869 15
    public function getAll(): array
2870
    {
2871 15
        return $this->toArray();
2872
    }
2873
2874
    /**
2875
     * Get the current array from the "Arrayy"-object.
2876
     *
2877
     * alias for "toArray()"
2878
     *
2879
     * @param bool $convertAllArrayyElements <p>
2880
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2881
     *                                       </p>
2882
     * @param bool $preserveKeys             <p>
2883
     *                                       e.g.: A generator maybe return the same key more then once,
2884
     *                                       so maybe you will ignore the keys.
2885
     *                                       </p>
2886
     *
2887
     * @return array
2888
     *
2889
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2890
     * @psalm-mutation-free
2891
     *
2892
     * @see Arrayy::toArray()
2893
     */
2894 501
    public function getArray(
2895
        bool $convertAllArrayyElements = false,
2896
        bool $preserveKeys = true
2897
    ): array {
2898 501
        return $this->toArray(
2899 501
            $convertAllArrayyElements,
2900 501
            $preserveKeys
2901
        );
2902
    }
2903
2904
    /**
2905
     * @param string $json
2906
     *
2907
     * @return $this
2908
     */
2909 3
    public static function createFromJsonMapper(string $json)
2910
    {
2911
        // init
2912 3
        $class = static::create();
2913
2914 3
        $jsonObject = \json_decode($json, false);
2915
2916 3
        $mapper = new \Arrayy\Mapper\Json();
2917 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
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...
2918
            if ($class->checkPropertiesMismatchInConstructor) {
2919
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

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...
2920
            }
2921
        };
2922
2923 3
        return $mapper->map($jsonObject, $class);
2924
    }
2925
2926
    /**
2927
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (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...
2928
     *
2929
     * @internal
2930
     */
2931 6
    public function getPhpDocPropertiesFromClass()
2932
    {
2933 6
        if ($this->properties === []) {
2934 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2935
        }
2936
2937 6
        return $this->properties;
2938
    }
2939
2940
    /**
2941
     * Get the current array from the "Arrayy"-object as list.
2942
     *
2943
     * alias for "toList()"
2944
     *
2945
     * @param bool $convertAllArrayyElements <p>
2946
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2947
     *                                       </p>
2948
     *
2949
     * @return array
2950
     *
2951
     * @psalm-return array<int,mixed>|array<int,T>
2952
     * @psalm-mutation-free
2953
     *
2954
     * @see Arrayy::toList()
2955
     */
2956 1
    public function getList(bool $convertAllArrayyElements = false): array
2957
    {
2958 1
        return $this->toList($convertAllArrayyElements);
2959
    }
2960
2961
    /**
2962
     * Returns the values from a single column of the input array, identified by
2963
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2964
     *
2965
     * EXAMPLE: <code>
2966
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
2967
     * </code>
2968
     *
2969
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
2970
     *       array by the values from the $indexKey column in the input array.
2971
     *
2972
     * @param mixed $columnKey
2973
     * @param mixed $indexKey
2974
     *
2975
     * @return static
2976
     *                <p>(Immutable)</p>
2977
     *
2978
     * @psalm-return static<TKey,T>
2979
     * @psalm-mutation-free
2980
     */
2981 1
    public function getColumn($columnKey = null, $indexKey = null): self
2982
    {
2983 1
        return static::create(
2984 1
            \array_column($this->toArray(), $columnKey, $indexKey),
2985 1
            $this->iteratorClass,
2986 1
            false
2987
        );
2988
    }
2989
2990
    /**
2991
     * Get the current array from the "Arrayy"-object as generator by reference.
2992
     *
2993
     * @return \Generator
2994
     *
2995
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2996
     */
2997 75
    public function &getGeneratorByReference(): \Generator
2998
    {
2999 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3000
            // -> false-positive -> see "&" from method
3001
            /** @noinspection YieldFromCanBeUsedInspection */
3002 17
            foreach ($this->generator as $key => $value) {
3003 17
                yield $key => $value;
3004
            }
3005
3006 5
            return;
3007
        }
3008
3009
        // -> false-positive -> see "&$value"
3010
        /** @noinspection YieldFromCanBeUsedInspection */
3011
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3012 59
        foreach ($this->array as $key => &$value) {
3013 54
            yield $key => $value;
3014
        }
3015 35
    }
3016
3017
    /**
3018
     * Get the current array from the "Arrayy"-object as generator.
3019
     *
3020
     * @return \Generator
3021
     *
3022
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3023
     * @psalm-mutation-free
3024
     */
3025 999
    public function getGenerator(): \Generator
3026
    {
3027 999
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3028 41
            yield from $this->generator;
3029
3030 41
            return;
3031
        }
3032
3033 997
        yield from $this->array;
3034 967
    }
3035
3036
    /**
3037
     * alias: for "Arrayy->keys()"
3038
     *
3039
     * @return static
3040
     *                <p>(Immutable)</p>
3041
     *
3042
     * @see          Arrayy::keys()
3043
     *
3044
     * @psalm-return static<array-key,TKey>
3045
     * @psalm-mutation-free
3046
     */
3047 2
    public function getKeys()
3048
    {
3049 2
        return $this->keys();
3050
    }
3051
3052
    /**
3053
     * Get the current array from the "Arrayy"-object as object.
3054
     *
3055
     * @return \stdClass
3056
     */
3057 4
    public function getObject(): \stdClass
3058
    {
3059 4
        return self::arrayToObject($this->toArray());
3060
    }
3061
3062
    /**
3063
     * alias: for "Arrayy->randomImmutable()"
3064
     *
3065
     * @return static
3066
     *                <p>(Immutable)</p>
3067
     *
3068
     * @see          Arrayy::randomImmutable()
3069
     *
3070
     * @psalm-return static<int|array-key,T>
3071
     */
3072 4
    public function getRandom(): self
3073
    {
3074 4
        return $this->randomImmutable();
3075
    }
3076
3077
    /**
3078
     * alias: for "Arrayy->randomKey()"
3079
     *
3080
     * @return mixed
3081
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3082
     *
3083
     * @see Arrayy::randomKey()
3084
     */
3085 3
    public function getRandomKey()
3086
    {
3087 3
        return $this->randomKey();
3088
    }
3089
3090
    /**
3091
     * alias: for "Arrayy->randomKeys()"
3092
     *
3093
     * @param int $number
3094
     *
3095
     * @return static
3096
     *                <p>(Immutable)</p>
3097
     *
3098
     * @see          Arrayy::randomKeys()
3099
     *
3100
     * @psalm-return static<TKey,T>
3101
     */
3102 8
    public function getRandomKeys(int $number): self
3103
    {
3104 8
        return $this->randomKeys($number);
3105
    }
3106
3107
    /**
3108
     * alias: for "Arrayy->randomValue()"
3109
     *
3110
     * @return mixed
3111
     *               <p>Get a random value or null if there wasn't a value.</p>
3112
     *
3113
     * @see Arrayy::randomValue()
3114
     */
3115 3
    public function getRandomValue()
3116
    {
3117 3
        return $this->randomValue();
3118
    }
3119
3120
    /**
3121
     * alias: for "Arrayy->randomValues()"
3122
     *
3123
     * @param int $number
3124
     *
3125
     * @return static
3126
     *                <p>(Immutable)</p>
3127
     *
3128
     * @see          Arrayy::randomValues()
3129
     *
3130
     * @psalm-return static<TKey,T>
3131
     */
3132 6
    public function getRandomValues(int $number): self
3133
    {
3134 6
        return $this->randomValues($number);
3135
    }
3136
3137
    /**
3138
     * Gets all values.
3139
     *
3140
     * @return static
3141
     *                <p>The values of all elements in this array, in the order they
3142
     *                appear in the array.</p>
3143
     *
3144
     * @psalm-return static<TKey,T>
3145
     */
3146 4
    public function getValues()
3147
    {
3148 4
        $this->generatorToArray(false);
3149
3150 4
        return static::create(
3151 4
            \array_values($this->array),
3152 4
            $this->iteratorClass,
3153 4
            false
3154
        );
3155
    }
3156
3157
    /**
3158
     * Gets all values via Generator.
3159
     *
3160
     * @return \Generator
3161
     *                    <p>The values of all elements in this array, in the order they
3162
     *                    appear in the array as Generator.</p>
3163
     *
3164
     * @psalm-return \Generator<TKey,T>
3165
     */
3166 4
    public function getValuesYield(): \Generator
3167
    {
3168 4
        yield from $this->getGenerator();
3169 4
    }
3170
3171
    /**
3172
     * Group values from a array according to the results of a closure.
3173
     *
3174
     * @param callable|string $grouper  <p>A callable function name.</p>
3175
     * @param bool            $saveKeys
3176
     *
3177
     * @return static
3178
     *                <p>(Immutable)</p>
3179
     *
3180
     * @psalm-return static<TKey,T>
3181
     * @psalm-mutation-free
3182
     */
3183 4
    public function group($grouper, bool $saveKeys = false): self
3184
    {
3185
        // init
3186 4
        $result = [];
3187
3188
        // Iterate over values, group by property/results from closure.
3189 4
        foreach ($this->getGenerator() as $key => $value) {
3190 4
            if (\is_callable($grouper) === true) {
3191 3
                $groupKey = $grouper($value, $key);
3192
            } else {
3193 1
                $groupKey = $this->get($grouper);
3194
            }
3195
3196 4
            $newValue = $this->get($groupKey, null, $result);
3197
3198 4
            if ($groupKey instanceof self) {
3199
                $groupKey = $groupKey->toArray();
3200
            }
3201
3202 4
            if ($newValue instanceof self) {
3203 4
                $newValue = $newValue->toArray();
3204
            }
3205
3206
            // Add to results.
3207 4
            if ($groupKey !== null) {
3208 3
                if ($saveKeys) {
3209 2
                    $result[$groupKey] = $newValue;
3210 2
                    $result[$groupKey][$key] = $value;
3211
                } else {
3212 1
                    $result[$groupKey] = $newValue;
3213 4
                    $result[$groupKey][] = $value;
3214
                }
3215
            }
3216
        }
3217
3218 4
        return static::create(
3219 4
            $result,
3220 4
            $this->iteratorClass,
3221 4
            false
3222
        );
3223
    }
3224
3225
    /**
3226
     * Check if an array has a given key.
3227
     *
3228
     * @param mixed $key
3229
     *
3230
     * @return bool
3231
     */
3232 30
    public function has($key): bool
3233
    {
3234 30
        static $UN_FOUND = null;
3235
3236 30
        if ($UN_FOUND === null) {
3237
            // Generate unique string to use as marker.
3238 1
            $UN_FOUND = \uniqid('arrayy', true);
3239
        }
3240
3241 30
        if (\is_array($key)) {
3242 1
            if ($key === []) {
3243
                return false;
3244
            }
3245
3246 1
            foreach ($key as $keyTmp) {
3247 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3248 1
                if ($found === false) {
3249 1
                    return false;
3250
                }
3251
            }
3252
3253 1
            return true;
3254
        }
3255
3256 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3257
    }
3258
3259
    /**
3260
     * Check if an array has a given value.
3261
     *
3262
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3263
     *
3264
     * @param mixed $value
3265
     *
3266
     * @return bool
3267
     */
3268 1
    public function hasValue($value): bool
3269
    {
3270 1
        return $this->contains($value);
3271
    }
3272
3273
    /**
3274
     * Implodes the values of this array.
3275
     *
3276
     * EXAMPLE: <code>
3277
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3278
     * </code>
3279
     *
3280
     * @param string $glue
3281
     * @param string $prefix
3282
     *
3283
     * @return string
3284
     * @psalm-mutation-free
3285
     */
3286 28
    public function implode(string $glue = '', string $prefix = ''): string
3287
    {
3288 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3289
    }
3290
3291
    /**
3292
     * Implodes the keys of this array.
3293
     *
3294
     * @param string $glue
3295
     *
3296
     * @return string
3297
     * @psalm-mutation-free
3298
     */
3299 8
    public function implodeKeys(string $glue = ''): string
3300
    {
3301 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3302
    }
3303
3304
    /**
3305
     * Given a list and an iterate-function that returns
3306
     * a key for each element in the list (or a property name),
3307
     * returns an object with an index of each item.
3308
     *
3309
     * @param mixed $key
3310
     *
3311
     * @return static
3312
     *                <p>(Immutable)</p>
3313
     *
3314
     * @psalm-return static<TKey,T>
3315
     * @psalm-mutation-free
3316
     */
3317 4 View Code Duplication
    public function indexBy($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...
3318
    {
3319
        // init
3320 4
        $results = [];
3321
3322 4
        foreach ($this->getGenerator() as $a) {
3323 4
            if (\array_key_exists($key, $a) === true) {
3324 4
                $results[$a[$key]] = $a;
3325
            }
3326
        }
3327
3328 4
        return static::create(
3329 4
            $results,
3330 4
            $this->iteratorClass,
3331 4
            false
3332
        );
3333
    }
3334
3335
    /**
3336
     * alias: for "Arrayy->searchIndex()"
3337
     *
3338
     * @param mixed $value <p>The value to search for.</p>
3339
     *
3340
     * @return false|mixed
3341
     *
3342
     * @see Arrayy::searchIndex()
3343
     */
3344 4
    public function indexOf($value)
3345
    {
3346 4
        return $this->searchIndex($value);
3347
    }
3348
3349
    /**
3350
     * Get everything but the last..$to items.
3351
     *
3352
     * EXAMPLE: <code>
3353
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3354
     * </code>
3355
     *
3356
     * @param int $to
3357
     *
3358
     * @return static
3359
     *                <p>(Immutable)</p>
3360
     *
3361
     * @psalm-return static<TKey,T>
3362
     * @psalm-mutation-free
3363
     */
3364 12
    public function initial(int $to = 1): self
3365
    {
3366 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3367
    }
3368
3369
    /**
3370
     * Return an array with all elements found in input array.
3371
     *
3372
     * EXAMPLE: <code>
3373
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3374
     * </code>
3375
     *
3376
     * @param array $search
3377
     * @param bool  $keepKeys
3378
     *
3379
     * @return static
3380
     *                <p>(Immutable)</p>
3381
     *
3382
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3383
     * @psalm-return static<TKey,T>
3384
     * @psalm-mutation-free
3385
     */
3386 4
    public function intersection(array $search, bool $keepKeys = false): self
3387
    {
3388 4
        if ($keepKeys) {
3389
            /**
3390
             * @psalm-suppress MissingClosureReturnType
3391
             * @psalm-suppress MissingClosureParamType
3392
             */
3393 1
            return static::create(
3394 1
                \array_uintersect(
3395 1
                    $this->toArray(),
3396 1
                    $search,
3397
                    static function ($a, $b) {
3398 1
                        return $a === $b ? 0 : -1;
3399 1
                    }
3400
                ),
3401 1
                $this->iteratorClass,
3402 1
                false
3403
            );
3404
        }
3405
3406 3
        return static::create(
3407 3
            \array_values(\array_intersect($this->toArray(), $search)),
3408 3
            $this->iteratorClass,
3409 3
            false
3410
        );
3411
    }
3412
3413
    /**
3414
     * Return an array with all elements found in input array.
3415
     *
3416
     * @param array ...$array
3417
     *
3418
     * @return static
3419
     *                <p>(Immutable)</p>
3420
     *
3421
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3422
     * @psalm-return static<TKey,T>
3423
     * @psalm-mutation-free
3424
     */
3425 1
    public function intersectionMulti(...$array): self
3426
    {
3427 1
        return static::create(
3428 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3429 1
            $this->iteratorClass,
3430 1
            false
3431
        );
3432
    }
3433
3434
    /**
3435
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3436
     *
3437
     * EXAMPLE: <code>
3438
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3439
     * </code>
3440
     *
3441
     * @param array $search
3442
     *
3443
     * @return bool
3444
     *
3445
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3446
     */
3447 1
    public function intersects(array $search): bool
3448
    {
3449 1
        return $this->intersection($search)->count() > 0;
3450
    }
3451
3452
    /**
3453
     * Invoke a function on all of an array's values.
3454
     *
3455
     * @param callable $callable
3456
     * @param mixed    $arguments
3457
     *
3458
     * @return static
3459
     *                <p>(Immutable)</p>
3460
     *
3461
     * @psalm-param  callable(T=,mixed):mixed $callable
3462
     * @psalm-return static<TKey,T>
3463
     * @psalm-mutation-free
3464
     */
3465 1
    public function invoke($callable, $arguments = []): self
3466
    {
3467
        // If one argument given for each iteration, create an array for it.
3468 1
        if (!\is_array($arguments)) {
3469 1
            $arguments = \array_fill(
3470 1
                0,
3471 1
                $this->count(),
3472 1
                $arguments
3473
            );
3474
        }
3475
3476
        // If the callable has arguments, pass them.
3477 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...
3478 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3479
        } else {
3480 1
            $array = $this->map($callable);
3481
        }
3482
3483 1
        return static::create(
3484 1
            $array,
3485 1
            $this->iteratorClass,
3486 1
            false
3487
        );
3488
    }
3489
3490
    /**
3491
     * Check whether array is associative or not.
3492
     *
3493
     * EXAMPLE: <code>
3494
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3495
     * </code>
3496
     *
3497
     * @param bool $recursive
3498
     *
3499
     * @return bool
3500
     *              <p>Returns true if associative, false otherwise.</p>
3501
     */
3502 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...
3503
    {
3504 15
        if ($this->isEmpty()) {
3505 3
            return false;
3506
        }
3507
3508
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3509 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3510 13
            if ((string) $key !== $key) {
3511 13
                return false;
3512
            }
3513
        }
3514
3515 3
        return true;
3516
    }
3517
3518
    /**
3519
     * Check if a given key or keys are empty.
3520
     *
3521
     * @param int|int[]|string|string[]|null $keys
3522
     *
3523
     * @return bool
3524
     *              <p>Returns true if empty, false otherwise.</p>
3525
     * @psalm-mutation-free
3526
     */
3527 45
    public function isEmpty($keys = null): bool
3528
    {
3529 45
        if ($this->generator) {
3530
            return $this->toArray() === [];
3531
        }
3532
3533 45
        if ($keys === null) {
3534 43
            return $this->array === [];
3535
        }
3536
3537 2
        foreach ((array) $keys as $key) {
3538 2
            if (!empty($this->get($key))) {
3539 2
                return false;
3540
            }
3541
        }
3542
3543 2
        return true;
3544
    }
3545
3546
    /**
3547
     * Check if the current array is equal to the given "$array" or not.
3548
     *
3549
     * EXAMPLE: <code>
3550
     * a(['💩'])->isEqual(['💩']); // true
3551
     * </code>
3552
     *
3553
     * @param array $array
3554
     *
3555
     * @return bool
3556
     *
3557
     * @psalm-param array<mixed,mixed> $array
3558
     */
3559 1
    public function isEqual(array $array): bool
3560
    {
3561 1
        return $this->toArray() === $array;
3562
    }
3563
3564
    /**
3565
     * Check if the current array is a multi-array.
3566
     *
3567
     * EXAMPLE: <code>
3568
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3569
     * </code>
3570
     *
3571
     * @return bool
3572
     */
3573 22
    public function isMultiArray(): bool
3574
    {
3575 22
        foreach ($this->getGenerator() as $key => $value) {
3576 20
            if (\is_array($value)) {
3577 20
                return true;
3578
            }
3579
        }
3580
3581 18
        return false;
3582
    }
3583
3584
    /**
3585
     * Check whether array is numeric or not.
3586
     *
3587
     * @return bool
3588
     *              <p>Returns true if numeric, false otherwise.</p>
3589
     */
3590 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...
3591
    {
3592 5
        if ($this->isEmpty()) {
3593 2
            return false;
3594
        }
3595
3596
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3597 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3598 4
            if ((int) $key !== $key) {
3599 4
                return false;
3600
            }
3601
        }
3602
3603 2
        return true;
3604
    }
3605
3606
    /**
3607
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3608
     *
3609
     * EXAMPLE: <code>
3610
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3611
     * </code>
3612
     *
3613
     * INFO: If the array is empty we count it as non-sequential.
3614
     *
3615
     * @param bool $recursive
3616
     *
3617
     * @return bool
3618
     * @psalm-mutation-free
3619
     */
3620 10
    public function isSequential(bool $recursive = false): bool
3621
    {
3622 10
        $i = 0;
3623 10
        foreach ($this->getGenerator() as $key => $value) {
3624
            if (
3625 9
                $recursive
3626
                &&
3627 9
                (\is_array($value) || $value instanceof self)
3628
                &&
3629 9
                self::create($value)->isSequential() === false
3630
            ) {
3631 1
                return false;
3632
            }
3633
3634 9
            if ($key !== $i) {
3635 3
                return false;
3636
            }
3637
3638 8
            ++$i;
3639
        }
3640
3641
        /** @noinspection IfReturnReturnSimplificationInspection */
3642 9
        if ($i === 0) {
3643 3
            return false;
3644
        }
3645
3646 8
        return true;
3647
    }
3648
3649
    /**
3650
     * @return array
3651
     *
3652
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3653
     */
3654 2
    public function jsonSerialize(): array
3655
    {
3656 2
        return $this->toArray();
3657
    }
3658
3659
    /**
3660
     * Gets the key/index of the element at the current internal iterator position.
3661
     *
3662
     * @return int|string|null
3663
     */
3664
    public function key()
3665
    {
3666
        return \key($this->array);
3667
    }
3668
3669
    /**
3670
     * Checks if the given key exists in the provided array.
3671
     *
3672
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3673
     *       then you need to use "Arrayy->offsetExists()".
3674
     *
3675
     * @param int|string $key the key to look for
3676
     *
3677
     * @return bool
3678
     * @psalm-mutation-free
3679
     */
3680 164
    public function keyExists($key): bool
3681
    {
3682 164
        return \array_key_exists($key, $this->array);
3683
    }
3684
3685
    /**
3686
     * Get all keys from the current array.
3687
     *
3688
     * EXAMPLE: <code>
3689
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3690
     * </code>
3691
     *
3692
     * @param bool       $recursive     [optional] <p>
3693
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3694
     *                                  </p>
3695
     * @param mixed|null $search_values [optional] <p>
3696
     *                                  If specified, then only keys containing these values are returned.
3697
     *                                  </p>
3698
     * @param bool       $strict        [optional] <p>
3699
     *                                  Determines if strict comparison (===) should be used during the search.
3700
     *                                  </p>
3701
     *
3702
     * @return static
3703
     *                <p>(Immutable) An array of all the keys in input.</p>
3704
     *
3705
     * @psalm-return static<array-key,TKey>
3706
     * @psalm-mutation-free
3707
     */
3708 29
    public function keys(
3709
        bool $recursive = false,
3710
        $search_values = null,
3711
        bool $strict = true
3712
    ): self {
3713
3714
        // recursive
3715
3716 29
        if ($recursive === true) {
3717 4
            $array = $this->array_keys_recursive(
3718 4
                null,
3719 4
                $search_values,
3720 4
                $strict
3721
            );
3722
3723 4
            return static::create(
3724 4
                $array,
3725 4
                $this->iteratorClass,
3726 4
                false
3727
            );
3728
        }
3729
3730
        // non recursive
3731
3732 28
        if ($search_values === null) {
3733
            $arrayFunction = function (): \Generator {
3734 28
                foreach ($this->getGenerator() as $key => $value) {
3735 26
                    yield $key;
3736
                }
3737 28
            };
3738
        } else {
3739
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3740 1
                $is_array_tmp = \is_array($search_values);
3741
3742
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3743 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3744 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...
3745
                        (
3746 1
                            $is_array_tmp === false
3747
                            &&
3748 1
                            $strict === true
3749
                            &&
3750 1
                            $search_values === $value
3751
                        )
3752
                        ||
3753
                        (
3754 1
                            $is_array_tmp === false
3755
                            &&
3756 1
                            $strict === false
3757
                            &&
3758 1
                            $search_values == $value
3759
                        )
3760
                        ||
3761
                        (
3762 1
                            $is_array_tmp === true
3763
                            &&
3764 1
                            \in_array($value, $search_values, $strict)
3765
                        )
3766
                    ) {
3767 1
                        yield $key;
3768
                    }
3769
                }
3770 1
            };
3771
        }
3772
3773 28
        return static::create(
3774 28
            $arrayFunction,
3775 28
            $this->iteratorClass,
3776 28
            false
3777
        );
3778
    }
3779
3780
    /**
3781
     * Sort an array by key in reverse order.
3782
     *
3783
     * @param int $sort_flags [optional] <p>
3784
     *                        You may modify the behavior of the sort using the optional
3785
     *                        parameter sort_flags, for details
3786
     *                        see sort.
3787
     *                        </p>
3788
     *
3789
     * @return $this
3790
     *               <p>(Mutable) Return this Arrayy object.</p>
3791
     *
3792
     * @psalm-return static<TKey,T>
3793
     */
3794 4
    public function krsort(int $sort_flags = 0): self
3795
    {
3796 4
        $this->generatorToArray();
3797
3798 4
        \krsort($this->array, $sort_flags);
3799
3800 4
        return $this;
3801
    }
3802
3803
    /**
3804
     * Sort an array by key in reverse order.
3805
     *
3806
     * @param int $sort_flags [optional] <p>
3807
     *                        You may modify the behavior of the sort using the optional
3808
     *                        parameter sort_flags, for details
3809
     *                        see sort.
3810
     *                        </p>
3811
     *
3812
     * @return $this
3813
     *               <p>(Immutable) Return this Arrayy object.</p>
3814
     *
3815
     * @psalm-return static<TKey,T>
3816
     * @psalm-mutation-free
3817
     */
3818 4
    public function krsortImmutable(int $sort_flags = 0): self
3819
    {
3820 4
        $that = clone $this;
3821
3822
        /**
3823
         * @psalm-suppress ImpureMethodCall - object is already cloned
3824
         */
3825 4
        $that->krsort($sort_flags);
3826
3827 4
        return $that;
3828
    }
3829
3830
    /**
3831
     * Get the last value from the current array.
3832
     *
3833
     * EXAMPLE: <code>
3834
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
3835
     * </code>
3836
     *
3837
     * @return mixed|null
3838
     *                    <p>Return null if there wasn't a element.</p>
3839
     * @psalm-mutation-free
3840
     */
3841 17
    public function last()
3842
    {
3843 17
        $key_last = $this->lastKey();
3844 17
        if ($key_last === null) {
3845 2
            return null;
3846
        }
3847
3848 15
        return $this->get($key_last);
3849
    }
3850
3851
    /**
3852
     * Get the last key from the current array.
3853
     *
3854
     * @return mixed|null
3855
     *                    <p>Return null if there wasn't a element.</p>
3856
     * @psalm-mutation-free
3857
     */
3858 21
    public function lastKey()
3859
    {
3860 21
        $this->generatorToArray();
3861
3862 21
        return \array_key_last($this->array);
3863
    }
3864
3865
    /**
3866
     * Get the last value(s) from the current array.
3867
     *
3868
     * EXAMPLE: <code>
3869
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3870
     * </code>
3871
     *
3872
     * @param int|null $number
3873
     *
3874
     * @return static
3875
     *                <p>(Immutable)</p>
3876
     *
3877
     * @psalm-return static<TKey,T>
3878
     * @psalm-mutation-free
3879
     */
3880 13
    public function lastsImmutable(int $number = null): self
3881
    {
3882 13
        if ($this->isEmpty()) {
3883 1
            return static::create(
3884 1
                [],
3885 1
                $this->iteratorClass,
3886 1
                false
3887
            );
3888
        }
3889
3890 12
        if ($number === null) {
3891 8
            $poppedValue = $this->last();
3892
3893 8
            if ($poppedValue === null) {
3894 1
                $poppedValue = [$poppedValue];
3895
            } else {
3896 7
                $poppedValue = (array) $poppedValue;
3897
            }
3898
3899 8
            $arrayy = static::create(
3900 8
                $poppedValue,
3901 8
                $this->iteratorClass,
3902 8
                false
3903
            );
3904
        } else {
3905 4
            $arrayy = $this->rest(-$number);
3906
        }
3907
3908 12
        return $arrayy;
3909
    }
3910
3911
    /**
3912
     * Get the last value(s) from the current array.
3913
     *
3914
     * EXAMPLE: <code>
3915
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3916
     * </code>
3917
     *
3918
     * @param int|null $number
3919
     *
3920
     * @return $this
3921
     *               <p>(Mutable)</p>
3922
     *
3923
     * @psalm-return static<TKey,T>
3924
     */
3925 13
    public function lastsMutable(int $number = null): self
3926
    {
3927 13
        if ($this->isEmpty()) {
3928 1
            return $this;
3929
        }
3930
3931 12
        $this->array = $this->lastsImmutable($number)->toArray();
3932 12
        $this->generator = null;
3933
3934 12
        return $this;
3935
    }
3936
3937
    /**
3938
     * Count the values from the current array.
3939
     *
3940
     * alias: for "Arrayy->count()"
3941
     *
3942
     * @param int $mode
3943
     *
3944
     * @return int
3945
     *
3946
     * @see Arrayy::count()
3947
     */
3948 20
    public function length(int $mode = \COUNT_NORMAL): int
3949
    {
3950 20
        return $this->count($mode);
3951
    }
3952
3953
    /**
3954
     * Apply the given function to the every element of the array,
3955
     * collecting the results.
3956
     *
3957
     * @param callable $callable
3958
     * @param bool     $useKeyAsSecondParameter
3959
     * @param mixed    ...$arguments
3960
     *
3961
     * @return static
3962
     *                <p>(Immutable) Arrayy object with modified elements.</p>
3963
     *
3964
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
3965
     * @psalm-return static<TKey,T>
3966
     * @psalm-mutation-free
3967
     */
3968 5
    public function map(
3969
        callable $callable,
3970
        bool $useKeyAsSecondParameter = false,
3971
        ...$arguments
3972
    ) {
3973
        /**
3974
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
3975
         */
3976 5
        $useArguments = \func_num_args() > 2;
3977
3978 5
        return static::create(
3979
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
3980 5
                foreach ($this->getGenerator() as $key => $value) {
3981 4
                    if ($useArguments) {
3982 3
                        if ($useKeyAsSecondParameter) {
3983
                            yield $key => $callable($value, $key, ...$arguments);
3984
                        } else {
3985 3
                            yield $key => $callable($value, ...$arguments);
3986
                        }
3987
                    } else {
3988
                        /** @noinspection NestedPositiveIfStatementsInspection */
3989 4
                        if ($useKeyAsSecondParameter) {
3990
                            yield $key => $callable($value, $key);
3991
                        } else {
3992 4
                            yield $key => $callable($value);
3993
                        }
3994
                    }
3995
                }
3996 5
            },
3997 5
            $this->iteratorClass,
3998 5
            false
3999
        );
4000
    }
4001
4002
    /**
4003
     * Check if all items in current array match a truth test.
4004
     *
4005
     * EXAMPLE: <code>
4006
     * $closure = function ($value, $key) {
4007
     *     return ($value % 2 === 0);
4008
     * };
4009
     * a([2, 4, 8])->matches($closure); // true
4010
     * </code>
4011
     *
4012
     * @param \Closure $closure
4013
     *
4014
     * @return bool
4015
     */
4016 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...
4017
    {
4018 15
        if ($this->count() === 0) {
4019 2
            return false;
4020
        }
4021
4022 13
        foreach ($this->getGenerator() as $key => $value) {
4023 13
            $value = $closure($value, $key);
4024
4025 13
            if ($value === false) {
4026 13
                return false;
4027
            }
4028
        }
4029
4030 7
        return true;
4031
    }
4032
4033
    /**
4034
     * Check if any item in the current array matches a truth test.
4035
     *
4036
     * EXAMPLE: <code>
4037
     * $closure = function ($value, $key) {
4038
     *     return ($value % 2 === 0);
4039
     * };
4040
     * a([1, 4, 7])->matches($closure); // true
4041
     * </code>
4042
     *
4043
     * @param \Closure $closure
4044
     *
4045
     * @return bool
4046
     */
4047 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...
4048
    {
4049 14
        if ($this->count() === 0) {
4050 2
            return false;
4051
        }
4052
4053 12
        foreach ($this->getGenerator() as $key => $value) {
4054 12
            $value = $closure($value, $key);
4055
4056 12
            if ($value === true) {
4057 12
                return true;
4058
            }
4059
        }
4060
4061 4
        return false;
4062
    }
4063
4064
    /**
4065
     * Get the max value from an array.
4066
     *
4067
     * EXAMPLE: <code>
4068
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4069
     * </code>
4070
     *
4071
     * @return false|mixed
4072
     *                     <p>Will return false if there are no values.</p>
4073
     */
4074 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...
4075
    {
4076 10
        if ($this->count() === 0) {
4077 1
            return false;
4078
        }
4079
4080 9
        $max = false;
4081
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4082 9
        foreach ($this->getGeneratorByReference() as &$value) {
4083
            if (
4084 9
                $max === false
4085
                ||
4086 9
                $value > $max
4087
            ) {
4088 9
                $max = $value;
4089
            }
4090
        }
4091
4092 9
        return $max;
4093
    }
4094
4095
    /**
4096
     * Merge the new $array into the current array.
4097
     *
4098
     * - keep key,value from the current array, also if the index is in the new $array
4099
     *
4100
     * EXAMPLE: <code>
4101
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4102
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4103
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4104
     * </code>
4105
     *
4106
     * @param array $array
4107
     * @param bool  $recursive
4108
     *
4109
     * @return static
4110
     *                <p>(Immutable)</p>
4111
     *
4112
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4113
     * @psalm-return static<int|TKey,T>
4114
     * @psalm-mutation-free
4115
     */
4116 32 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...
4117
    {
4118 32
        if ($recursive === true) {
4119 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4120 9
            $result = \array_replace_recursive($this->toArray(), $array);
4121
        } else {
4122 23
            $result = \array_replace($this->toArray(), $array);
4123
        }
4124
4125 32
        return static::create(
4126 32
            $result,
4127 32
            $this->iteratorClass,
4128 32
            false
4129
        );
4130
    }
4131
4132
    /**
4133
     * Merge the new $array into the current array.
4134
     *
4135
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4136
     * - create new indexes
4137
     *
4138
     * EXAMPLE: <code>
4139
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4140
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4141
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => three']
4142
     * </code>
4143
     *
4144
     * @param array $array
4145
     * @param bool  $recursive
4146
     *
4147
     * @return static
4148
     *                <p>(Immutable)</p>
4149
     *
4150
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4151
     * @psalm-return static<TKey,T>
4152
     * @psalm-mutation-free
4153
     */
4154 19 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...
4155
    {
4156 19
        if ($recursive === true) {
4157 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4158 5
            $result = \array_merge_recursive($this->toArray(), $array);
4159
        } else {
4160 14
            $result = \array_merge($this->toArray(), $array);
4161
        }
4162
4163 19
        return static::create(
4164 19
            $result,
4165 19
            $this->iteratorClass,
4166 19
            false
4167
        );
4168
    }
4169
4170
    /**
4171
     * Merge the the current array into the $array.
4172
     *
4173
     * - use key,value from the new $array, also if the index is in the current array
4174
     *
4175
     * EXAMPLE: <code>
4176
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4177
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4178
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4179
     * </code>
4180
     *
4181
     * @param array $array
4182
     * @param bool  $recursive
4183
     *
4184
     * @return static
4185
     *                <p>(Immutable)</p>
4186
     *
4187
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4188
     * @psalm-return static<TKey,T>
4189
     * @psalm-mutation-free
4190
     */
4191 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...
4192
    {
4193 16
        if ($recursive === true) {
4194 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4195 4
            $result = \array_replace_recursive($array, $this->toArray());
4196
        } else {
4197 12
            $result = \array_replace($array, $this->toArray());
4198
        }
4199
4200 16
        return static::create(
4201 16
            $result,
4202 16
            $this->iteratorClass,
4203 16
            false
4204
        );
4205
    }
4206
4207
    /**
4208
     * Merge the current array into the new $array.
4209
     *
4210
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4211
     * - create new indexes
4212
     *
4213
     * EXAMPLE: <code>
4214
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4215
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4216
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4217
     * </code>
4218
     *
4219
     * @param array $array
4220
     * @param bool  $recursive
4221
     *
4222
     * @return static
4223
     *                <p>(Immutable)</p>
4224
     *
4225
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4226
     * @psalm-return static<TKey,T>
4227
     * @psalm-mutation-free
4228
     */
4229 20 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...
4230
    {
4231 20
        if ($recursive === true) {
4232 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4233 7
            $result = \array_merge_recursive($array, $this->toArray());
4234
        } else {
4235 13
            $result = \array_merge($array, $this->toArray());
4236
        }
4237
4238 20
        return static::create(
4239 20
            $result,
4240 20
            $this->iteratorClass,
4241 20
            false
4242
        );
4243
    }
4244
4245
    /**
4246
     * @return ArrayyMeta|static
4247
     */
4248 18
    public static function meta()
4249
    {
4250 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4251
    }
4252
4253
    /**
4254
     * Get the min value from an array.
4255
     *
4256
     * EXAMPLE: <code>
4257
     * a([-9, -8, -7, 1.32])->min(); // -9
4258
     * </code>
4259
     *
4260
     * @return false|mixed
4261
     *                     <p>Will return false if there are no values.</p>
4262
     */
4263 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...
4264
    {
4265 10
        if ($this->count() === 0) {
4266 1
            return false;
4267
        }
4268
4269 9
        $min = false;
4270
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4271 9
        foreach ($this->getGeneratorByReference() as &$value) {
4272
            if (
4273 9
                $min === false
4274
                ||
4275 9
                $value < $min
4276
            ) {
4277 9
                $min = $value;
4278
            }
4279
        }
4280
4281 9
        return $min;
4282
    }
4283
4284
    /**
4285
     * Get the most used value from the array.
4286
     *
4287
     * @return mixed|null
4288
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4289
     * @psalm-mutation-free
4290
     */
4291 3
    public function mostUsedValue()
4292
    {
4293 3
        return $this->countValues()->arsortImmutable()->firstKey();
4294
    }
4295
4296
    /**
4297
     * Get the most used value from the array.
4298
     *
4299
     * @param int|null $number <p>How many values you will take?</p>
4300
     *
4301
     * @return static
4302
     *                <p>(Immutable)</p>
4303
     *
4304
     * @psalm-return static<TKey,T>
4305
     * @psalm-mutation-free
4306
     */
4307 3
    public function mostUsedValues(int $number = null): self
4308
    {
4309 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4310
    }
4311
4312
    /**
4313
     * Move an array element to a new index.
4314
     *
4315
     * EXAMPLE: <code>
4316
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4317
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4318
     * </code>
4319
     *
4320
     * @param int|string $from
4321
     * @param int        $to
4322
     *
4323
     * @return static
4324
     *                <p>(Immutable)</p>
4325
     *
4326
     * @psalm-return static<TKey,T>
4327
     * @psalm-mutation-free
4328
     */
4329 1
    public function moveElement($from, $to): self
4330
    {
4331 1
        $array = $this->toArray();
4332
4333 1
        if ((int) $from === $from) {
4334 1
            $tmp = \array_splice($array, $from, 1);
4335 1
            \array_splice($array, (int) $to, 0, $tmp);
4336 1
            $output = $array;
4337 1
        } elseif ((string) $from === $from) {
4338 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4339 1
            $itemToMove = $array[$from];
4340 1
            if ($indexToMove !== false) {
4341 1
                \array_splice($array, $indexToMove, 1);
4342
            }
4343 1
            $i = 0;
4344 1
            $output = [];
4345 1
            foreach ($array as $key => $item) {
4346 1
                if ($i === $to) {
4347 1
                    $output[$from] = $itemToMove;
4348
                }
4349 1
                $output[$key] = $item;
4350 1
                ++$i;
4351
            }
4352
        } else {
4353
            $output = [];
4354
        }
4355
4356 1
        return static::create(
4357 1
            $output,
4358 1
            $this->iteratorClass,
4359 1
            false
4360
        );
4361
    }
4362
4363
    /**
4364
     * Move an array element to the first place.
4365
     *
4366
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4367
     *       loss the keys of an indexed array.
4368
     *
4369
     * @param int|string $key
4370
     *
4371
     * @return static
4372
     *                <p>(Immutable)</p>
4373
     *
4374
     * @psalm-return static<TKey,T>
4375
     * @psalm-mutation-free
4376
     */
4377 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...
4378
    {
4379 1
        $array = $this->toArray();
4380
4381 1
        if ($this->offsetExists($key)) {
4382 1
            $tmpValue = $this->get($key);
4383 1
            unset($array[$key]);
4384 1
            $array = [$key => $tmpValue] + $array;
4385
        }
4386
4387 1
        return static::create(
4388 1
            $array,
4389 1
            $this->iteratorClass,
4390 1
            false
4391
        );
4392
    }
4393
4394
    /**
4395
     * Move an array element to the last place.
4396
     *
4397
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4398
     *       loss the keys of an indexed array.
4399
     *
4400
     * @param int|string $key
4401
     *
4402
     * @return static
4403
     *                <p>(Immutable)</p>
4404
     *
4405
     * @psalm-return static<TKey,T>
4406
     * @psalm-mutation-free
4407
     */
4408 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...
4409
    {
4410 1
        $array = $this->toArray();
4411
4412 1
        if ($this->offsetExists($key)) {
4413 1
            $tmpValue = $this->get($key);
4414 1
            unset($array[$key]);
4415 1
            $array += [$key => $tmpValue];
4416
        }
4417
4418 1
        return static::create(
4419 1
            $array,
4420 1
            $this->iteratorClass,
4421 1
            false
4422
        );
4423
    }
4424
4425
    /**
4426
     * Moves the internal iterator position to the next element and returns this element.
4427
     *
4428
     * @return false|mixed
4429
     *                     <p>(Mutable) Will return false if there are no values.</p>
4430
     */
4431
    public function next()
4432
    {
4433
        return \next($this->array);
4434
    }
4435
4436
    /**
4437
     * Get the next nth keys and values from the array.
4438
     *
4439
     * @param int $step
4440
     * @param int $offset
4441
     *
4442
     * @return static
4443
     *                <p>(Immutable)</p>
4444
     *
4445
     * @psalm-return static<TKey,T>
4446
     * @psalm-mutation-free
4447
     */
4448 1
    public function nth(int $step, int $offset = 0): self
4449
    {
4450
        $arrayFunction = function () use ($step, $offset): \Generator {
4451 1
            $position = 0;
4452 1
            foreach ($this->getGenerator() as $key => $value) {
4453 1
                if ($position++ % $step !== $offset) {
4454 1
                    continue;
4455
                }
4456
4457 1
                yield $key => $value;
4458
            }
4459 1
        };
4460
4461 1
        return static::create(
4462 1
            $arrayFunction,
4463 1
            $this->iteratorClass,
4464 1
            false
4465
        );
4466
    }
4467
4468
    /**
4469
     * Get a subset of the items from the given array.
4470
     *
4471
     * @param int[]|string[] $keys
4472
     *
4473
     * @return static
4474
     *                <p>(Immutable)</p>
4475
     *
4476
     * @psalm-param array-key[] $keys
4477
     * @psalm-return static<TKey,T>
4478
     * @psalm-mutation-free
4479
     */
4480 1 View Code Duplication
    public function only(array $keys): 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...
4481
    {
4482 1
        $keys = \array_flip($keys);
4483
4484
        $generator = function () use ($keys): \Generator {
4485 1
            foreach ($this->getGenerator() as $key => $value) {
4486 1
                if (isset($keys[$key])) {
4487 1
                    yield $key => $value;
4488
                }
4489
            }
4490 1
        };
4491
4492 1
        return static::create(
4493 1
            $generator,
4494 1
            $this->iteratorClass,
4495 1
            false
4496
        );
4497
    }
4498
4499
    /**
4500
     * Pad array to the specified size with a given value.
4501
     *
4502
     * @param int   $size  <p>Size of the result array.</p>
4503
     * @param mixed $value <p>Empty value by default.</p>
4504
     *
4505
     * @return static
4506
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4507
     *
4508
     * @psalm-return static<TKey,T>
4509
     * @psalm-mutation-free
4510
     */
4511 5
    public function pad(int $size, $value): self
4512
    {
4513 5
        return static::create(
4514 5
            \array_pad($this->toArray(), $size, $value),
4515 5
            $this->iteratorClass,
4516 5
            false
4517
        );
4518
    }
4519
4520
    /**
4521
     * Partitions this array in two array according to a predicate.
4522
     * Keys are preserved in the resulting array.
4523
     *
4524
     * @param \Closure $closure
4525
     *                          <p>The predicate on which to partition.</p>
4526
     *
4527
     * @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...
4528
     *                    <p>An array with two elements. The first element contains the array
4529
     *                    of elements where the predicate returned TRUE, the second element
4530
     *                    contains the array of elements where the predicate returned FALSE.</p>
4531
     *
4532
     * @psalm-return array<int, static<TKey,T>>
4533
     */
4534 1
    public function partition(\Closure $closure): array
4535
    {
4536
        // init
4537 1
        $matches = [];
4538 1
        $noMatches = [];
4539
4540 1
        foreach ($this->getGenerator() as $key => $value) {
4541 1
            if ($closure($value, $key)) {
4542 1
                $matches[$key] = $value;
4543
            } else {
4544 1
                $noMatches[$key] = $value;
4545
            }
4546
        }
4547
4548 1
        return [self::create($matches), self::create($noMatches)];
4549
    }
4550
4551
    /**
4552
     * Pop a specified value off the end of the current array.
4553
     *
4554
     * @return mixed|null
4555
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4556
     */
4557 5
    public function pop()
4558
    {
4559 5
        $this->generatorToArray();
4560
4561 5
        return \array_pop($this->array);
4562
    }
4563
4564
    /**
4565
     * Prepend a (key) + value to the current array.
4566
     *
4567
     * EXAMPLE: <code>
4568
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4569
     * </code>
4570
     *
4571
     * @param mixed $value
4572
     * @param mixed $key
4573
     *
4574
     * @return $this
4575
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4576
     *
4577
     * @psalm-return static<TKey,T>
4578
     */
4579 11
    public function prepend($value, $key = null)
4580
    {
4581 11
        $this->generatorToArray();
4582
4583 11
        if ($this->properties !== []) {
4584 3
            $this->checkType($key, $value);
4585
        }
4586
4587 9
        if ($key === null) {
4588 8
            \array_unshift($this->array, $value);
4589
        } else {
4590 2
            $this->array = [$key => $value] + $this->array;
4591
        }
4592
4593 9
        return $this;
4594
    }
4595
4596
    /**
4597
     * Prepend a (key) + value to the current array.
4598
     *
4599
     * EXAMPLE: <code>
4600
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4601
     * </code>
4602
     *
4603
     * @param mixed $value
4604
     * @param mixed $key
4605
     *
4606
     * @return $this
4607
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4608
     *
4609
     * @psalm-return static<TKey,T>
4610
     * @psalm-mutation-free
4611
     */
4612 1 View Code Duplication
    public function prependImmutable($value, $key = null)
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...
4613
    {
4614
        $generator = function () use ($key, $value): \Generator {
4615 1
            if ($this->properties !== []) {
4616
                $this->checkType($key, $value);
4617
            }
4618
4619 1
            if ($key !== null) {
4620
                yield $key => $value;
4621
            } else {
4622 1
                yield $value;
4623
            }
4624
4625
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4626 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4627 1
                yield $keyOld => $itemOld;
4628
            }
4629 1
        };
4630
4631 1
        return static::create(
4632 1
            $generator,
4633 1
            $this->iteratorClass,
4634 1
            false
4635
        );
4636
    }
4637
4638
    /**
4639
     * Add a suffix to each key.
4640
     *
4641
     * @param mixed $suffix
4642
     *
4643
     * @return static
4644
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4645
     *
4646
     * @psalm-return static<TKey,T>
4647
     * @psalm-mutation-free
4648
     */
4649 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...
4650
    {
4651
        // init
4652 10
        $result = [];
4653
4654 10
        foreach ($this->getGenerator() as $key => $item) {
4655 9
            if ($item instanceof self) {
4656
                $result[$key] = $item->prependToEachKey($suffix);
4657 9
            } elseif (\is_array($item)) {
4658
                $result[$key] = self::create(
4659
                    $item,
4660
                    $this->iteratorClass,
4661
                    false
4662
                )->prependToEachKey($suffix)
4663
                    ->toArray();
4664
            } else {
4665 9
                $result[$key . $suffix] = $item;
4666
            }
4667
        }
4668
4669 10
        return self::create(
4670 10
            $result,
4671 10
            $this->iteratorClass,
4672 10
            false
4673
        );
4674
    }
4675
4676
    /**
4677
     * Add a suffix to each value.
4678
     *
4679
     * @param mixed $suffix
4680
     *
4681
     * @return static
4682
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4683
     *
4684
     * @psalm-return static<TKey,T>
4685
     * @psalm-mutation-free
4686
     */
4687 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...
4688
    {
4689
        // init
4690 10
        $result = [];
4691
4692 10
        foreach ($this->getGenerator() as $key => $item) {
4693 9
            if ($item instanceof self) {
4694
                $result[$key] = $item->prependToEachValue($suffix);
4695 9
            } elseif (\is_array($item)) {
4696
                $result[$key] = self::create(
4697
                    $item,
4698
                    $this->iteratorClass,
4699
                    false
4700
                )->prependToEachValue($suffix)
4701
                    ->toArray();
4702 9
            } elseif (\is_object($item) === true) {
4703 1
                $result[$key] = $item;
4704
            } else {
4705 9
                $result[$key] = $item . $suffix;
4706
            }
4707
        }
4708
4709 10
        return self::create(
4710 10
            $result,
4711 10
            $this->iteratorClass,
4712 10
            false
4713
        );
4714
    }
4715
4716
    /**
4717
     * Return the value of a given key and
4718
     * delete the key.
4719
     *
4720
     * @param int|int[]|string|string[]|null $keyOrKeys
4721
     * @param mixed                          $fallback
4722
     *
4723
     * @return mixed
4724
     */
4725 5
    public function pull($keyOrKeys = null, $fallback = null)
4726
    {
4727 5
        if ($keyOrKeys === null) {
4728 1
            $array = $this->toArray();
4729 1
            $this->clear();
4730
4731 1
            return $array;
4732
        }
4733
4734 4
        if (\is_array($keyOrKeys)) {
4735 1
            $valueOrValues = [];
4736 1
            foreach ($keyOrKeys as $key) {
4737 1
                $valueOrValues[] = $this->get($key, $fallback);
4738 1
                $this->offsetUnset($key);
4739
            }
4740
        } else {
4741 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4742 4
            $this->offsetUnset($keyOrKeys);
4743
        }
4744
4745 4
        return $valueOrValues;
4746
    }
4747
4748
    /**
4749
     * Push one or more values onto the end of array at once.
4750
     *
4751
     * @param array ...$args
4752
     *
4753
     * @return $this
4754
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4755
     *
4756
     * @noinspection ReturnTypeCanBeDeclaredInspection
4757
     *
4758
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4759
     * @psalm-return static<TKey,T>
4760
     */
4761 7
    public function push(...$args)
4762
    {
4763 7
        $this->generatorToArray();
4764
4765
        if (
4766 7
            $this->checkPropertyTypes
4767
            &&
4768 7
            $this->properties !== []
4769
        ) {
4770 1
            foreach ($args as $key => $value) {
4771 1
                $this->checkType($key, $value);
4772
            }
4773
        }
4774
4775 7
        \array_push($this->array, ...$args);
4776
4777 7
        return $this;
4778
    }
4779
4780
    /**
4781
     * Get a random value from the current array.
4782
     *
4783
     * EXAMPLE: <code>
4784
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
4785
     * </code>
4786
     *
4787
     * @param int|null $number <p>How many values you will take?</p>
4788
     *
4789
     * @return static
4790
     *                <p>(Immutable)</p>
4791
     *
4792
     * @psalm-return static<int|array-key,T>
4793
     */
4794 19
    public function randomImmutable(int $number = null): self
4795
    {
4796 19
        $this->generatorToArray();
4797
4798 19
        if ($this->count() === 0) {
4799 1
            return static::create(
4800 1
                [],
4801 1
                $this->iteratorClass,
4802 1
                false
4803
            );
4804
        }
4805
4806 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...
4807
            /** @noinspection NonSecureArrayRandUsageInspection */
4808 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4809
4810 13
            return static::create(
4811 13
                $arrayRandValue,
4812 13
                $this->iteratorClass,
4813 13
                false
4814
            );
4815
        }
4816
4817 6
        $arrayTmp = $this->array;
4818
        /** @noinspection NonSecureShuffleUsageInspection */
4819 6
        \shuffle($arrayTmp);
4820
4821 6
        return static::create(
4822 6
            $arrayTmp,
4823 6
            $this->iteratorClass,
4824 6
            false
4825 6
        )->firstsImmutable($number);
4826
    }
4827
4828
    /**
4829
     * Pick a random key/index from the keys of this array.
4830
     *
4831
     * EXAMPLE: <code>
4832
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
4833
     * $arrayy->randomKey(); // e.g. 2
4834
     * </code>
4835
     *
4836
     * @throws \RangeException If array is empty
4837
     *
4838
     * @return mixed
4839
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4840
     */
4841 4
    public function randomKey()
4842
    {
4843 4
        $result = $this->randomKeys(1);
4844
4845 4
        if (!isset($result[0])) {
4846
            $result[0] = null;
4847
        }
4848
4849 4
        return $result[0];
4850
    }
4851
4852
    /**
4853
     * Pick a given number of random keys/indexes out of this array.
4854
     *
4855
     * EXAMPLE: <code>
4856
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
4857
     * </code>
4858
     *
4859
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4860
     *
4861
     * @throws \RangeException If array is empty
4862
     *
4863
     * @return static
4864
     *                <p>(Immutable)</p>
4865
     *
4866
     * @psalm-return static<TKey,T>
4867
     */
4868 13
    public function randomKeys(int $number): self
4869
    {
4870 13
        $this->generatorToArray();
4871
4872 13
        $count = $this->count();
4873
4874
        if (
4875 13
            $number === 0
4876
            ||
4877 13
            $number > $count
4878
        ) {
4879 2
            throw new \RangeException(
4880 2
                \sprintf(
4881 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
4882 2
                    $number,
4883 2
                    $count
4884
                )
4885
            );
4886
        }
4887
4888 11
        $result = (array) \array_rand($this->array, $number);
4889
4890 11
        return static::create(
4891 11
            $result,
4892 11
            $this->iteratorClass,
4893 11
            false
4894
        );
4895
    }
4896
4897
    /**
4898
     * Get a random value from the current array.
4899
     *
4900
     * EXAMPLE: <code>
4901
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
4902
     * </code>
4903
     *
4904
     * @param int|null $number <p>How many values you will take?</p>
4905
     *
4906
     * @return $this
4907
     *               <p>(Mutable) Return this Arrayy object.</p>
4908
     *
4909
     * @psalm-return static<TKey,T>
4910
     */
4911 17
    public function randomMutable(int $number = null): self
4912
    {
4913 17
        $this->generatorToArray();
4914
4915 17
        if ($this->count() === 0) {
4916
            return static::create(
4917
                [],
4918
                $this->iteratorClass,
4919
                false
4920
            );
4921
        }
4922
4923 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...
4924
            /** @noinspection NonSecureArrayRandUsageInspection */
4925 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4926 7
            $this->array = $arrayRandValue;
4927
4928 7
            return $this;
4929
        }
4930
4931
        /** @noinspection NonSecureShuffleUsageInspection */
4932 11
        \shuffle($this->array);
4933
4934 11
        return $this->firstsMutable($number);
4935
    }
4936
4937
    /**
4938
     * Pick a random value from the values of this array.
4939
     *
4940
     * EXAMPLE: <code>
4941
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
4942
     * </code>
4943
     *
4944
     * @return mixed
4945
     *               <p>Get a random value or null if there wasn't a value.</p>
4946
     */
4947 4
    public function randomValue()
4948
    {
4949 4
        $result = $this->randomImmutable();
4950
4951 4
        if (!isset($result[0])) {
4952
            $result[0] = null;
4953
        }
4954
4955 4
        return $result[0];
4956
    }
4957
4958
    /**
4959
     * Pick a given number of random values out of this array.
4960
     *
4961
     * EXAMPLE: <code>
4962
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
4963
     * </code>
4964
     *
4965
     * @param int $number
4966
     *
4967
     * @return static
4968
     *                <p>(Mutable)</p>
4969
     *
4970
     * @psalm-return static<TKey,T>
4971
     */
4972 7
    public function randomValues(int $number): self
4973
    {
4974 7
        return $this->randomMutable($number);
4975
    }
4976
4977
    /**
4978
     * Get a random value from an array, with the ability to skew the results.
4979
     *
4980
     * EXAMPLE: <code>
4981
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
4982
     * </code>
4983
     *
4984
     * @param array    $array
4985
     * @param int|null $number <p>How many values you will take?</p>
4986
     *
4987
     * @return static<int,mixed>
0 ignored issues
show
Documentation introduced by
The doc-type static<int,mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (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...
4988
     *                           <p>(Immutable)</p>
4989
     *
4990
     * @psalm-param  array<mixed,mixed> $array
4991
     * @psalm-return static<int|array-key,T>
4992
     */
4993 9
    public function randomWeighted(array $array, int $number = null): self
4994
    {
4995
        // init
4996 9
        $options = [];
4997
4998 9
        foreach ($array as $option => $weight) {
4999 9
            if ($this->searchIndex($option) !== false) {
5000 9
                for ($i = 0; $i < $weight; ++$i) {
5001 1
                    $options[] = $option;
5002
                }
5003
            }
5004
        }
5005
5006 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5007
    }
5008
5009
    /**
5010
     * Reduce the current array via callable e.g. anonymous-function.
5011
     *
5012
     * EXAMPLE: <code>
5013
     * function myReducer($resultArray, $value) {
5014
     *     if ($value == 'foo') {
5015
     *         $resultArray[] = $value;
5016
     *     }
5017
     *     return $resultArray;
5018
     * };
5019
     * a(['foo', 'bar'])->reduce('myReducer'); // Arrayy['foo']
5020
     * </cdde>
5021
     *
5022
     * @param callable $callable
5023
     * @param mixed    $init
5024
     *
5025
     * @return static
5026
     *                <p>(Immutable)</p>
5027
     *
5028
     * @psalm-return static<TKey,T>
5029
     * @psalm-mutation-free
5030
     */
5031 18
    public function reduce($callable, $init = []): self
5032
    {
5033 18
        if ($this->generator) {
5034 1
            $result = $init;
5035
5036 1
            foreach ($this->getGenerator() as $value) {
5037 1
                $result = $callable($result, $value);
5038
            }
5039
5040 1
            return static::create(
5041 1
                $result,
5042 1
                $this->iteratorClass,
5043 1
                false
5044
            );
5045
        }
5046
5047 18
        $result = \array_reduce($this->array, $callable, $init);
5048
5049 18
        if ($result === null) {
5050
            $this->array = [];
5051
        } else {
5052 18
            $this->array = (array) $result;
5053
        }
5054
5055 18
        return static::create(
5056 18
            $this->array,
5057 18
            $this->iteratorClass,
5058 18
            false
5059
        );
5060
    }
5061
5062
    /**
5063
     * @param bool $unique
5064
     *
5065
     * @return static
5066
     *                <p>(Immutable)</p>
5067
     *
5068
     * @psalm-return static<TKey,T>
5069
     * @psalm-mutation-free
5070
     */
5071 14
    public function reduce_dimension(bool $unique = true): self
5072
    {
5073
        // init
5074 14
        $result = [];
5075
5076 14
        foreach ($this->getGenerator() as $val) {
5077 12
            if (\is_array($val)) {
5078 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5079
            } else {
5080 12
                $result[] = [$val];
5081
            }
5082
        }
5083
5084 14
        $result = $result === [] ? [] : \array_merge(...$result);
5085
5086 14
        $resultArrayy = new static($result);
5087
5088
        /**
5089
         * @psalm-suppress ImpureMethodCall - object is already re-created
5090
         * @psalm-suppress InvalidReturnStatement - why?
5091
         */
5092 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5093
    }
5094
5095
    /**
5096
     * Create a numerically re-indexed Arrayy object.
5097
     *
5098
     * EXAMPLE: <code>
5099
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5100
     * </code>
5101
     *
5102
     * @return $this
5103
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5104
     *
5105
     * @psalm-return static<TKey,T>
5106
     */
5107 9
    public function reindex(): self
5108
    {
5109 9
        $this->generatorToArray(false);
5110
5111 9
        $this->array = \array_values($this->array);
5112
5113 9
        return $this;
5114
    }
5115
5116
    /**
5117
     * Return all items that fail the truth test.
5118
     *
5119
     * EXAMPLE: <code>
5120
     * $closure = function ($value) {
5121
     *     return $value % 2 !== 0;
5122
     * }
5123
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5124
     * </code>
5125
     *
5126
     * @param \Closure $closure
5127
     *
5128
     * @return static
5129
     *                <p>(Immutable)</p>
5130
     *
5131
     * @psalm-return static<TKey,T>
5132
     * @psalm-mutation-free
5133
     */
5134 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...
5135
    {
5136
        // init
5137 1
        $filtered = [];
5138
5139 1
        foreach ($this->getGenerator() as $key => $value) {
5140 1
            if (!$closure($value, $key)) {
5141 1
                $filtered[$key] = $value;
5142
            }
5143
        }
5144
5145 1
        return static::create(
5146 1
            $filtered,
5147 1
            $this->iteratorClass,
5148 1
            false
5149
        );
5150
    }
5151
5152
    /**
5153
     * Remove a value from the current array (optional using dot-notation).
5154
     *
5155
     * EXAMPLE: <code>
5156
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5157
     * </code>
5158
     *
5159
     * @param mixed $key
5160
     *
5161
     * @return static
5162
     *                <p>(Mutable)</p>
5163
     *
5164
     * @psalm-param  TKey $key
5165
     * @psalm-return static<TKey,T>
5166
     */
5167 22
    public function remove($key)
5168
    {
5169
        // recursive call
5170 22
        if (\is_array($key)) {
5171 1
            foreach ($key as $k) {
5172 1
                $this->internalRemove($k);
5173
            }
5174
5175 1
            return static::create(
5176 1
                $this->toArray(),
5177 1
                $this->iteratorClass,
5178 1
                false
5179
            );
5180
        }
5181
5182 21
        $this->internalRemove($key);
5183
5184 21
        return static::create(
5185 21
            $this->toArray(),
5186 21
            $this->iteratorClass,
5187 21
            false
5188
        );
5189
    }
5190
5191
    /**
5192
     * alias: for "Arrayy->removeValue()"
5193
     *
5194
     * @param mixed $element
5195
     *
5196
     * @return static
5197
     *                <p>(Immutable)</p>
5198
     *
5199
     * @psalm-param  T $element
5200
     * @psalm-return static<TKey,T>
5201
     * @psalm-mutation-free
5202
     */
5203 8
    public function removeElement($element)
5204
    {
5205 8
        return $this->removeValue($element);
5206
    }
5207
5208
    /**
5209
     * Remove the first value from the current array.
5210
     *
5211
     * EXAMPLE: <code>
5212
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5213
     * </code>
5214
     *
5215
     * @return static
5216
     *                <p>(Immutable)</p>
5217
     *
5218
     * @psalm-return static<TKey,T>
5219
     * @psalm-mutation-free
5220
     */
5221 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...
5222
    {
5223 7
        $tmpArray = $this->toArray();
5224
5225 7
        \array_shift($tmpArray);
5226
5227 7
        return static::create(
5228 7
            $tmpArray,
5229 7
            $this->iteratorClass,
5230 7
            false
5231
        );
5232
    }
5233
5234
    /**
5235
     * Remove the last value from the current array.
5236
     *
5237
     * EXAMPLE: <code>
5238
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5239
     * </code>
5240
     *
5241
     * @return static
5242
     *                <p>(Immutable)</p>
5243
     *
5244
     * @psalm-return static<TKey,T>
5245
     * @psalm-mutation-free
5246
     */
5247 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...
5248
    {
5249 7
        $tmpArray = $this->toArray();
5250
5251 7
        \array_pop($tmpArray);
5252
5253 7
        return static::create(
5254 7
            $tmpArray,
5255 7
            $this->iteratorClass,
5256 7
            false
5257
        );
5258
    }
5259
5260
    /**
5261
     * Removes a particular value from an array (numeric or associative).
5262
     *
5263
     * EXAMPLE: <code>
5264
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5265
     * </code>
5266
     *
5267
     * @param mixed $value
5268
     *
5269
     * @return static
5270
     *                <p>(Immutable)</p>
5271
     *
5272
     * @psalm-param  T $value
5273
     * @psalm-return static<TKey,T>
5274
     * @psalm-mutation-free
5275
     */
5276 8
    public function removeValue($value): self
5277
    {
5278 8
        $this->generatorToArray();
5279
5280
        // init
5281 8
        $isSequentialArray = $this->isSequential();
5282
5283 8
        foreach ($this->array as $key => $item) {
5284 7
            if ($item === $value) {
5285 7
                unset($this->array[$key]);
5286
            }
5287
        }
5288
5289 8
        if ($isSequentialArray) {
5290 6
            $this->array = \array_values($this->array);
5291
        }
5292
5293 8
        return static::create(
5294 8
            $this->array,
5295 8
            $this->iteratorClass,
5296 8
            false
5297
        );
5298
    }
5299
5300
    /**
5301
     * Generate array of repeated arrays.
5302
     *
5303
     * @param int $times <p>How many times has to be repeated.</p>
5304
     *
5305
     * @return static
5306
     *                <p>(Immutable)</p>
5307
     *
5308
     * @psalm-return static<TKey,T>
5309
     * @psalm-mutation-free
5310
     */
5311 1
    public function repeat($times): self
5312
    {
5313 1
        if ($times === 0) {
5314 1
            return static::create([], $this->iteratorClass);
5315
        }
5316
5317 1
        return static::create(
5318 1
            \array_fill(0, (int) $times, $this->toArray()),
5319 1
            $this->iteratorClass,
5320 1
            false
5321
        );
5322
    }
5323
5324
    /**
5325
     * Replace a key with a new key/value pair.
5326
     *
5327
     * EXAMPLE: <code>
5328
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5329
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5330
     * </code>
5331
     *
5332
     * @param mixed $oldKey
5333
     * @param mixed $newKey
5334
     * @param mixed $newValue
5335
     *
5336
     * @return static
5337
     *                <p>(Immutable)</p>
5338
     *
5339
     * @psalm-return static<TKey,T>
5340
     * @psalm-mutation-free
5341
     */
5342 5
    public function replace($oldKey, $newKey, $newValue): self
5343
    {
5344 5
        $that = clone $this;
5345
5346
        /**
5347
         * @psalm-suppress ImpureMethodCall - object is already cloned
5348
         */
5349 5
        return $that->remove($oldKey)
5350 5
            ->set($newKey, $newValue);
5351
    }
5352
5353
    /**
5354
     * Create an array using the current array as values and the other array as keys.
5355
     *
5356
     * EXAMPLE: <code>
5357
     * $firstArray = [
5358
     *     1 => 'one',
5359
     *     2 => 'two',
5360
     *     3 => 'three',
5361
     * ];
5362
     * $secondArray = [
5363
     *     'one' => 1,
5364
     *     1     => 'one',
5365
     *     2     => 2,
5366
     * ];
5367
     * $arrayy = a($firstArray);
5368
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5369
     * </code>
5370
     *
5371
     * @param array $keys <p>An array of keys.</p>
5372
     *
5373
     * @return static
5374
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5375
     *
5376
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5377
     * @psalm-return static<TKey,T>
5378
     * @psalm-mutation-free
5379
     */
5380 2
    public function replaceAllKeys(array $keys): self
5381
    {
5382 2
        return static::create(
5383 2
            \array_combine($keys, $this->toArray()),
5384 2
            $this->iteratorClass,
5385 2
            false
5386
        );
5387
    }
5388
5389
    /**
5390
     * Create an array using the current array as keys and the other array as values.
5391
     *
5392
     * EXAMPLE: <code>
5393
     * $firstArray = [
5394
     *     1 => 'one',
5395
     *     2 => 'two',
5396
     *     3 => 'three',
5397
     * ];
5398
     * $secondArray = [
5399
     *     'one' => 1,
5400
     *     1     => 'one',
5401
     *     2     => 2,
5402
     * ];
5403
     * $arrayy = a($firstArray);
5404
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5405
     * </code>
5406
     *
5407
     * @param array $array <p>An array of values.</p>
5408
     *
5409
     * @return static
5410
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5411
     *
5412
     * @psalm-param  array<mixed,T> $array
5413
     * @psalm-return static<TKey,T>
5414
     * @psalm-mutation-free
5415
     */
5416 2
    public function replaceAllValues(array $array): self
5417
    {
5418 2
        return static::create(
5419 2
            \array_combine($this->array, $array),
5420 2
            $this->iteratorClass,
5421 2
            false
5422
        );
5423
    }
5424
5425
    /**
5426
     * Replace the keys in an array with another set.
5427
     *
5428
     * EXAMPLE: <code>
5429
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5430
     * </code>
5431
     *
5432
     * @param array $keys <p>An array of keys matching the array's size</p>
5433
     *
5434
     * @return static
5435
     *                <p>(Immutable)</p>
5436
     *
5437
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5438
     * @psalm-return static<TKey,T>
5439
     * @psalm-mutation-free
5440
     */
5441 1
    public function replaceKeys(array $keys): self
5442
    {
5443 1
        $values = \array_values($this->toArray());
5444 1
        $result = \array_combine($keys, $values);
5445
5446 1
        return static::create(
5447 1
            $result,
5448 1
            $this->iteratorClass,
5449 1
            false
5450
        );
5451
    }
5452
5453
    /**
5454
     * Replace the first matched value in an array.
5455
     *
5456
     * EXAMPLE: <code>
5457
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5458
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5459
     * </code>
5460
     *
5461
     * @param mixed $search      <p>The value to replace.</p>
5462
     * @param mixed $replacement <p>The value to replace.</p>
5463
     *
5464
     * @return static
5465
     *                <p>(Immutable)</p>
5466
     *
5467
     * @psalm-return static<TKey,T>
5468
     * @psalm-mutation-free
5469
     */
5470 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): 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...
5471
    {
5472 3
        $array = $this->toArray();
5473 3
        $key = \array_search($search, $array, true);
5474
5475 3
        if ($key !== false) {
5476 3
            $array[$key] = $replacement;
5477
        }
5478
5479 3
        return static::create(
5480 3
            $array,
5481 3
            $this->iteratorClass,
5482 3
            false
5483
        );
5484
    }
5485
5486
    /**
5487
     * Replace values in the current array.
5488
     *
5489
     * EXAMPLE: <code>
5490
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5491
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5492
     * </code>
5493
     *
5494
     * @param mixed $search      <p>The value to replace.</p>
5495
     * @param mixed $replacement <p>What to replace it with.</p>
5496
     *
5497
     * @return static
5498
     *                <p>(Immutable)</p>
5499
     *
5500
     * @psalm-return static<TKey,T>
5501
     * @psalm-mutation-free
5502
     */
5503 1
    public function replaceValues($search, $replacement = ''): self
5504
    {
5505
        /**
5506
         * @psalm-suppress MissingClosureReturnType
5507
         * @psalm-suppress MissingClosureParamType
5508
         */
5509 1
        return $this->each(
5510
            static function ($value) use ($search, $replacement) {
5511 1
                return \str_replace($search, $replacement, $value);
5512 1
            }
5513
        );
5514
    }
5515
5516
    /**
5517
     * Get the last elements from index $from until the end of this array.
5518
     *
5519
     * EXAMPLE: <code>
5520
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5521
     * </code>
5522
     *
5523
     * @param int $from
5524
     *
5525
     * @return static
5526
     *                <p>(Immutable)</p>
5527
     *
5528
     * @psalm-return static<TKey,T>
5529
     * @psalm-mutation-free
5530
     */
5531 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...
5532
    {
5533 15
        $tmpArray = $this->toArray();
5534
5535 15
        return static::create(
5536 15
            \array_splice($tmpArray, $from),
5537 15
            $this->iteratorClass,
5538 15
            false
5539
        );
5540
    }
5541
5542
    /**
5543
     * Return the array in the reverse order.
5544
     *
5545
     * EXAMPLE: <code>
5546
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5547
     * </code>
5548
     *
5549
     * @return $this
5550
     *               <p>(Mutable) Return this Arrayy object.</p>
5551
     *
5552
     * @psalm-return static<TKey,T>
5553
     */
5554 9
    public function reverse(): self
5555
    {
5556 9
        $this->generatorToArray();
5557
5558 9
        $this->array = \array_reverse($this->array);
5559
5560 9
        return $this;
5561
    }
5562
5563
    /**
5564
     * Sort an array in reverse order.
5565
     *
5566
     * @param int $sort_flags [optional] <p>
5567
     *                        You may modify the behavior of the sort using the optional
5568
     *                        parameter sort_flags, for details
5569
     *                        see sort.
5570
     *                        </p>
5571
     *
5572
     * @return $this
5573
     *               <p>(Mutable) Return this Arrayy object.</p>
5574
     *
5575
     * @psalm-return static<TKey,T>
5576
     */
5577 4
    public function rsort(int $sort_flags = 0): self
5578
    {
5579 4
        $this->generatorToArray();
5580
5581 4
        \rsort($this->array, $sort_flags);
5582
5583 4
        return $this;
5584
    }
5585
5586
    /**
5587
     * Sort an array in reverse order.
5588
     *
5589
     * @param int $sort_flags [optional] <p>
5590
     *                        You may modify the behavior of the sort using the optional
5591
     *                        parameter sort_flags, for details
5592
     *                        see sort.
5593
     *                        </p>
5594
     *
5595
     * @return $this
5596
     *               <p>(Immutable) Return this Arrayy object.</p>
5597
     *
5598
     * @psalm-return static<TKey,T>
5599
     * @psalm-mutation-free
5600
     */
5601 4
    public function rsortImmutable(int $sort_flags = 0): self
5602
    {
5603 4
        $that = clone $this;
5604
5605
        /**
5606
         * @psalm-suppress ImpureMethodCall - object is already cloned
5607
         */
5608 4
        $that->rsort($sort_flags);
5609
5610 4
        return $that;
5611
    }
5612
5613
    /**
5614
     * Search for the first index of the current array via $value.
5615
     *
5616
     * EXAMPLE: <code>
5617
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5618
     * </code>
5619
     *
5620
     * @param mixed $value
5621
     *
5622
     * @return false|float|int|string
5623
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5624
     * @psalm-mutation-free
5625
     */
5626 21
    public function searchIndex($value)
5627
    {
5628 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5629 20
            if ($value === $valueFromArray) {
5630 20
                return $keyFromArray;
5631
            }
5632
        }
5633
5634 11
        return false;
5635
    }
5636
5637
    /**
5638
     * Search for the value of the current array via $index.
5639
     *
5640
     * EXAMPLE: <code>
5641
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5642
     * </code>
5643
     *
5644
     * @param mixed $index
5645
     *
5646
     * @return static
5647
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5648
     *
5649
     * @psalm-return static<TKey,T>
5650
     * @psalm-mutation-free
5651
     */
5652 9
    public function searchValue($index): self
5653
    {
5654 9
        $this->generatorToArray();
5655
5656
        // init
5657 9
        $return = [];
5658
5659 9
        if ($this->array === []) {
5660
            return static::create(
5661
                [],
5662
                $this->iteratorClass,
5663
                false
5664
            );
5665
        }
5666
5667
        // php cast "bool"-index into "int"-index
5668 9
        if ((bool) $index === $index) {
5669 1
            $index = (int) $index;
5670
        }
5671
5672 9
        if ($this->offsetExists($index)) {
5673 7
            $return = [$this->array[$index]];
5674
        }
5675
5676 9
        return static::create(
5677 9
            $return,
5678 9
            $this->iteratorClass,
5679 9
            false
5680
        );
5681
    }
5682
5683
    /**
5684
     * Set a value for the current array (optional using dot-notation).
5685
     *
5686
     * EXAMPLE: <code>
5687
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5688
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5689
     * </code>
5690
     *
5691
     * @param string $key   <p>The key to set.</p>
5692
     * @param mixed  $value <p>Its value.</p>
5693
     *
5694
     * @return $this
5695
     *               <p>(Mutable) Return this Arrayy object.</p>
5696
     *
5697
     * @psalm-param  TKey $key
5698
     * @psalm-param  T $value
5699
     * @psalm-return static<TKey,T>
5700
     */
5701 28
    public function set($key, $value): self
5702
    {
5703 28
        $this->internalSet($key, $value);
5704
5705 27
        return $this;
5706
    }
5707
5708
    /**
5709
     * Get a value from a array and set it if it was not.
5710
     *
5711
     * WARNING: this method only set the value, if the $key is not already set
5712
     *
5713
     * EXAMPLE: <code>
5714
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5715
     * $arrayy->setAndGet(1, 4); // 1
5716
     * $arrayy->setAndGet(0, 4); // 4
5717
     * </code>
5718
     *
5719
     * @param mixed $key      <p>The key</p>
5720
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5721
     *
5722
     * @return mixed
5723
     *               <p>(Mutable)</p>
5724
     */
5725 11
    public function setAndGet($key, $fallback = null)
5726
    {
5727 11
        $this->generatorToArray();
5728
5729
        // If the key doesn't exist, set it.
5730 11
        if (!$this->has($key)) {
5731 4
            $this->array = $this->set($key, $fallback)->toArray();
5732
        }
5733
5734 11
        return $this->get($key);
5735
    }
5736
5737
    /**
5738
     * Shifts a specified value off the beginning of array.
5739
     *
5740
     * @return mixed
5741
     *               <p>(Mutable) A shifted element from the current array.</p>
5742
     */
5743 5
    public function shift()
5744
    {
5745 5
        $this->generatorToArray();
5746
5747 5
        return \array_shift($this->array);
5748
    }
5749
5750
    /**
5751
     * Shuffle the current array.
5752
     *
5753
     * EXAMPLE: <code>
5754
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5755
     * </code>
5756
     *
5757
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5758
     * @param array $array  [optional]
5759
     *
5760
     * @return static
5761
     *                <p>(Immutable)</p>
5762
     *
5763
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5764
     * @psalm-return static<TKey,T>
5765
     *
5766
     * @noinspection BadExceptionsProcessingInspection
5767
     * @noinspection RandomApiMigrationInspection
5768
     * @noinspection NonSecureShuffleUsageInspection
5769
     */
5770 2
    public function shuffle(bool $secure = false, array $array = null): self
5771
    {
5772 2
        if ($array === null) {
5773 2
            $array = $this->toArray(false);
5774
        }
5775
5776 2
        if ($secure !== true) {
5777 2
            \shuffle($array);
5778
        } else {
5779 1
            $size = \count($array, \COUNT_NORMAL);
5780 1
            $keys = \array_keys($array);
5781 1
            for ($i = $size - 1; $i > 0; --$i) {
5782
                try {
5783 1
                    $r = \random_int(0, $i);
5784
                } catch (\Exception $e) {
5785
                    $r = \mt_rand(0, $i);
5786
                }
5787 1
                if ($r !== $i) {
5788 1
                    $temp = $array[$keys[$r]];
5789 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5790 1
                    $array[$keys[$i]] = $temp;
5791
                }
5792
            }
5793
        }
5794
5795 2
        foreach ($array as $key => $value) {
5796
            // check if recursive is needed
5797 2
            if (\is_array($value)) {
5798 2
                $array[$key] = $this->shuffle($secure, $value);
5799
            }
5800
        }
5801
5802 2
        return static::create(
5803 2
            $array,
5804 2
            $this->iteratorClass,
5805 2
            false
5806
        );
5807
    }
5808
5809
    /**
5810
     * Count the values from the current array.
5811
     *
5812
     * alias: for "Arrayy->count()"
5813
     *
5814
     * @param int $mode
5815
     *
5816
     * @return int
5817
     */
5818 20
    public function size(int $mode = \COUNT_NORMAL): int
5819
    {
5820 20
        return $this->count($mode);
5821
    }
5822
5823
    /**
5824
     * Checks whether array has exactly $size items.
5825
     *
5826
     * @param int $size
5827
     *
5828
     * @return bool
5829
     */
5830 1
    public function sizeIs(int $size): bool
5831
    {
5832
        // init
5833 1
        $itemsTempCount = 0;
5834
5835
        /** @noinspection PhpUnusedLocalVariableInspection */
5836
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5837 1
        foreach ($this->getGeneratorByReference() as &$value) {
5838 1
            ++$itemsTempCount;
5839 1
            if ($itemsTempCount > $size) {
5840 1
                return false;
5841
            }
5842
        }
5843
5844 1
        return $itemsTempCount === $size;
5845
    }
5846
5847
    /**
5848
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5849
     * smaller than $fromSize.
5850
     *
5851
     * @param int $fromSize
5852
     * @param int $toSize
5853
     *
5854
     * @return bool
5855
     */
5856 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5857
    {
5858 1
        if ($fromSize > $toSize) {
5859 1
            $tmp = $toSize;
5860 1
            $toSize = $fromSize;
5861 1
            $fromSize = $tmp;
5862
        }
5863
5864
        // init
5865 1
        $itemsTempCount = 0;
5866
5867 1
        foreach ($this->getGenerator() as $key => $value) {
5868 1
            ++$itemsTempCount;
5869 1
            if ($itemsTempCount > $toSize) {
5870 1
                return false;
5871
            }
5872
        }
5873
5874 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5875
    }
5876
5877
    /**
5878
     * Checks whether array has more than $size items.
5879
     *
5880
     * @param int $size
5881
     *
5882
     * @return bool
5883
     */
5884 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...
5885
    {
5886
        // init
5887 1
        $itemsTempCount = 0;
5888
5889 1
        foreach ($this->getGenerator() as $key => $value) {
5890 1
            ++$itemsTempCount;
5891 1
            if ($itemsTempCount > $size) {
5892 1
                return true;
5893
            }
5894
        }
5895
5896 1
        return $itemsTempCount > $size;
5897
    }
5898
5899
    /**
5900
     * Checks whether array has less than $size items.
5901
     *
5902
     * @param int $size
5903
     *
5904
     * @return bool
5905
     */
5906 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...
5907
    {
5908
        // init
5909 1
        $itemsTempCount = 0;
5910
5911 1
        foreach ($this->getGenerator() as $key => $value) {
5912 1
            ++$itemsTempCount;
5913 1
            if ($itemsTempCount > $size) {
5914 1
                return false;
5915
            }
5916
        }
5917
5918 1
        return $itemsTempCount < $size;
5919
    }
5920
5921
    /**
5922
     * Counts all elements in an array, or something in an object.
5923
     *
5924
     * <p>
5925
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
5926
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
5927
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
5928
     * implemented and used in PHP.
5929
     * </p>
5930
     *
5931
     * @return int
5932
     *             <p>
5933
     *             The number of elements in var, which is
5934
     *             typically an array, since anything else will have one
5935
     *             element.
5936
     *             </p>
5937
     *             <p>
5938
     *             If var is not an array or an object with
5939
     *             implemented Countable interface,
5940
     *             1 will be returned.
5941
     *             There is one exception, if var is &null;,
5942
     *             0 will be returned.
5943
     *             </p>
5944
     *             <p>
5945
     *             Caution: count may return 0 for a variable that isn't set,
5946
     *             but it may also return 0 for a variable that has been initialized with an
5947
     *             empty array. Use isset to test if a variable is set.
5948
     *             </p>
5949
     */
5950 10
    public function sizeRecursive(): int
5951
    {
5952 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
5953
    }
5954
5955
    /**
5956
     * Extract a slice of the array.
5957
     *
5958
     * @param int      $offset       <p>Slice begin index.</p>
5959
     * @param int|null $length       <p>Length of the slice.</p>
5960
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
5961
     *
5962
     * @return static
5963
     *                <p>(Immutable) A slice of the original array with length $length.</p>
5964
     *
5965
     * @psalm-return static<TKey,T>
5966
     * @psalm-mutation-free
5967
     */
5968 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
5969
    {
5970 5
        return static::create(
5971 5
            \array_slice(
5972 5
                $this->toArray(),
5973 5
                $offset,
5974 5
                $length,
5975 5
                $preserveKeys
5976
            ),
5977 5
            $this->iteratorClass,
5978 5
            false
5979
        );
5980
    }
5981
5982
    /**
5983
     * Sort the current array and optional you can keep the keys.
5984
     *
5985
     * EXAMPLE: <code>
5986
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
5987
     * </code>
5988
     *
5989
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5990
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
5991
     *                              <strong>SORT_NATURAL</strong></p>
5992
     * @param bool       $keepKeys
5993
     *
5994
     * @return static
5995
     *                <p>(Mutable) Return this Arrayy object.</p>
5996
     *
5997
     * @psalm-return static<TKey,T>
5998
     */
5999 20
    public function sort(
6000
        $direction = \SORT_ASC,
6001
        int $strategy = \SORT_REGULAR,
6002
        bool $keepKeys = false
6003
    ): self {
6004 20
        $this->generatorToArray();
6005
6006 20
        return $this->sorting(
6007 20
            $this->array,
6008 20
            $direction,
6009 20
            $strategy,
6010 20
            $keepKeys
6011
        );
6012
    }
6013
6014
    /**
6015
     * Sort the current array and optional you can keep the keys.
6016
     *
6017
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6018
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6019
     *                              <strong>SORT_NATURAL</strong></p>
6020
     * @param bool       $keepKeys
6021
     *
6022
     * @return static
6023
     *                <p>(Immutable) Return this Arrayy object.</p>
6024
     *
6025
     * @psalm-return static<TKey,T>
6026
     */
6027 12
    public function sortImmutable(
6028
        $direction = \SORT_ASC,
6029
        int $strategy = \SORT_REGULAR,
6030
        bool $keepKeys = false
6031
    ): self {
6032 12
        $that = clone $this;
6033
6034 12
        $that->generatorToArray();
6035
6036 12
        return $that->sorting(
6037 12
            $that->array,
6038 12
            $direction,
6039 12
            $strategy,
6040 12
            $keepKeys
6041
        );
6042
    }
6043
6044
    /**
6045
     * Sort the current array by key.
6046
     *
6047
     * EXAMPLE: <code>
6048
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6049
     * </code>
6050
     *
6051
     * @see http://php.net/manual/en/function.ksort.php
6052
     * @see http://php.net/manual/en/function.krsort.php
6053
     *
6054
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6055
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6056
     *                              <strong>SORT_NATURAL</strong></p>
6057
     *
6058
     * @return $this
6059
     *               <p>(Mutable) Return this Arrayy object.</p>
6060
     *
6061
     * @psalm-return static<TKey,T>
6062
     */
6063 18
    public function sortKeys(
6064
        $direction = \SORT_ASC,
6065
        int $strategy = \SORT_REGULAR
6066
    ): self {
6067 18
        $this->generatorToArray();
6068
6069 18
        $this->sorterKeys($this->array, $direction, $strategy);
6070
6071 18
        return $this;
6072
    }
6073
6074
    /**
6075
     * Sort the current array by key.
6076
     *
6077
     * @see          http://php.net/manual/en/function.ksort.php
6078
     * @see          http://php.net/manual/en/function.krsort.php
6079
     *
6080
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6081
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6082
     *                              <strong>SORT_NATURAL</strong></p>
6083
     *
6084
     * @return $this
6085
     *               <p>(Immutable) Return this Arrayy object.</p>
6086
     *
6087
     * @psalm-return static<TKey,T>
6088
     * @psalm-mutation-free
6089
     */
6090 8
    public function sortKeysImmutable(
6091
        $direction = \SORT_ASC,
6092
        int $strategy = \SORT_REGULAR
6093
    ): self {
6094 8
        $that = clone $this;
6095
6096
        /**
6097
         * @psalm-suppress ImpureMethodCall - object is already cloned
6098
         */
6099 8
        $that->sortKeys($direction, $strategy);
6100
6101 8
        return $that;
6102
    }
6103
6104
    /**
6105
     * Sort the current array by value.
6106
     *
6107
     * EXAMPLE: <code>
6108
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6109
     * </code>
6110
     *
6111
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6112
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6113
     *                              <strong>SORT_NATURAL</strong></p>
6114
     *
6115
     * @return static
6116
     *                <p>(Mutable)</p>
6117
     *
6118
     * @psalm-return static<TKey,T>
6119
     */
6120 1
    public function sortValueKeepIndex(
6121
        $direction = \SORT_ASC,
6122
        int $strategy = \SORT_REGULAR
6123
    ): self {
6124 1
        return $this->sort($direction, $strategy, true);
6125
    }
6126
6127
    /**
6128
     * Sort the current array by value.
6129
     *
6130
     * EXAMPLE: <code>
6131
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6132
     * </code>
6133
     *
6134
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6135
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6136
     *                              <strong>SORT_NATURAL</strong></p>
6137
     *
6138
     * @return static
6139
     *                <p>(Mutable)</p>
6140
     *
6141
     * @psalm-return static<TKey,T>
6142
     */
6143 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6144
    {
6145 1
        return $this->sort($direction, $strategy, false);
6146
    }
6147
6148
    /**
6149
     * Sort a array by value or by a closure.
6150
     *
6151
     * - If the sorter is null, the array is sorted naturally.
6152
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6153
     *
6154
     * EXAMPLE: <code>
6155
     * $testArray = range(1, 5);
6156
     * $under = a($testArray)->sorter(
6157
     *     function ($value) {
6158
     *         return $value % 2 === 0;
6159
     *     }
6160
     * );
6161
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6162
     * </code>
6163
     *
6164
     * @param callable|string|null $sorter
6165
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6166
     *                                        <strong>SORT_DESC</strong></p>
6167
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6168
     *                                        <strong>SORT_NATURAL</strong></p>
6169
     *
6170
     * @return static
6171
     *                <p>(Immutable)</p>
6172
     *
6173
     * @psalm-return static<TKey,T>
6174
     * @psalm-mutation-free
6175
     */
6176 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6177
    {
6178 1
        $array = $this->toArray();
6179 1
        $direction = $this->getDirection($direction);
6180
6181
        // Transform all values into their results.
6182 1
        if ($sorter) {
6183 1
            $arrayy = static::create(
6184 1
                $array,
6185 1
                $this->iteratorClass,
6186 1
                false
6187
            );
6188
6189
            /**
6190
             * @psalm-suppress MissingClosureReturnType
6191
             * @psalm-suppress MissingClosureParamType
6192
             */
6193 1
            $results = $arrayy->each(
6194
                static function ($value) use ($sorter) {
6195 1
                    if (\is_callable($sorter) === true) {
6196 1
                        return $sorter($value);
6197
                    }
6198
6199 1
                    return $sorter === $value;
6200 1
                }
6201
            );
6202
6203 1
            $results = $results->toArray();
6204
        } else {
6205 1
            $results = $array;
6206
        }
6207
6208
        // Sort by the results and replace by original values
6209 1
        \array_multisort($results, $direction, $strategy, $array);
6210
6211 1
        return static::create(
6212 1
            $array,
6213 1
            $this->iteratorClass,
6214 1
            false
6215
        );
6216
    }
6217
6218
    /**
6219
     * @param int      $offset
6220
     * @param int|null $length
6221
     * @param array    $replacement
6222
     *
6223
     * @return static
6224
     *                <p>(Immutable)</p>
6225
     *
6226
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6227
     * @psalm-return static<TKey,T>
6228
     * @psalm-mutation-free
6229
     */
6230 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6231
    {
6232 1
        $tmpArray = $this->toArray();
6233
6234 1
        \array_splice(
6235 1
            $tmpArray,
6236 1
            $offset,
6237 1
            $length ?? $this->count(),
6238 1
            $replacement
6239
        );
6240
6241 1
        return static::create(
6242 1
            $tmpArray,
6243 1
            $this->iteratorClass,
6244 1
            false
6245
        );
6246
    }
6247
6248
    /**
6249
     * Split an array in the given amount of pieces.
6250
     *
6251
     * EXAMPLE: <code>
6252
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6253
     * </code>
6254
     *
6255
     * @param int  $numberOfPieces
6256
     * @param bool $keepKeys
6257
     *
6258
     * @return static
6259
     *                <p>(Immutable)</p>
6260
     *
6261
     * @psalm-return static<TKey,T>
6262
     * @psalm-mutation-free
6263
     */
6264 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6265
    {
6266 1
        if ($keepKeys) {
6267 View Code Duplication
            $generator = function () use ($numberOfPieces) {
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...
6268 1
                $carry = [];
6269 1
                $i = 1;
6270 1
                foreach ($this->getGenerator() as $key => $value) {
6271 1
                    $carry[$key] = $value;
6272
6273 1
                    if ($i % $numberOfPieces !== 0) {
6274 1
                        ++$i;
6275
6276 1
                        continue;
6277
                    }
6278
6279 1
                    yield $carry;
6280
6281 1
                    $carry = [];
6282 1
                    $i = 1;
6283
                }
6284
6285 1
                if ($carry !== []) {
6286 1
                    yield $carry;
6287
                }
6288 1
            };
6289
        } else {
6290 View Code Duplication
            $generator = function () use ($numberOfPieces) {
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...
6291 1
                $carry = [];
6292 1
                $i = 1;
6293 1
                foreach ($this->getGenerator() as $key => $value) {
6294 1
                    $carry[] = $value;
6295
6296 1
                    if ($i % $numberOfPieces !== 0) {
6297 1
                        ++$i;
6298
6299 1
                        continue;
6300
                    }
6301
6302 1
                    yield $carry;
6303
6304 1
                    $carry = [];
6305 1
                    $i = 1;
6306
                }
6307
6308 1
                if ($carry !== []) {
6309 1
                    yield $carry;
6310
                }
6311 1
            };
6312
        }
6313
6314 1
        return static::create(
6315 1
            $generator,
6316 1
            $this->iteratorClass,
6317 1
            false
6318
        );
6319
    }
6320
6321
    /**
6322
     * Strip all empty items from the current array.
6323
     *
6324
     * EXAMPLE: <code>
6325
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6326
     * </code>
6327
     *
6328
     * @return static
6329
     *                <p>(Immutable)</p>
6330
     *
6331
     * @psalm-return static<TKey,T>
6332
     * @psalm-mutation-free
6333
     */
6334 1
    public function stripEmpty(): self
6335
    {
6336 1
        return $this->filter(
6337
            static function ($item) {
6338 1
                if ($item === null) {
6339 1
                    return false;
6340
                }
6341
6342 1
                return (bool) \trim((string) $item);
6343 1
            }
6344
        );
6345
    }
6346
6347
    /**
6348
     * Swap two values between positions by key.
6349
     *
6350
     * EXAMPLE: <code>
6351
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6352
     * </code>
6353
     *
6354
     * @param int|string $swapA <p>a key in the array</p>
6355
     * @param int|string $swapB <p>a key in the array</p>
6356
     *
6357
     * @return static
6358
     *                <p>(Immutable)</p>
6359
     *
6360
     * @psalm-return static<TKey,T>
6361
     * @psalm-mutation-free
6362
     */
6363 1
    public function swap($swapA, $swapB): self
6364
    {
6365 1
        $array = $this->toArray();
6366
6367 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6368
6369 1
        return static::create(
6370 1
            $array,
6371 1
            $this->iteratorClass,
6372 1
            false
6373
        );
6374
    }
6375
6376
    /**
6377
     * Get the current array from the "Arrayy"-object.
6378
     * alias for "getArray()"
6379
     *
6380
     * @param bool $convertAllArrayyElements <p>
6381
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6382
     *                                       </p>
6383
     * @param bool $preserveKeys             <p>
6384
     *                                       e.g.: A generator maybe return the same key more then once,
6385
     *                                       so maybe you will ignore the keys.
6386
     *                                       </p>
6387
     *
6388
     * @return array
6389
     *
6390
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6391
     * @psalm-mutation-free
6392
     */
6393 930
    public function toArray(
6394
        bool $convertAllArrayyElements = false,
6395
        bool $preserveKeys = true
6396
    ): array {
6397
        // init
6398 930
        $array = [];
6399
6400 930
        if ($convertAllArrayyElements) {
6401 2
            foreach ($this->getGenerator() as $key => $value) {
6402 2
                if ($value instanceof self) {
6403 1
                    $value = $value->toArray(
6404 1
                        $convertAllArrayyElements,
6405 1
                        $preserveKeys
6406
                    );
6407
                }
6408
6409 2
                if ($preserveKeys) {
6410 1
                    $array[$key] = $value;
6411
                } else {
6412 2
                    $array[] = $value;
6413
                }
6414
            }
6415
        } else {
6416 930
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6417
        }
6418
6419 930
        return $array;
6420
    }
6421
6422
    /**
6423
     * Get the current array from the "Arrayy"-object as list.
6424
     *
6425
     * @param bool $convertAllArrayyElements <p>
6426
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6427
     *                                       </p>
6428
     *
6429
     * @return array
6430
     *
6431
     * @psalm-return list<array<TKey,T>>
6432
     * @psalm-mutation-free
6433
     */
6434 1
    public function toList(bool $convertAllArrayyElements = false): array
6435
    {
6436 1
        return $this->toArray(
6437 1
            $convertAllArrayyElements,
6438 1
            false
6439
        );
6440
    }
6441
6442
    /**
6443
     * Convert the current array to JSON.
6444
     *
6445
     * EXAMPLE: <code>
6446
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6447
     * </code>
6448
     *
6449
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6450
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6451
     *
6452
     * @return string
6453
     */
6454 12
    public function toJson(int $options = 0, int $depth = 512): string
6455
    {
6456 12
        $return = \json_encode($this->toArray(), $options, $depth);
6457 12
        if ($return === false) {
6458
            return '';
6459
        }
6460
6461 12
        return $return;
6462
    }
6463
6464
    /**
6465
     * @param string[]|null $items  [optional]
6466
     * @param string[]      $helper [optional]
6467
     *
6468
     * @return static|static[]
6469
     *
6470
     * @psalm-return static<int, static<TKey,T>>
6471
     */
6472 1
    public function toPermutation(array $items = null, array $helper = []): self
6473
    {
6474
        // init
6475 1
        $return = [];
6476
6477 1
        if ($items === null) {
6478 1
            $items = $this->toArray();
6479
        }
6480
6481 1
        if (empty($items)) {
6482 1
            $return[] = $helper;
6483
        } else {
6484 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6485 1
                $new_items = $items;
6486 1
                $new_helper = $helper;
6487 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6488
                /** @noinspection PhpSillyAssignmentInspection */
6489
                /** @var string[] $new_items */
6490 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...
6491 1
                \array_unshift($new_helper, $tmp_helper);
6492
                /** @noinspection SlowArrayOperationsInLoopInspection */
6493 1
                $return = \array_merge(
6494 1
                    $return,
6495 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6496
                );
6497
            }
6498
        }
6499
6500 1
        return static::create(
6501 1
            $return,
6502 1
            $this->iteratorClass,
6503 1
            false
6504
        );
6505
    }
6506
6507
    /**
6508
     * Implodes array to a string with specified separator.
6509
     *
6510
     * @param string $separator [optional] <p>The element's separator.</p>
6511
     *
6512
     * @return string
6513
     *                <p>The string representation of array, separated by ",".</p>
6514
     */
6515 19
    public function toString(string $separator = ','): string
6516
    {
6517 19
        return $this->implode($separator);
6518
    }
6519
6520
    /**
6521
     * Return a duplicate free copy of the current array.
6522
     *
6523
     * EXAMPLE: <code>
6524
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6525
     * </code>
6526
     *
6527
     * @return $this
6528
     *               <p>(Mutable)</p>
6529
     *
6530
     * @psalm-return static<TKey,T>
6531
     */
6532 13
    public function uniqueNewIndex(): self
6533
    {
6534
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6535
6536
        /**
6537
         * @psalm-suppress MissingClosureReturnType
6538
         * @psalm-suppress MissingClosureParamType
6539
         */
6540 13
        $this->array = $this->reduce(
6541
            static function ($resultArray, $value) {
6542 12
                if (!\in_array($value, $resultArray, true)) {
6543 12
                    $resultArray[] = $value;
6544
                }
6545
6546 12
                return $resultArray;
6547 13
            },
6548 13
            []
6549 13
        )->toArray();
6550 13
        $this->generator = null;
6551
6552 13
        return $this;
6553
    }
6554
6555
    /**
6556
     * Return a duplicate free copy of the current array. (with the old keys)
6557
     *
6558
     * EXAMPLE: <code>
6559
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6560
     * </code>
6561
     *
6562
     * @return $this
6563
     *               <p>(Mutable)</p>
6564
     *
6565
     * @psalm-return static<TKey,T>
6566
     */
6567 11
    public function uniqueKeepIndex(): self
6568
    {
6569
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6570
6571
        // init
6572 11
        $array = $this->toArray();
6573
6574
        /**
6575
         * @psalm-suppress MissingClosureReturnType
6576
         * @psalm-suppress MissingClosureParamType
6577
         */
6578 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...
6579 11
            \array_keys($array),
6580
            static function ($resultArray, $key) use ($array) {
6581 10
                if (!\in_array($array[$key], $resultArray, true)) {
6582 10
                    $resultArray[$key] = $array[$key];
6583
                }
6584
6585 10
                return $resultArray;
6586 11
            },
6587 11
            []
6588
        );
6589 11
        $this->generator = null;
6590
6591 11
        return $this;
6592
    }
6593
6594
    /**
6595
     * alias: for "Arrayy->uniqueNewIndex()"
6596
     *
6597
     * @return static
6598
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6599
     *
6600
     * @see          Arrayy::unique()
6601
     *
6602
     * @psalm-return static<TKey,T>
6603
     */
6604 13
    public function unique(): self
6605
    {
6606 13
        return $this->uniqueNewIndex();
6607
    }
6608
6609
    /**
6610
     * Prepends one or more values to the beginning of array at once.
6611
     *
6612
     * @param array ...$args
6613
     *
6614
     * @return $this
6615
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6616
     *
6617
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6618
     * @psalm-return static<TKey,T>
6619
     */
6620 4
    public function unshift(...$args): self
6621
    {
6622 4
        $this->generatorToArray();
6623
6624 4
        \array_unshift($this->array, ...$args);
6625
6626 4
        return $this;
6627
    }
6628
6629
    /**
6630
     * Tests whether the given closure return something valid for all elements of this array.
6631
     *
6632
     * @param \Closure $closure the predicate
6633
     *
6634
     * @return bool
6635
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6636
     */
6637 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...
6638
    {
6639 1
        foreach ($this->getGenerator() as $key => $value) {
6640 1
            if (!$closure($value, $key)) {
6641 1
                return false;
6642
            }
6643
        }
6644
6645 1
        return true;
6646
    }
6647
6648
    /**
6649
     * Get all values from a array.
6650
     *
6651
     * EXAMPLE: <code>
6652
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6653
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6654
     * </code>
6655
     *
6656
     * @return static
6657
     *                <p>(Immutable)</p>
6658
     *
6659
     * @psalm-return static<TKey,T>
6660
     * @psalm-mutation-free
6661
     */
6662 2
    public function values(): self
6663
    {
6664 2
        return static::create(
6665
            function () {
6666
                /** @noinspection YieldFromCanBeUsedInspection */
6667 2
                foreach ($this->getGenerator() as $value) {
6668 2
                    yield $value;
6669
                }
6670 2
            },
6671 2
            $this->iteratorClass,
6672 2
            false
6673
        );
6674
    }
6675
6676
    /**
6677
     * Apply the given function to every element in the array, discarding the results.
6678
     *
6679
     * EXAMPLE: <code>
6680
     * $callable = function (&$value, $key) {
6681
     *     $value = $key;
6682
     * };
6683
     * $arrayy = a([1, 2, 3]);
6684
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6685
     * </code>
6686
     *
6687
     * @param callable $callable
6688
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6689
     * @param mixed    $userData  [optional] <p>
6690
     *                            If the optional $userData parameter is supplied,
6691
     *                            it will be passed as the third parameter to the $callable.
6692
     *                            </p>
6693
     *
6694
     * @return $this
6695
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6696
     *
6697
     * @psalm-return static<TKey,T>
6698
     */
6699 12
    public function walk(
6700
        $callable,
6701
        bool $recursive = false,
6702
        $userData = self::ARRAYY_HELPER_WALK
6703
    ): self {
6704 12
        $this->generatorToArray();
6705
6706 12
        if ($this->array !== []) {
6707 10
            if ($recursive === true) {
6708 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6709
                    \array_walk_recursive($this->array, $callable, $userData);
6710
                } else {
6711 5
                    \array_walk_recursive($this->array, $callable);
6712
                }
6713
            } else {
6714
                /** @noinspection NestedPositiveIfStatementsInspection */
6715 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6716
                    \array_walk($this->array, $callable, $userData);
6717
                } else {
6718 5
                    \array_walk($this->array, $callable);
6719
                }
6720
            }
6721
        }
6722
6723 12
        return $this;
6724
    }
6725
6726
    /**
6727
     * Returns a collection of matching items.
6728
     *
6729
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6730
     * @param mixed  $value                 the value to match
6731
     *
6732
     * @throws \InvalidArgumentException if property or method is not defined
6733
     *
6734
     * @return static
6735
     *
6736
     * @psalm-param  T $value
6737
     * @psalm-return static<TKey,T>
6738
     */
6739 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6740
    {
6741 1
        return $this->filter(
6742
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6743
                $accessorValue = $this->extractValue(
6744
                    $item,
6745
                    $keyOrPropertyOrMethod
6746
                );
6747
6748
                return $accessorValue === $value;
6749 1
            }
6750
        );
6751
    }
6752
6753
    /**
6754
     * Convert an array into a object.
6755
     *
6756
     * @param array $array
6757
     *
6758
     * @return \stdClass
6759
     *
6760
     * @psalm-param array<mixed,mixed> $array
6761
     */
6762 4
    final protected static function arrayToObject(array $array = []): \stdClass
6763
    {
6764
        // init
6765 4
        $object = new \stdClass();
6766
6767 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6768 1
            return $object;
6769
        }
6770
6771 3
        foreach ($array as $name => $value) {
6772 3
            if (\is_array($value)) {
6773 1
                $object->{$name} = static::arrayToObject($value);
6774
            } else {
6775 3
                $object->{$name} = $value;
6776
            }
6777
        }
6778
6779 3
        return $object;
6780
    }
6781
6782
    /**
6783
     * @param array|\Generator|null $input         <p>
6784
     *                                             An array containing keys to return.
6785
     *                                             </p>
6786
     * @param mixed|null            $search_values [optional] <p>
6787
     *                                             If specified, then only keys containing these values are returned.
6788
     *                                             </p>
6789
     * @param bool                  $strict        [optional] <p>
6790
     *                                             Determines if strict comparison (===) should be used during the
6791
     *                                             search.
6792
     *                                             </p>
6793
     *
6794
     * @return array
6795
     *               <p>an array of all the keys in input</p>
6796
     *
6797
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6798
     * @psalm-return array<TKey|mixed>
6799
     * @psalm-mutation-free
6800
     */
6801 11
    protected function array_keys_recursive(
6802
        $input = null,
6803
        $search_values = null,
6804
        bool $strict = true
6805
    ): array {
6806
        // init
6807 11
        $keys = [];
6808 11
        $keysTmp = [];
6809
6810 11
        if ($input === null) {
6811 4
            $input = $this->getGenerator();
6812
        }
6813
6814 11
        if ($search_values === null) {
6815 11
            foreach ($input as $key => $value) {
6816 11
                $keys[] = $key;
6817
6818
                // check if recursive is needed
6819 11
                if (\is_array($value)) {
6820 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6821
                }
6822
            }
6823
        } else {
6824 1
            $is_array_tmp = \is_array($search_values);
6825
6826 1
            foreach ($input as $key => $value) {
6827 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...
6828
                    (
6829 1
                        $is_array_tmp === false
6830
                        &&
6831 1
                        $strict === true
6832
                        &&
6833 1
                        $search_values === $value
6834
                    )
6835
                    ||
6836
                    (
6837 1
                        $is_array_tmp === false
6838
                        &&
6839 1
                        $strict === false
6840
                        &&
6841 1
                        $search_values == $value
6842
                    )
6843
                    ||
6844
                    (
6845 1
                        $is_array_tmp === true
6846
                        &&
6847 1
                        \in_array($value, $search_values, $strict)
6848
                    )
6849
                ) {
6850 1
                    $keys[] = $key;
6851
                }
6852
6853
                // check if recursive is needed
6854 1
                if (\is_array($value)) {
6855 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6856
                }
6857
            }
6858
        }
6859
6860 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6861
    }
6862
6863
    /**
6864
     * @param mixed      $path
6865
     * @param callable   $callable
6866
     * @param array|null $currentOffset
6867
     *
6868
     * @return void
6869
     *
6870
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6871
     * @psalm-mutation-free
6872
     */
6873 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6874
    {
6875 10
        $this->generatorToArray();
6876
6877 10
        if ($currentOffset === null) {
6878 10
            $currentOffset = &$this->array;
6879
        }
6880
6881 10
        $explodedPath = \explode($this->pathSeparator, $path);
6882 10
        if ($explodedPath === false) {
6883
            return;
6884
        }
6885
6886 10
        $nextPath = \array_shift($explodedPath);
6887
6888 10
        if (!isset($currentOffset[$nextPath])) {
6889 1
            return;
6890
        }
6891
6892 9
        if (!empty($explodedPath)) {
6893 1
            $this->callAtPath(
6894 1
                \implode($this->pathSeparator, $explodedPath),
6895 1
                $callable,
6896 1
                $currentOffset[$nextPath]
6897
            );
6898
        } else {
6899 9
            $callable($currentOffset[$nextPath]);
6900
        }
6901 9
    }
6902
6903
    /**
6904
     * Extracts the value of the given property or method from the object.
6905
     *
6906
     * @param static $object                <p>The object to extract the value from.</p>
6907
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
6908
     *                                      value should be extracted.</p>
6909
     *
6910
     * @throws \InvalidArgumentException if the method or property is not defined
6911
     *
6912
     * @return mixed
6913
     *               <p>The value extracted from the specified property or method.</p>
6914
     *
6915
     * @psalm-param self<TKey,T> $object
6916
     */
6917 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
6918
    {
6919 1
        if (isset($object[$keyOrPropertyOrMethod])) {
6920 1
            $return = $object->get($keyOrPropertyOrMethod);
6921
6922 1
            if ($return instanceof self) {
6923
                return $return->toArray();
6924
            }
6925
6926 1
            return $return;
6927
        }
6928
6929
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
6930
            return $object->{$keyOrPropertyOrMethod};
6931
        }
6932
6933
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
6934
            return $object->{$keyOrPropertyOrMethod}();
6935
        }
6936
6937
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
6938
    }
6939
6940
    /**
6941
     * create a fallback for array
6942
     *
6943
     * 1. use the current array, if it's a array
6944
     * 2. fallback to empty array, if there is nothing
6945
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
6946
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
6947
     * 5. call "__toArray()" on object, if the method exists
6948
     * 6. cast a string or object with "__toString()" into an array
6949
     * 7. throw a "InvalidArgumentException"-Exception
6950
     *
6951
     * @param mixed $data
6952
     *
6953
     * @throws \InvalidArgumentException
6954
     *
6955
     * @return array
6956
     *
6957
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6958
     */
6959 1205
    protected function fallbackForArray(&$data): array
6960
    {
6961 1205
        $data = $this->internalGetArray($data);
6962
6963 1205
        if ($data === null) {
6964 2
            throw new \InvalidArgumentException('Passed value should be a array');
6965
        }
6966
6967 1203
        return $data;
6968
    }
6969
6970
    /**
6971
     * @param bool $preserveKeys <p>
6972
     *                           e.g.: A generator maybe return the same key more then once,
6973
     *                           so maybe you will ignore the keys.
6974
     *                           </p>
6975
     *
6976
     * @return bool
6977
     *
6978
     * @noinspection ReturnTypeCanBeDeclaredInspection
6979
     * @psalm-mutation-free :/
6980
     */
6981 1117
    protected function generatorToArray(bool $preserveKeys = true)
6982
    {
6983 1117
        if ($this->generator) {
6984 2
            $this->array = $this->toArray(false, $preserveKeys);
6985 2
            $this->generator = null;
6986
6987 2
            return true;
6988
        }
6989
6990 1117
        return false;
6991
    }
6992
6993
    /**
6994
     * Get correct PHP constant for direction.
6995
     *
6996
     * @param int|string $direction
6997
     *
6998
     * @return int
6999
     * @psalm-mutation-free
7000
     */
7001 43
    protected function getDirection($direction): int
7002
    {
7003 43
        if ((string) $direction === $direction) {
7004 10
            $direction = \strtolower($direction);
7005
7006 10
            if ($direction === 'desc') {
7007 2
                $direction = \SORT_DESC;
7008
            } else {
7009 9
                $direction = \SORT_ASC;
7010
            }
7011
        }
7012
7013
        if (
7014 43
            $direction !== \SORT_DESC
7015
            &&
7016 43
            $direction !== \SORT_ASC
7017
        ) {
7018
            $direction = \SORT_ASC;
7019
        }
7020
7021 43
        return $direction;
7022
    }
7023
7024
    /**
7025
     * @return TypeCheckInterface[]
7026
     *
7027
     * @noinspection ReturnTypeCanBeDeclaredInspection
7028
     */
7029 24
    protected function getPropertiesFromPhpDoc()
7030
    {
7031 24
        static $PROPERTY_CACHE = [];
7032 24
        $cacheKey = 'Class::' . static::class;
7033
7034 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7035 22
            return $PROPERTY_CACHE[$cacheKey];
7036
        }
7037
7038
        // init
7039 4
        $properties = [];
7040
7041 4
        $reflector = new \ReflectionClass($this);
7042 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7043 4
        $docComment = $reflector->getDocComment();
7044 4 View Code Duplication
        if ($docComment) {
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...
7045 3
            $docblock = $factory->create($docComment);
7046
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7047 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7048 3
                $typeName = $tag->getVariableName();
7049
                /** @var string|null $typeName */
7050 3
                if ($typeName !== null) {
7051 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7052 3
                    if ($typeCheckPhpDoc !== null) {
7053 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7054
                    }
7055
                }
7056
            }
7057
        }
7058
7059
        /** @noinspection PhpAssignmentInConditionInspection */
7060 4
        while ($reflector = $reflector->getParentClass()) {
7061 4
            $docComment = $reflector->getDocComment();
7062 4 View Code Duplication
            if ($docComment) {
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...
7063 4
                $docblock = $factory->create($docComment);
7064
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7065 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7066 1
                    $typeName = $tag->getVariableName();
7067
                    /** @var string|null $typeName */
7068 1
                    if ($typeName !== null) {
7069 1
                        if (isset($properties[$typeName])) {
7070 1
                            continue;
7071
                        }
7072
7073 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7074 1
                        if ($typeCheckPhpDoc !== null) {
7075 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7076
                        }
7077
                    }
7078
                }
7079
            }
7080
        }
7081
7082 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7083
    }
7084
7085
    /**
7086
     * @param mixed $glue
7087
     * @param mixed $pieces
7088
     * @param bool  $useKeys
7089
     *
7090
     * @return string
7091
     * @psalm-mutation-free
7092
     */
7093 36
    protected function implode_recursive(
7094
        $glue = '',
7095
        $pieces = [],
7096
        bool $useKeys = false
7097
    ): string {
7098 36
        if ($pieces instanceof self) {
7099 1
            $pieces = $pieces->toArray();
7100
        }
7101
7102 36
        if (\is_array($pieces)) {
7103 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7104 36
            $pieces_count_not_zero = $pieces_count > 0;
7105
7106 36
            return \implode(
7107 36
                $glue,
7108 36
                \array_map(
7109 36
                    [$this, 'implode_recursive'],
7110 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7111 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7112
                )
7113
            );
7114
        }
7115
7116
        if (
7117 36
            \is_scalar($pieces) === true
7118
            ||
7119 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7120
        ) {
7121 32
            return (string) $pieces;
7122
        }
7123
7124 8
        return '';
7125
    }
7126
7127
    /**
7128
     * @param mixed                 $needle   <p>
7129
     *                                        The searched value.
7130
     *                                        </p>
7131
     *                                        <p>
7132
     *                                        If needle is a string, the comparison is done
7133
     *                                        in a case-sensitive manner.
7134
     *                                        </p>
7135
     * @param array|\Generator|null $haystack <p>
7136
     *                                        The array.
7137
     *                                        </p>
7138
     * @param bool                  $strict   [optional] <p>
7139
     *                                        If the third parameter strict is set to true
7140
     *                                        then the in_array function will also check the
7141
     *                                        types of the
7142
     *                                        needle in the haystack.
7143
     *                                        </p>
7144
     *
7145
     * @return bool
7146
     *              <p>true if needle is found in the array, false otherwise</p>
7147
     *
7148
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7149
     * @psalm-mutation-free
7150
     */
7151 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7152
    {
7153 19
        if ($haystack === null) {
7154
            $haystack = $this->getGenerator();
7155
        }
7156
7157 19
        foreach ($haystack as $item) {
7158 15
            if (\is_array($item)) {
7159 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7160
            } else {
7161
                /** @noinspection NestedPositiveIfStatementsInspection */
7162 15
                if ($strict === true) {
7163 15
                    $returnTmp = $item === $needle;
7164
                } else {
7165
                    $returnTmp = $item == $needle;
7166
                }
7167
            }
7168
7169 15
            if ($returnTmp === true) {
7170 15
                return true;
7171
            }
7172
        }
7173
7174 8
        return false;
7175
    }
7176
7177
    /**
7178
     * @param mixed $data
7179
     *
7180
     * @return array|null
7181
     *
7182
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7183
     */
7184 1205
    protected function internalGetArray(&$data)
7185
    {
7186 1205
        if (\is_array($data)) {
7187 1199
            return $data;
7188
        }
7189
7190 74
        if (!$data) {
7191 7
            return [];
7192
        }
7193
7194 73
        if (\is_object($data) === true) {
7195 66
            if ($data instanceof \ArrayObject) {
7196 5
                return $data->getArrayCopy();
7197
            }
7198
7199 62
            if ($data instanceof \Generator) {
7200
                return static::createFromGeneratorImmutable($data)->toArray();
7201
            }
7202
7203 62
            if ($data instanceof \Traversable) {
7204
                return static::createFromObject($data)->toArray();
7205
            }
7206
7207 62
            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...
7208
                return (array) $data->jsonSerialize();
7209
            }
7210
7211 62
            if (\method_exists($data, '__toArray')) {
7212
                return (array) $data->__toArray();
7213
            }
7214
7215 62
            if (\method_exists($data, '__toString')) {
7216
                return [(string) $data];
7217
            }
7218
        }
7219
7220 69
        if (\is_callable($data)) {
7221
            /**
7222
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7223
             */
7224 60
            $this->generator = new ArrayyRewindableGenerator($data);
7225
7226 60
            return [];
7227
        }
7228
7229 11
        if (\is_scalar($data)) {
7230 9
            return [$data];
7231
        }
7232
7233 2
        return null;
7234
    }
7235
7236
    /**
7237
     * Internal mechanics of remove method.
7238
     *
7239
     * @param mixed $key
7240
     *
7241
     * @return bool
7242
     */
7243 22
    protected function internalRemove($key): bool
7244
    {
7245 22
        $this->generatorToArray();
7246
7247
        if (
7248 22
            $this->pathSeparator
7249
            &&
7250 22
            (string) $key === $key
7251
            &&
7252 22
            \strpos($key, $this->pathSeparator) !== false
7253
        ) {
7254
            $path = \explode($this->pathSeparator, (string) $key);
7255
7256
            if ($path !== false) {
7257
                // crawl though the keys
7258
                while (\count($path, \COUNT_NORMAL) > 1) {
7259
                    $key = \array_shift($path);
7260
7261
                    if (!$this->has($key)) {
7262
                        return false;
7263
                    }
7264
7265
                    $this->array = &$this->array[$key];
7266
                }
7267
7268
                $key = \array_shift($path);
7269
            }
7270
        }
7271
7272 22
        unset($this->array[$key]);
7273
7274 22
        return true;
7275
    }
7276
7277
    /**
7278
     * Internal mechanic of set method.
7279
     *
7280
     * @param int|string|null $key
7281
     * @param mixed           $value
7282
     * @param bool            $checkProperties
7283
     *
7284
     * @return bool
7285
     */
7286 1055
    protected function internalSet(
7287
        $key,
7288
        &$value,
7289
        bool $checkProperties = true
7290
    ): bool {
7291
        if (
7292 1055
            $checkProperties === true
7293
            &&
7294 1055
            $this->properties !== []
7295
        ) {
7296 112
            $this->checkType($key, $value);
7297
        }
7298
7299 1053
        if ($key === null) {
7300
            return false;
7301
        }
7302
7303 1053
        $this->generatorToArray();
7304
7305
        /** @psalm-var array<int|string,mixed> $array */
7306 1053
        $array = &$this->array;
7307
7308
        /**
7309
         * https://github.com/vimeo/psalm/issues/2536
7310
         *
7311
         * @psalm-suppress PossiblyInvalidArgument
7312
         * @psalm-suppress InvalidScalarArgument
7313
         */
7314
        if (
7315 1053
            $this->pathSeparator
7316
            &&
7317 1053
            (string) $key === $key
7318
            &&
7319 1053
            \strpos($key, $this->pathSeparator) !== false
7320
        ) {
7321 9
            $path = \explode($this->pathSeparator, (string) $key);
7322
7323 9
            if ($path !== false) {
7324
                // crawl through the keys
7325 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7326 9
                    $key = \array_shift($path);
7327
7328 9
                    $array = &$array[$key];
7329
                }
7330
7331 9
                $key = \array_shift($path);
7332
            }
7333
        }
7334
7335 1053
        if ($array === null) {
7336 4
            $array = [];
7337 1050
        } elseif (!\is_array($array)) {
7338 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7339
        }
7340
7341 1053
        $array[$key] = $value;
7342
7343 1053
        return true;
7344
    }
7345
7346
    /**
7347
     * Convert a object into an array.
7348
     *
7349
     * @param mixed|object $object
7350
     *
7351
     * @return array|mixed
7352
     *
7353
     * @psalm-mutation-free
7354
     */
7355 5
    protected static function objectToArray($object)
7356
    {
7357 5
        if (!\is_object($object)) {
7358 4
            return $object;
7359
        }
7360
7361 5
        $object = \get_object_vars($object);
7362
7363
        /**
7364
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7365
         */
7366 5
        return \array_map(['static', 'objectToArray'], $object);
7367
    }
7368
7369
    /**
7370
     * @param array $data
7371
     * @param bool  $checkPropertiesInConstructor
7372
     *
7373
     * @return void
7374
     *
7375
     * @psalm-param array<mixed,T> $data
7376
     */
7377 1203
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7378
    {
7379 1203
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7380
                                        &&
7381 1203
                                        $checkPropertiesInConstructor === true;
7382
7383 1203
        if ($this->properties !== []) {
7384 99
            foreach ($data as $key => &$valueInner) {
7385 99
                $this->internalSet(
7386 99
                    $key,
7387 99
                    $valueInner,
7388 99
                    $checkPropertiesInConstructor
7389
                );
7390
            }
7391
        } else {
7392
            if (
7393 1123
                $this->checkPropertyTypes === true
7394
                ||
7395 1123
                $checkPropertiesInConstructor === true
7396
            ) {
7397 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7398
            }
7399
7400
            /** @var TypeCheckInterface[] $properties */
7401 1123
            $properties = $this->properties;
7402
7403
            if (
7404 1123
                $this->checkPropertiesMismatchInConstructor === true
7405
                &&
7406 1123
                \count($data) !== 0
7407
                &&
7408 1123
                \count(\array_diff_key($properties, $data)) > 0
7409
            ) {
7410 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...eys($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...
7411
            }
7412
7413 1122
            foreach ($data as $key => &$valueInner) {
7414 951
                $this->internalSet(
7415 951
                    $key,
7416 951
                    $valueInner,
7417 951
                    $checkPropertiesInConstructor
7418
                );
7419
            }
7420
        }
7421 1195
    }
7422
7423
    /**
7424
     * sorting keys
7425
     *
7426
     * @param array      $elements
7427
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7428
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7429
     *                              <strong>SORT_NATURAL</strong></p>
7430
     *
7431
     * @return $this
7432
     *               <p>(Mutable) Return this Arrayy object.</p>
7433
     *
7434
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7435
     * @psalm-return static<TKey,T>
7436
     */
7437 18
    protected function sorterKeys(
7438
        array &$elements,
7439
        $direction = \SORT_ASC,
7440
        int $strategy = \SORT_REGULAR
7441
    ): self {
7442 18
        $direction = $this->getDirection($direction);
7443
7444
        switch ($direction) {
7445 18
            case 'desc':
7446 18
            case \SORT_DESC:
7447 6
                \krsort($elements, $strategy);
7448
7449 6
                break;
7450 13
            case 'asc':
7451 13
            case \SORT_ASC:
7452
            default:
7453 13
                \ksort($elements, $strategy);
7454
        }
7455
7456 18
        return $this;
7457
    }
7458
7459
    /**
7460
     * @param array      $elements  <p>Warning: used as reference</p>
7461
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7462
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7463
     *                              <strong>SORT_NATURAL</strong></p>
7464
     * @param bool       $keepKeys
7465
     *
7466
     * @return $this
7467
     *               <p>(Mutable) Return this Arrayy object.</p>
7468
     *
7469
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7470
     * @psalm-return static<TKey,T>
7471
     */
7472 24
    protected function sorting(
7473
        array &$elements,
7474
        $direction = \SORT_ASC,
7475
        int $strategy = \SORT_REGULAR,
7476
        bool $keepKeys = false
7477
    ): self {
7478 24
        $direction = $this->getDirection($direction);
7479
7480 24
        if (!$strategy) {
7481 24
            $strategy = \SORT_REGULAR;
7482
        }
7483
7484
        switch ($direction) {
7485 24
            case 'desc':
7486 24
            case \SORT_DESC:
7487 13
                if ($keepKeys) {
7488 9
                    \arsort($elements, $strategy);
7489
                } else {
7490 4
                    \rsort($elements, $strategy);
7491
                }
7492
7493 13
                break;
7494 11
            case 'asc':
7495 11
            case \SORT_ASC:
7496
            default:
7497 11
                if ($keepKeys) {
7498 4
                    \asort($elements, $strategy);
7499
                } else {
7500 7
                    \sort($elements, $strategy);
7501
                }
7502
        }
7503
7504 24
        return $this;
7505
    }
7506
7507
    /**
7508
     * @param array $array
7509
     *
7510
     * @return array
7511
     *
7512
     * @psalm-mutation-free
7513
     */
7514 25
    private function getArrayRecursiveHelperArrayy(array $array)
7515
    {
7516 25
        if ($array === []) {
7517
            return [];
7518
        }
7519
7520 25
        \array_walk_recursive(
7521 25
            $array,
7522
            /**
7523
             * @param array|self $item
7524
             *
7525
             * @return void
7526
             */
7527
            static function (&$item) {
7528 25
                if ($item instanceof self) {
7529 1
                    $item = $item->getArray();
7530
                }
7531 25
            }
7532
        );
7533
7534 25
        return $array;
7535
    }
7536
7537
    /**
7538
     * @param int|string|null $key
7539
     * @param mixed           $value
7540
     *
7541
     * @return void
7542
     */
7543 112
    private function checkType($key, $value)
7544
    {
7545
        if (
7546 112
            $key !== null
7547
            &&
7548 112
            isset($this->properties[$key]) === false
7549
            &&
7550 112
            $this->checkPropertiesMismatch === true
7551
        ) {
7552
            throw new \TypeError('The key "' . $key . '" does not exists as "@property" phpdoc. (' . \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 . '" ...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...
7553
        }
7554
7555 112
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7556 97
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7557 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7558 24
            $this->properties[$key]->checkType($value);
7559
        }
7560 110
    }
7561
}
7562