Completed
Push — master ( ff32ef...16b7ec )
by Lars
01:38
created

Arrayy::contains()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6.288

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 3
dl 0
loc 22
ccs 8
cts 10
cp 0.8
crap 6.288
rs 8.9457
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 1207
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1207
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1205
        parent::__construct([], 0, $iteratorClass);
116
117 1205
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1197
        $this->setIteratorClass($iteratorClass);
120 1197
    }
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 1197
    public function setIteratorClass($iteratorClass)
848
    {
849 1197
        if (\class_exists($iteratorClass)) {
850 1197
            $this->iteratorClass = $iteratorClass;
851
852 1197
            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 1
                foreach ($values as $value) {
1012
                    $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 8
                $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
        if ($preserveKeys) {
1265
            $generator = function () use ($size) {
1266
                $values = [];
1267
                $tmpCounter = 0;
1268
                foreach ($this->getGenerator() as $key => $value) {
1269
                    ++$tmpCounter;
1270
1271
                    $values[$key] = $value;
1272
                    if ($tmpCounter === $size) {
1273
                        yield $values;
1274
1275
                        $values = [];
1276
                        $tmpCounter = 0;
1277
                    }
1278
                }
1279
1280
                if ($values !== []) {
1281
                    yield $values;
1282
                }
1283
            };
1284 View Code Duplication
        } else {
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...
1285
            $generator = function () use ($size) {
1286 5
                $values = [];
1287 5
                $tmpCounter = 0;
1288 5
                foreach ($this->getGenerator() as $key => $value) {
1289 5
                    ++$tmpCounter;
1290
1291 5
                    $values[] = $value;
1292 5
                    if ($tmpCounter === $size) {
1293 5
                        yield $values;
1294
1295 5
                        $values = [];
1296 5
                        $tmpCounter = 0;
1297
                    }
1298
                }
1299
1300 5
                if ($values !== []) {
1301 4
                    yield $values;
1302
                }
1303 5
            };
1304
        }
1305
1306 5
        return static::create(
1307 5
            $generator,
1308 5
            $this->iteratorClass,
1309 5
            false
1310
        );
1311
    }
1312
1313
    /**
1314
     * Clean all falsy values from the current array.
1315
     *
1316
     * EXAMPLE: <code>
1317
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1318
     * </code>
1319
     *
1320
     * @return static
1321
     *                <p>(Immutable)</p>
1322
     *
1323
     * @psalm-return static<TKey,T>
1324
     * @psalm-mutation-free
1325
     */
1326 8
    public function clean(): self
1327
    {
1328 8
        return $this->filter(
1329
            static function ($value) {
1330 7
                return (bool) $value;
1331 8
            }
1332
        );
1333
    }
1334
1335
    /**
1336
     * WARNING!!! -> Clear the current full array or a $key of it.
1337
     *
1338
     * EXAMPLE: <code>
1339
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1340
     * </code>
1341
     *
1342
     * @param int|int[]|string|string[]|null $key
1343
     *
1344
     * @return $this
1345
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1346
     *
1347
     * @psalm-return static<TKey,T>
1348
     */
1349 10
    public function clear($key = null): self
1350
    {
1351 10
        if ($key !== null) {
1352 3
            if (\is_array($key)) {
1353 1
                foreach ($key as $keyTmp) {
1354 1
                    $this->offsetUnset($keyTmp);
1355
                }
1356
            } else {
1357 2
                $this->offsetUnset($key);
1358
            }
1359
1360 3
            return $this;
1361
        }
1362
1363 7
        $this->array = [];
1364 7
        $this->generator = null;
1365
1366 7
        return $this;
1367
    }
1368
1369
    /**
1370
     * Check if an item is in the current array.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1, true])->contains(true); // true
1374
     * </code>
1375
     *
1376
     * @param float|int|string $value
1377
     * @param bool             $recursive
1378
     * @param bool             $strict
1379
     *
1380
     * @return bool
1381
     * @psalm-mutation-free
1382
     */
1383 24
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1384
    {
1385 24
        if ($recursive === true) {
1386 19
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1387
        }
1388
1389
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1390 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1391 11
            if ($strict) {
1392 11
                if ($value === $valueFromArray) {
1393 11
                    return true;
1394
                }
1395
            } else {
1396
                /** @noinspection NestedPositiveIfStatementsInspection */
1397
                if ($value == $valueFromArray) {
1398
                    return true;
1399
                }
1400
            }
1401
        }
1402
1403 7
        return false;
1404
    }
1405
1406
    /**
1407
     * Check if an (case-insensitive) string is in the current array.
1408
     *
1409
     * EXAMPLE: <code>
1410
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1411
     * </code>
1412
     *
1413
     * @param mixed $value
1414
     * @param bool  $recursive
1415
     *
1416
     * @return bool
1417
     * @psalm-mutation-free
1418
     *
1419
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1420
     */
1421 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1422
    {
1423 26
        if ($value === null) {
1424 2
            return false;
1425
        }
1426
1427 24
        if ($recursive === true) {
1428
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1429 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1430 22
                if (\is_array($valueTmp)) {
1431 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1432 5
                    if ($return === true) {
1433 5
                        return $return;
1434
                    }
1435 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1436 16
                    return true;
1437
                }
1438
            }
1439
1440 8
            return false;
1441
        }
1442
1443
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1444 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1445 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1446 8
                return true;
1447
            }
1448
        }
1449
1450 4
        return false;
1451
    }
1452
1453
    /**
1454
     * Check if the given key/index exists in the array.
1455
     *
1456
     * EXAMPLE: <code>
1457
     * a([1 => true])->containsKey(1); // true
1458
     * </code>
1459
     *
1460
     * @param int|string $key <p>key/index to search for</p>
1461
     *
1462
     * @return bool
1463
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1464
     *
1465
     * @psalm-mutation-free
1466
     */
1467 4
    public function containsKey($key): bool
1468
    {
1469 4
        return $this->offsetExists($key);
1470
    }
1471
1472
    /**
1473
     * Check if all given needles are present in the array as key/index.
1474
     *
1475
     * EXAMPLE: <code>
1476
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1477
     * </code>
1478
     *
1479
     * @param array $needles   <p>The keys you are searching for.</p>
1480
     * @param bool  $recursive
1481
     *
1482
     * @return bool
1483
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1484
     *
1485
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1486
     * @psalm-mutation-free
1487
     */
1488 2
    public function containsKeys(array $needles, $recursive = false): bool
1489
    {
1490 2
        if ($recursive === true) {
1491
            return
1492 2
                \count(
1493 2
                    \array_intersect(
1494 2
                        $needles,
1495 2
                        $this->keys(true)->toArray()
1496
                    ),
1497 2
                    \COUNT_RECURSIVE
1498
                )
1499
                ===
1500 2
                \count(
1501 2
                    $needles,
1502 2
                    \COUNT_RECURSIVE
1503
                );
1504
        }
1505
1506 1
        return \count(
1507 1
            \array_intersect($needles, $this->keys()->toArray()),
1508 1
            \COUNT_NORMAL
1509
        )
1510
                ===
1511 1
                \count(
1512 1
                    $needles,
1513 1
                    \COUNT_NORMAL
1514
                );
1515
    }
1516
1517
    /**
1518
     * Check if all given needles are present in the array as key/index.
1519
     *
1520
     * @param array $needles <p>The keys you are searching for.</p>
1521
     *
1522
     * @return bool
1523
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1524
     *
1525
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1526
     * @psalm-mutation-free
1527
     */
1528 1
    public function containsKeysRecursive(array $needles): bool
1529
    {
1530 1
        return $this->containsKeys($needles, true);
1531
    }
1532
1533
    /**
1534
     * alias: for "Arrayy->contains()"
1535
     *
1536
     * @param float|int|string $value
1537
     *
1538
     * @return bool
1539
     *
1540
     * @see Arrayy::contains()
1541
     * @psalm-mutation-free
1542
     */
1543 9
    public function containsValue($value): bool
1544
    {
1545 9
        return $this->contains($value);
1546
    }
1547
1548
    /**
1549
     * alias: for "Arrayy->contains($value, true)"
1550
     *
1551
     * @param float|int|string $value
1552
     *
1553
     * @return bool
1554
     *
1555
     * @see Arrayy::contains()
1556
     * @psalm-mutation-free
1557
     */
1558 18
    public function containsValueRecursive($value): bool
1559
    {
1560 18
        return $this->contains($value, true);
1561
    }
1562
1563
    /**
1564
     * Check if all given needles are present in the array.
1565
     *
1566
     * EXAMPLE: <code>
1567
     * a([1, true])->containsValues(array(1, true)); // true
1568
     * </code>
1569
     *
1570
     * @param array $needles
1571
     *
1572
     * @return bool
1573
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1574
     *
1575
     * @psalm-param array<mixed>|array<T> $needles
1576
     * @psalm-mutation-free
1577
     */
1578 1
    public function containsValues(array $needles): bool
1579
    {
1580 1
        return \count(
1581 1
            \array_intersect(
1582 1
                $needles,
1583 1
                $this->toArray()
1584
            ),
1585 1
            \COUNT_NORMAL
1586
        )
1587
               ===
1588 1
               \count(
1589 1
                   $needles,
1590 1
                   \COUNT_NORMAL
1591
               );
1592
    }
1593
1594
    /**
1595
     * Counts all the values of an array
1596
     *
1597
     * @see          http://php.net/manual/en/function.array-count-values.php
1598
     *
1599
     * @return static
1600
     *                <p>
1601
     *                (Immutable)
1602
     *                An associative Arrayy-object of values from input as
1603
     *                keys and their count as value.
1604
     *                </p>
1605
     *
1606
     * @psalm-return static<TKey,T>
1607
     * @psalm-mutation-free
1608
     */
1609 7
    public function countValues(): self
1610
    {
1611 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1612
    }
1613
1614
    /**
1615
     * Creates an Arrayy object.
1616
     *
1617
     * @param mixed  $data
1618
     * @param string $iteratorClass
1619
     * @param bool   $checkPropertiesInConstructor
1620
     *
1621
     * @return static
1622
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1623
     *
1624
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1625
     *
1626
     * @psalm-mutation-free
1627
     */
1628 724
    public static function create(
1629
        $data = [],
1630
        string $iteratorClass = ArrayyIterator::class,
1631
        bool $checkPropertiesInConstructor = true
1632
    ) {
1633 724
        return new static(
1634 724
            $data,
1635 724
            $iteratorClass,
1636 724
            $checkPropertiesInConstructor
1637
        );
1638
    }
1639
1640
    /**
1641
     * Flatten an array with the given character as a key delimiter.
1642
     *
1643
     * EXAMPLE: <code>
1644
     * $callable = function ($a, $b) {
1645
     *     if ($a == $b) {
1646
     *         return 0;
1647
     *     }
1648
     *     return ($a > $b) ? 1 : -1;
1649
     * };
1650
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1651
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1652
     * </code>
1653
     *
1654
     * @param string     $delimiter
1655
     * @param string     $prepend
1656
     * @param array|null $items
1657
     *
1658
     * @return array
1659
     */
1660 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1661
    {
1662
        // init
1663 2
        $flatten = [];
1664
1665 2
        if ($items === null) {
1666 2
            $items = $this->array;
1667
        }
1668
1669 2
        foreach ($items as $key => $value) {
1670 2
            if (\is_array($value) && $value !== []) {
1671 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1672
            } else {
1673 2
                $flatten[] = [$prepend . $key => $value];
1674
            }
1675
        }
1676
1677 2
        if (\count($flatten) === 0) {
1678
            return [];
1679
        }
1680
1681 2
        return \array_merge_recursive([], ...$flatten);
1682
    }
1683
1684
    /**
1685
     * WARNING: Creates an Arrayy object by reference.
1686
     *
1687
     * @param array $array
1688
     *
1689
     * @return $this
1690
     *               <p>(Mutable) Return this Arrayy object.</p>
1691
     *
1692
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1693
     */
1694 27
    public function createByReference(array &$array = []): self
1695
    {
1696 27
        $array = $this->fallbackForArray($array);
1697
1698 27
        $this->array = &$array;
1699
1700 27
        return $this;
1701
    }
1702
1703
    /**
1704
     * Create an new instance from a callable function which will return an Generator.
1705
     *
1706
     * @param callable $generatorFunction
1707
     *
1708
     * @return static
1709
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1710
     *
1711
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1712
     *
1713
     * @psalm-mutation-free
1714
     */
1715 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1716
    {
1717 7
        return self::create($generatorFunction);
1718
    }
1719
1720
    /**
1721
     * Create an new instance filled with a copy of values from a "Generator"-object.
1722
     *
1723
     * @param \Generator $generator
1724
     *
1725
     * @return static
1726
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1727
     *
1728
     * @psalm-param \Generator<array-key,mixed> $generator
1729
     *
1730
     * @psalm-mutation-free
1731
     */
1732 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1733
    {
1734 4
        return self::create(\iterator_to_array($generator, true));
1735
    }
1736
1737
    /**
1738
     * Create an new Arrayy object via JSON.
1739
     *
1740
     * @param string $json
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1744
     *
1745
     * @psalm-mutation-free
1746
     */
1747 5
    public static function createFromJson(string $json): self
1748
    {
1749 5
        return static::create(\json_decode($json, true));
1750
    }
1751
1752
    /**
1753
     * Create an new Arrayy object via JSON.
1754
     *
1755
     * @param array $array
1756
     *
1757
     * @return static
1758
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1759
     *
1760
     * @psalm-mutation-free
1761
     */
1762 1
    public static function createFromArray(array $array): self
1763
    {
1764 1
        return static::create($array);
1765
    }
1766
1767
    /**
1768
     * Create an new instance filled with values from an object that is iterable.
1769
     *
1770
     * @param \Traversable $object <p>iterable object</p>
1771
     *
1772
     * @return static
1773
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1774
     *
1775
     * @psalm-param \Traversable<array-key,mixed> $object
1776
     *
1777
     * @psalm-mutation-free
1778
     */
1779 4
    public static function createFromObject(\Traversable $object): self
1780
    {
1781
        // init
1782 4
        $arrayy = new static();
1783
1784 4
        if ($object instanceof self) {
1785 4
            $objectArray = $object->getGenerator();
1786
        } else {
1787
            $objectArray = $object;
1788
        }
1789
1790 4
        foreach ($objectArray as $key => $value) {
1791
            /**
1792
             * @psalm-suppress ImpureMethodCall - object is already re-created
1793
             */
1794 3
            $arrayy->internalSet($key, $value);
1795
        }
1796
1797 4
        return $arrayy;
1798
    }
1799
1800
    /**
1801
     * Create an new instance filled with values from an object.
1802
     *
1803
     * @param object $object
1804
     *
1805
     * @return static
1806
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1807
     *
1808
     * @psalm-mutation-free
1809
     */
1810 5
    public static function createFromObjectVars($object): self
1811
    {
1812 5
        return self::create(self::objectToArray($object));
1813
    }
1814
1815
    /**
1816
     * Create an new Arrayy object via string.
1817
     *
1818
     * @param string      $str       <p>The input string.</p>
1819
     * @param string|null $delimiter <p>The boundary string.</p>
1820
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1821
     *                               used.</p>
1822
     *
1823
     * @return static
1824
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1825
     *
1826
     * @psalm-mutation-free
1827
     */
1828 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1829
    {
1830 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...
1831 1
            \preg_match_all($regEx, $str, $array);
1832
1833 1
            if (!empty($array)) {
1834 1
                $array = $array[0];
1835
            }
1836
        } else {
1837
            /** @noinspection NestedPositiveIfStatementsInspection */
1838 9
            if ($delimiter !== null) {
1839 7
                $array = \explode($delimiter, $str);
1840
            } else {
1841 2
                $array = [$str];
1842
            }
1843
        }
1844
1845
        // trim all string in the array
1846
        /**
1847
         * @psalm-suppress MissingClosureParamType
1848
         */
1849 10
        \array_walk(
1850 10
            $array,
1851
            static function (&$val) {
1852 10
                if ((string) $val === $val) {
1853 10
                    $val = \trim($val);
1854
                }
1855 10
            }
1856
        );
1857
1858 10
        return static::create($array);
1859
    }
1860
1861
    /**
1862
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1863
     *
1864
     * @param \Traversable $traversable
1865
     *
1866
     * @return static
1867
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1868
     *
1869
     * @psalm-param \Traversable<array-key,mixed> $traversable
1870
     *
1871
     * @psalm-mutation-free
1872
     */
1873 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1874
    {
1875 1
        return self::create(\iterator_to_array($traversable, true));
1876
    }
1877
1878
    /**
1879
     * Create an new instance containing a range of elements.
1880
     *
1881
     * @param float|int|string $low  <p>First value of the sequence.</p>
1882
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1883
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1884
     *
1885
     * @return static
1886
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1887
     *
1888
     * @psalm-mutation-free
1889
     */
1890 2
    public static function createWithRange($low, $high, $step = 1): self
1891
    {
1892 2
        return static::create(\range($low, $high, $step));
1893
    }
1894
1895
    /**
1896
     * Gets the element of the array at the current internal iterator position.
1897
     *
1898
     * @return false|mixed
1899
     */
1900
    public function current()
1901
    {
1902
        return \current($this->array);
1903
    }
1904
1905
    /**
1906
     * Custom sort by index via "uksort".
1907
     *
1908
     * EXAMPLE: <code>
1909
     * $callable = function ($a, $b) {
1910
     *     if ($a == $b) {
1911
     *         return 0;
1912
     *     }
1913
     *     return ($a > $b) ? 1 : -1;
1914
     * };
1915
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1916
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1917
     * </code>
1918
     *
1919
     * @see          http://php.net/manual/en/function.uksort.php
1920
     *
1921
     * @param callable $function
1922
     *
1923
     * @throws \InvalidArgumentException
1924
     *
1925
     * @return $this
1926
     *               <p>(Mutable) Return this Arrayy object.</p>
1927
     *
1928
     * @psalm-return static<TKey,T>
1929
     */
1930 5
    public function customSortKeys(callable $function): self
1931
    {
1932 5
        $this->generatorToArray();
1933
1934 5
        \uksort($this->array, $function);
1935
1936 5
        return $this;
1937
    }
1938
1939
    /**
1940
     * Custom sort by index via "uksort".
1941
     *
1942
     * @see          http://php.net/manual/en/function.uksort.php
1943
     *
1944
     * @param callable $function
1945
     *
1946
     * @throws \InvalidArgumentException
1947
     *
1948
     * @return $this
1949
     *               <p>(Immutable) Return this Arrayy object.</p>
1950
     *
1951
     * @psalm-return static<TKey,T>
1952
     * @psalm-mutation-free
1953
     */
1954 1
    public function customSortKeysImmutable(callable $function): self
1955
    {
1956 1
        $that = clone $this;
1957
1958 1
        $that->generatorToArray();
1959
1960
        /**
1961
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1962
         */
1963 1
        \uksort($that->array, $function);
1964
1965 1
        return $that;
1966
    }
1967
1968
    /**
1969
     * Custom sort by value via "usort".
1970
     *
1971
     * EXAMPLE: <code>
1972
     * $callable = function ($a, $b) {
1973
     *     if ($a == $b) {
1974
     *         return 0;
1975
     *     }
1976
     *     return ($a > $b) ? 1 : -1;
1977
     * };
1978
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1979
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1980
     * </code>
1981
     *
1982
     * @see          http://php.net/manual/en/function.usort.php
1983
     *
1984
     * @param callable $function
1985
     *
1986
     * @throws \InvalidArgumentException
1987
     *
1988
     * @return $this
1989
     *               <p>(Mutable) Return this Arrayy object.</p>
1990
     *
1991
     * @psalm-return static<TKey,T>
1992
     */
1993 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...
1994
    {
1995 10
        if (\is_callable($function) === false) {
1996
            throw new \InvalidArgumentException('Passed function must be callable');
1997
        }
1998
1999 10
        $this->generatorToArray();
2000
2001 10
        \usort($this->array, $function);
2002
2003 10
        return $this;
2004
    }
2005
2006
    /**
2007
     * Custom sort by value via "usort".
2008
     *
2009
     * @see          http://php.net/manual/en/function.usort.php
2010
     *
2011
     * @param callable $function
2012
     *
2013
     * @throws \InvalidArgumentException
2014
     *
2015
     * @return $this
2016
     *               <p>(Immutable) Return this Arrayy object.</p>
2017
     *
2018
     * @psalm-return static<TKey,T>
2019
     * @psalm-mutation-free
2020
     */
2021 4
    public function customSortValuesImmutable($function): self
2022
    {
2023 4
        $that = clone $this;
2024
2025
        /**
2026
         * @psalm-suppress ImpureMethodCall - object is already cloned
2027
         */
2028 4
        $that->customSortValues($function);
2029
2030 4
        return $that;
2031
    }
2032
2033
    /**
2034
     * Delete the given key or keys.
2035
     *
2036
     * @param int|int[]|string|string[] $keyOrKeys
2037
     *
2038
     * @return void
2039
     */
2040 9
    public function delete($keyOrKeys)
2041
    {
2042 9
        $keyOrKeys = (array) $keyOrKeys;
2043
2044 9
        foreach ($keyOrKeys as $key) {
2045 9
            $this->offsetUnset($key);
2046
        }
2047 9
    }
2048
2049
    /**
2050
     * Return values that are only in the current array.
2051
     *
2052
     * EXAMPLE: <code>
2053
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
2054
     * </code>
2055
     *
2056
     * @param array ...$array
2057
     *
2058
     * @return static
2059
     *                <p>(Immutable)</p>
2060
     *
2061
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2062
     * @psalm-return static<TKey,T>
2063
     * @psalm-mutation-free
2064
     */
2065 13
    public function diff(...$array): self
2066
    {
2067 13
        return static::create(
2068 13
            \array_diff($this->toArray(), ...$array),
2069 13
            $this->iteratorClass,
2070 13
            false
2071
        );
2072
    }
2073
2074
    /**
2075
     * Return values that are only in the current array.
2076
     *
2077
     * @param array ...$array
2078
     *
2079
     * @return static
2080
     *                <p>(Immutable)</p>
2081
     *
2082
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2083
     * @psalm-return static<TKey,T>
2084
     * @psalm-mutation-free
2085
     */
2086 8
    public function diffKey(...$array): self
2087
    {
2088 8
        return static::create(
2089 8
            \array_diff_key($this->toArray(), ...$array),
2090 8
            $this->iteratorClass,
2091 8
            false
2092
        );
2093
    }
2094
2095
    /**
2096
     * Return values and Keys that are only in the current array.
2097
     *
2098
     * @param array $array
2099
     *
2100
     * @return static
2101
     *                <p>(Immutable)</p>
2102
     *
2103
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2104
     * @psalm-return static<TKey,T>
2105
     * @psalm-mutation-free
2106
     */
2107 8
    public function diffKeyAndValue(array $array = []): self
2108
    {
2109 8
        return static::create(
2110 8
            \array_diff_assoc($this->toArray(), $array),
2111 8
            $this->iteratorClass,
2112 8
            false
2113
        );
2114
    }
2115
2116
    /**
2117
     * Return values that are only in the current multi-dimensional array.
2118
     *
2119
     * EXAMPLE: <code>
2120
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2121
     * </code>
2122
     *
2123
     * @param array                 $array
2124
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2125
     *
2126
     * @return static
2127
     *                <p>(Immutable)</p>
2128
     *
2129
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2130
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2131
     * @psalm-return static<TKey,T>
2132
     * @psalm-mutation-free
2133
     */
2134 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2135
    {
2136
        // init
2137 1
        $result = [];
2138
2139
        if (
2140 1
            $helperVariableForRecursion !== null
2141
            &&
2142 1
            \is_array($helperVariableForRecursion)
2143
        ) {
2144
            $arrayForTheLoop = $helperVariableForRecursion;
2145
        } else {
2146 1
            $arrayForTheLoop = $this->getGenerator();
2147
        }
2148
2149 1
        foreach ($arrayForTheLoop as $key => $value) {
2150 1
            if ($value instanceof self) {
2151 1
                $value = $value->toArray();
2152
            }
2153
2154 1
            if (\array_key_exists($key, $array)) {
2155 1
                if ($value !== $array[$key]) {
2156 1
                    $result[$key] = $value;
2157
                }
2158
            } else {
2159 1
                $result[$key] = $value;
2160
            }
2161
        }
2162
2163 1
        return static::create(
2164 1
            $result,
2165 1
            $this->iteratorClass,
2166 1
            false
2167
        );
2168
    }
2169
2170
    /**
2171
     * Return values that are only in the new $array.
2172
     *
2173
     * EXAMPLE: <code>
2174
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2175
     * </code>
2176
     *
2177
     * @param array $array
2178
     *
2179
     * @return static
2180
     *                <p>(Immutable)</p>
2181
     *
2182
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2183
     * @psalm-return static<TKey,T>
2184
     * @psalm-mutation-free
2185
     */
2186 8
    public function diffReverse(array $array = []): self
2187
    {
2188 8
        return static::create(
2189 8
            \array_diff($array, $this->toArray()),
2190 8
            $this->iteratorClass,
2191 8
            false
2192
        );
2193
    }
2194
2195
    /**
2196
     * Divide an array into two arrays. One with keys and the other with values.
2197
     *
2198
     * EXAMPLE: <code>
2199
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2200
     * </code>
2201
     *
2202
     * @return static
2203
     *                <p>(Immutable)</p>
2204
     *
2205
     * @psalm-return static<TKey,T>
2206
     * @psalm-mutation-free
2207
     */
2208 1
    public function divide(): self
2209
    {
2210 1
        return static::create(
2211
            [
2212 1
                $this->keys(),
2213 1
                $this->values(),
2214
            ],
2215 1
            $this->iteratorClass,
2216 1
            false
2217
        );
2218
    }
2219
2220
    /**
2221
     * Iterate over the current array and modify the array's value.
2222
     *
2223
     * EXAMPLE: <code>
2224
     * $result = A::create();
2225
     * $closure = function ($value) {
2226
     *     return ':' . $value . ':';
2227
     * };
2228
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2229
     * </code>
2230
     *
2231
     * @param \Closure $closure
2232
     *
2233
     * @return static
2234
     *                <p>(Immutable)</p>
2235
     *
2236
     * @psalm-return static<TKey,T>
2237
     * @psalm-mutation-free
2238
     */
2239 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...
2240
    {
2241
        // init
2242 5
        $array = [];
2243
2244 5
        foreach ($this->getGenerator() as $key => $value) {
2245 5
            $array[$key] = $closure($value, $key);
2246
        }
2247
2248 5
        return static::create(
2249 5
            $array,
2250 5
            $this->iteratorClass,
2251 5
            false
2252
        );
2253
    }
2254
2255
    /**
2256
     * Sets the internal iterator to the last element in the array and returns this element.
2257
     *
2258
     * @return mixed
2259
     */
2260
    public function end()
2261
    {
2262
        return \end($this->array);
2263
    }
2264
2265
    /**
2266
     * Check if a value is in the current array using a closure.
2267
     *
2268
     * EXAMPLE: <code>
2269
     * $callable = function ($value, $key) {
2270
     *     return 2 === $key and 'two' === $value;
2271
     * };
2272
     * a(['foo', 2 => 'two'])->exists($callable); // true
2273
     * </code>
2274
     *
2275
     * @param \Closure $closure
2276
     *
2277
     * @return bool
2278
     *              <p>Returns true if the given value is found, false otherwise.</p>
2279
     */
2280 4
    public function exists(\Closure $closure): bool
2281
    {
2282
        // init
2283 4
        $isExists = false;
2284
2285 4
        foreach ($this->getGenerator() as $key => $value) {
2286 3
            if ($closure($value, $key)) {
2287 1
                $isExists = true;
2288
2289 1
                break;
2290
            }
2291
        }
2292
2293 4
        return $isExists;
2294
    }
2295
2296
    /**
2297
     * Fill the array until "$num" with "$default" values.
2298
     *
2299
     * EXAMPLE: <code>
2300
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2301
     * </code>
2302
     *
2303
     * @param int   $num
2304
     * @param mixed $default
2305
     *
2306
     * @return static
2307
     *                <p>(Immutable)</p>
2308
     *
2309
     * @psalm-return static<TKey,T>
2310
     * @psalm-mutation-free
2311
     */
2312 8
    public function fillWithDefaults(int $num, $default = null): self
2313
    {
2314 8
        if ($num < 0) {
2315 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2316
        }
2317
2318 7
        $this->generatorToArray();
2319
2320 7
        $tmpArray = $this->array;
2321
2322 7
        $count = \count($tmpArray);
2323
2324 7
        while ($count < $num) {
2325 4
            $tmpArray[] = $default;
2326 4
            ++$count;
2327
        }
2328
2329 7
        return static::create(
2330 7
            $tmpArray,
2331 7
            $this->iteratorClass,
2332 7
            false
2333
        );
2334
    }
2335
2336
    /**
2337
     * Find all items in an array that pass the truth test.
2338
     *
2339
     * EXAMPLE: <code>
2340
     * $closure = function ($value) {
2341
     *     return $value % 2 !== 0;
2342
     * }
2343
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2344
     * </code>
2345
     *
2346
     * @param \Closure|null $closure [optional] <p>
2347
     *                               The callback function to use
2348
     *                               </p>
2349
     *                               <p>
2350
     *                               If no callback is supplied, all entries of
2351
     *                               input equal to false (see
2352
     *                               converting to
2353
     *                               boolean) will be removed.
2354
     *                               </p>
2355
     * @param int           $flag    [optional] <p>
2356
     *                               Flag determining what arguments are sent to <i>callback</i>:
2357
     *                               </p>
2358
     *                               <ul>
2359
     *                               <li>
2360
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2361
     *                               to <i>callback</i> instead of the value
2362
     *                               </li>
2363
     *                               <li>
2364
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2365
     *                               arguments to <i>callback</i> instead of the value
2366
     *                               </li>
2367
     *                               </ul>
2368
     *
2369
     * @return static
2370
     *                <p>(Immutable)</p>
2371
     *
2372
     * @psalm-param null|\Closure(T=,TKey=):bool|\Closure(T=):bool||\Closure(TKey=):bool $closure
2373
     * @psalm-return static<TKey,T>
2374
     * @psalm-mutation-free
2375
     */
2376 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2377
    {
2378 12
        if (!$closure) {
2379 1
            return $this->clean();
2380
        }
2381
2382 12
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2383
            $generator = function () use ($closure) {
2384 1
                foreach ($this->getGenerator() as $key => $value) {
2385 1
                    if ($closure($key) === true) {
2386 1
                        yield $key => $value;
2387
                    }
2388
                }
2389 1
            };
2390 12
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2391
            $generator = function () use ($closure) {
2392 11
                foreach ($this->getGenerator() as $key => $value) {
2393 10
                    if ($closure($value, $key) === true) {
2394 9
                        yield $key => $value;
2395
                    }
2396
                }
2397 12
            };
2398
        } else {
2399 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...
2400 1
                foreach ($this->getGenerator() as $key => $value) {
2401 1
                    if ($closure($value) === true) {
2402 1
                        yield $key => $value;
2403
                    }
2404
                }
2405 1
            };
2406
        }
2407
2408 12
        return static::create(
2409 12
            $generator,
2410 12
            $this->iteratorClass,
2411 12
            false
2412
        );
2413
    }
2414
2415
    /**
2416
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2417
     * property within that.
2418
     *
2419
     * @param string          $property
2420
     * @param string|string[] $value
2421
     * @param string          $comparisonOp
2422
     *                                      <p>
2423
     *                                      'eq' (equals),<br />
2424
     *                                      'gt' (greater),<br />
2425
     *                                      'gte' || 'ge' (greater or equals),<br />
2426
     *                                      'lt' (less),<br />
2427
     *                                      'lte' || 'le' (less or equals),<br />
2428
     *                                      'ne' (not equals),<br />
2429
     *                                      'contains',<br />
2430
     *                                      'notContains',<br />
2431
     *                                      'newer' (via strtotime),<br />
2432
     *                                      'older' (via strtotime),<br />
2433
     *                                      </p>
2434
     *
2435
     * @return static
2436
     *                <p>(Immutable)</p>
2437
     *
2438
     * @psalm-return static<TKey,T>
2439
     * @psalm-mutation-free
2440
     *
2441
     * @psalm-suppress MissingClosureReturnType
2442
     * @psalm-suppress MissingClosureParamType
2443
     */
2444 1
    public function filterBy(
2445
        string $property,
2446
        $value,
2447
        string $comparisonOp = null
2448
    ): self {
2449 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...
2450 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
2451
        }
2452
2453
        $ops = [
2454
            'eq' => static function ($item, $prop, $value): bool {
2455 1
                return $item[$prop] === $value;
2456 1
            },
2457
            'gt' => static function ($item, $prop, $value): bool {
2458
                return $item[$prop] > $value;
2459 1
            },
2460
            'ge' => static function ($item, $prop, $value): bool {
2461
                return $item[$prop] >= $value;
2462 1
            },
2463
            'gte' => static function ($item, $prop, $value): bool {
2464
                return $item[$prop] >= $value;
2465 1
            },
2466
            'lt' => static function ($item, $prop, $value): bool {
2467 1
                return $item[$prop] < $value;
2468 1
            },
2469
            'le' => static function ($item, $prop, $value): bool {
2470
                return $item[$prop] <= $value;
2471 1
            },
2472
            'lte' => static function ($item, $prop, $value): bool {
2473
                return $item[$prop] <= $value;
2474 1
            },
2475
            'ne' => static function ($item, $prop, $value): bool {
2476
                return $item[$prop] !== $value;
2477 1
            },
2478
            'contains' => static function ($item, $prop, $value): bool {
2479 1
                return \in_array($item[$prop], (array) $value, true);
2480 1
            },
2481
            'notContains' => static function ($item, $prop, $value): bool {
2482
                return !\in_array($item[$prop], (array) $value, true);
2483 1
            },
2484
            'newer' => static function ($item, $prop, $value): bool {
2485
                return \strtotime($item[$prop]) > \strtotime($value);
2486 1
            },
2487
            'older' => static function ($item, $prop, $value): bool {
2488
                return \strtotime($item[$prop]) < \strtotime($value);
2489 1
            },
2490
        ];
2491
2492 1
        $result = \array_values(
2493 1
            \array_filter(
2494 1
                $this->toArray(false, true),
2495
                static function ($item) use (
2496 1
                    $property,
2497 1
                    $value,
2498 1
                    $ops,
2499 1
                    $comparisonOp
2500
                ) {
2501 1
                    $item = (array) $item;
2502 1
                    $itemArrayy = static::create($item);
2503 1
                    $item[$property] = $itemArrayy->get($property, []);
2504
2505 1
                    return $ops[$comparisonOp]($item, $property, $value);
2506 1
                }
2507
            )
2508
        );
2509
2510 1
        return static::create(
2511 1
            $result,
2512 1
            $this->iteratorClass,
2513 1
            false
2514
        );
2515
    }
2516
2517
    /**
2518
     * Find the first item in an array that passes the truth test, otherwise return false.
2519
     *
2520
     * EXAMPLE: <code>
2521
     * $search = 'foo';
2522
     * $closure = function ($value, $key) use ($search) {
2523
     *     return $value === $search;
2524
     * };
2525
     * a(['foo', 'bar', 'lall'])->find($closure); // 'foo'
2526
     * </code>
2527
     *
2528
     * @param \Closure $closure
2529
     *
2530
     * @return false|mixed
2531
     *                     <p>Return false if we did not find the value.</p>
2532
     */
2533 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...
2534
    {
2535 8
        foreach ($this->getGenerator() as $key => $value) {
2536 6
            if ($closure($value, $key)) {
2537 5
                return $value;
2538
            }
2539
        }
2540
2541 3
        return false;
2542
    }
2543
2544
    /**
2545
     * find by ...
2546
     *
2547
     * EXAMPLE: <code>
2548
     * $array = [
2549
     *     0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01'],
2550
     *     1 => ['id' => 456, 'name' => 'bar', 'group' => 'primary', 'value' => 1468, 'when' => '2014-07-15'],
2551
     * ];
2552
     * a($array)->filterBy('name', 'foo'); // Arrayy[0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01']]
2553
     * </code>
2554
     *
2555
     * @param string          $property
2556
     * @param string|string[] $value
2557
     * @param string          $comparisonOp
2558
     *
2559
     * @return static
2560
     *                <p>(Immutable)</p>
2561
     *
2562
     * @psalm-return static<TKey,T>
2563
     * @psalm-mutation-free
2564
     */
2565 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2566
    {
2567 1
        return $this->filterBy($property, $value, $comparisonOp);
2568
    }
2569
2570
    /**
2571
     * Get the first value from the current array.
2572
     *
2573
     * EXAMPLE: <code>
2574
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->first(); // 'foo'
2575
     * </code>
2576
     *
2577
     * @return mixed
2578
     *               <p>Return null if there wasn't a element.</p>
2579
     */
2580 23
    public function first()
2581
    {
2582 23
        $key_first = $this->firstKey();
2583 23
        if ($key_first === null) {
2584 3
            return null;
2585
        }
2586
2587 20
        return $this->get($key_first);
2588
    }
2589
2590
    /**
2591
     * Get the first key from the current array.
2592
     *
2593
     * @return mixed
2594
     *               <p>Return null if there wasn't a element.</p>
2595
     * @psalm-mutation-free
2596
     */
2597 30
    public function firstKey()
2598
    {
2599 30
        $this->generatorToArray();
2600
2601 30
        return \array_key_first($this->array);
2602
    }
2603
2604
    /**
2605
     * Get the first value(s) from the current array.
2606
     * And will return an empty array if there was no first entry.
2607
     *
2608
     * EXAMPLE: <code>
2609
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsImmutable(2); // Arrayy[0 => 'foo', 1 => 'bar']
2610
     * </code>
2611
     *
2612
     * @param int|null $number <p>How many values you will take?</p>
2613
     *
2614
     * @return static
2615
     *                <p>(Immutable)</p>
2616
     *
2617
     * @psalm-return static<TKey,T>
2618
     * @psalm-mutation-free
2619
     */
2620 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...
2621
    {
2622 37
        $arrayTmp = $this->toArray();
2623
2624 37
        if ($number === null) {
2625 14
            $array = (array) \array_shift($arrayTmp);
2626
        } else {
2627 23
            $array = \array_splice($arrayTmp, 0, $number);
2628
        }
2629
2630 37
        return static::create(
2631 37
            $array,
2632 37
            $this->iteratorClass,
2633 37
            false
2634
        );
2635
    }
2636
2637
    /**
2638
     * Get the first value(s) from the current array.
2639
     * And will return an empty array if there was no first entry.
2640
     *
2641
     * @param int|null $number <p>How many values you will take?</p>
2642
     *
2643
     * @return static
2644
     *                <p>(Immutable)</p>
2645
     *
2646
     * @psalm-return static<TKey,T>
2647
     * @psalm-mutation-free
2648
     */
2649 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...
2650
    {
2651 3
        $arrayTmp = $this->keys()->toArray();
2652
2653 3
        if ($number === null) {
2654
            $array = (array) \array_shift($arrayTmp);
2655
        } else {
2656 3
            $array = \array_splice($arrayTmp, 0, $number);
2657
        }
2658
2659 3
        return static::create(
2660 3
            $array,
2661 3
            $this->iteratorClass,
2662 3
            false
2663
        );
2664
    }
2665
2666
    /**
2667
     * Get and remove the first value(s) from the current array.
2668
     * And will return an empty array if there was no first entry.
2669
     *
2670
     * EXAMPLE: <code>
2671
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsMutable(); // 'foo'
2672
     * </code>
2673
     *
2674
     * @param int|null $number <p>How many values you will take?</p>
2675
     *
2676
     * @return $this
2677
     *               <p>(Mutable)</p>
2678
     *
2679
     * @psalm-return static<TKey,T>
2680
     */
2681 34
    public function firstsMutable(int $number = null): self
2682
    {
2683 34
        $this->generatorToArray();
2684
2685 34
        if ($number === null) {
2686 19
            $this->array = (array) \array_shift($this->array);
2687
        } else {
2688 15
            $this->array = \array_splice($this->array, 0, $number);
2689
        }
2690
2691 34
        return $this;
2692
    }
2693
2694
    /**
2695
     * Exchanges all keys with their associated values in an array.
2696
     *
2697
     * EXAMPLE: <code>
2698
     * a([0 => 'foo', 1 => 'bar'])->flip(); // Arrayy['foo' => 0, 'bar' => 1]
2699
     * </code>
2700
     *
2701
     * @return static
2702
     *                <p>(Immutable)</p>
2703
     *
2704
     * @psalm-return static<array-key,TKey>
2705
     * @psalm-mutation-free
2706
     */
2707 1
    public function flip(): self
2708
    {
2709
        $generator = function (): \Generator {
2710 1
            foreach ($this->getGenerator() as $key => $value) {
2711 1
                yield (string) $value => $key;
2712
            }
2713 1
        };
2714
2715 1
        return static::create(
2716 1
            $generator,
2717 1
            $this->iteratorClass,
2718 1
            false
2719
        );
2720
    }
2721
2722
    /**
2723
     * Get a value from an array (optional using dot-notation).
2724
     *
2725
     * EXAMPLE: <code>
2726
     * $arrayy = a(['user' => ['lastname' => 'Moelleken']]);
2727
     * $arrayy->get('user.lastname'); // 'Moelleken'
2728
     * // ---
2729
     * $arrayy = new A();
2730
     * $arrayy['user'] = ['lastname' => 'Moelleken'];
2731
     * $arrayy['user.firstname'] = 'Lars';
2732
     * $arrayy['user']['lastname']; // Moelleken
2733
     * $arrayy['user.lastname']; // Moelleken
2734
     * $arrayy['user.firstname']; // Lars
2735
     * </code>
2736
     *
2737
     * @param mixed $key            <p>The key to look for.</p>
2738
     * @param mixed $fallback       <p>Value to fallback to.</p>
2739
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2740
     *                              class.</p>
2741
     * @param bool  $useByReference
2742
     *
2743
     * @return mixed|static
2744
     *
2745
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2746
     * @psalm-mutation-free
2747
     */
2748 243
    public function get(
2749
        $key,
2750
        $fallback = null,
2751
        array $array = null,
2752
        bool $useByReference = false
2753
    ) {
2754 243
        if ($array !== null) {
2755 4
            if ($useByReference) {
2756
                $usedArray = &$array;
2757
            } else {
2758 4
                $usedArray = $array;
2759
            }
2760
        } else {
2761 240
            $this->generatorToArray();
2762
2763 240
            if ($useByReference) {
2764 128
                $usedArray = &$this->array;
2765
            } else {
2766 130
                $usedArray = $this->array;
2767
            }
2768
        }
2769
2770 243
        if ($key === null) {
2771 1
            return static::create(
2772 1
                [],
2773 1
                $this->iteratorClass,
2774 1
                false
2775 1
            )->createByReference($usedArray);
2776
        }
2777
2778
        // php cast "bool"-index into "int"-index
2779 243
        if ((bool) $key === $key) {
2780 3
            $key = (int) $key;
2781
        }
2782
2783 243
        if (\array_key_exists($key, $usedArray) === true) {
2784 205
            if (\is_array($usedArray[$key])) {
2785 19
                return static::create(
2786 19
                    [],
2787 19
                    $this->iteratorClass,
2788 19
                    false
2789 19
                )->createByReference($usedArray[$key]);
2790
            }
2791
2792 191
            return $usedArray[$key];
2793
        }
2794
2795
        // crawl through array, get key according to object or not
2796 61
        $usePath = false;
2797
        if (
2798 61
            $this->pathSeparator
2799
            &&
2800 61
            (string) $key === $key
2801
            &&
2802 61
            \strpos($key, $this->pathSeparator) !== false
2803
        ) {
2804 31
            $segments = \explode($this->pathSeparator, (string) $key);
2805 31
            if ($segments !== false) {
2806 31
                $usePath = true;
2807 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2808
2809 31
                foreach ($segments as $segment) {
2810
                    if (
2811
                        (
2812 31
                            \is_array($usedArrayTmp)
2813
                            ||
2814 31
                            $usedArrayTmp instanceof \ArrayAccess
2815
                        )
2816
                        &&
2817 31
                        isset($usedArrayTmp[$segment])
2818
                    ) {
2819 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2820
2821 30
                        continue;
2822
                    }
2823
2824
                    if (
2825 14
                        \is_object($usedArrayTmp) === true
2826
                        &&
2827 14
                        \property_exists($usedArrayTmp, $segment)
2828
                    ) {
2829 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2830
2831 1
                        continue;
2832
                    }
2833
2834 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2835 1
                        $segmentsTmp = $segments;
2836 1
                        unset($segmentsTmp[0]);
2837 1
                        $keyTmp = \implode('.', $segmentsTmp);
2838 1
                        $returnTmp = static::create(
2839 1
                            [],
2840 1
                            $this->iteratorClass,
2841 1
                            false
2842
                        );
2843 1
                        foreach ($this->getAll() as $dataTmp) {
2844 1
                            if ($dataTmp instanceof self) {
2845
                                $returnTmp->add($dataTmp->get($keyTmp));
2846
2847
                                continue;
2848
                            }
2849
2850
                            if (
2851
                                (
2852 1
                                    \is_array($dataTmp)
2853
                                    ||
2854 1
                                    $dataTmp instanceof \ArrayAccess
2855
                                )
2856
                                &&
2857 1
                                isset($dataTmp[$keyTmp])
2858
                            ) {
2859
                                $returnTmp->add($dataTmp[$keyTmp]);
2860
2861
                                continue;
2862
                            }
2863
2864
                            if (
2865 1
                                \is_object($dataTmp) === true
2866
                                &&
2867 1
                                \property_exists($dataTmp, $keyTmp)
2868
                            ) {
2869 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2870
2871
                                /** @noinspection UnnecessaryContinueInspection */
2872 1
                                continue;
2873
                            }
2874
                        }
2875
2876 1
                        if ($returnTmp->count() > 0) {
2877 1
                            return $returnTmp;
2878
                        }
2879
                    }
2880
2881 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2882
                }
2883
            }
2884
        }
2885
2886 58
        if (isset($usedArrayTmp)) {
2887 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...
2888
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2889
            }
2890
2891 28
            if (\is_array($usedArrayTmp)) {
2892 6
                return static::create(
2893 6
                    [],
2894 6
                    $this->iteratorClass,
2895 6
                    false
2896 6
                )->createByReference($usedArrayTmp);
2897
            }
2898
2899 28
            return $usedArrayTmp;
2900
        }
2901
2902 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...
2903 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2904
        }
2905
2906
        return static::create(
2907
            [],
2908
            $this->iteratorClass,
2909
            false
2910
        )->createByReference($usedArray);
2911
    }
2912
2913
    /**
2914
     * alias: for "Arrayy->toArray()"
2915
     *
2916
     * @return array
2917
     *
2918
     * @see          Arrayy::getArray()
2919
     *
2920
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2921
     */
2922 15
    public function getAll(): array
2923
    {
2924 15
        return $this->toArray();
2925
    }
2926
2927
    /**
2928
     * Get the current array from the "Arrayy"-object.
2929
     *
2930
     * alias for "toArray()"
2931
     *
2932
     * @param bool $convertAllArrayyElements <p>
2933
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2934
     *                                       </p>
2935
     * @param bool $preserveKeys             <p>
2936
     *                                       e.g.: A generator maybe return the same key more then once,
2937
     *                                       so maybe you will ignore the keys.
2938
     *                                       </p>
2939
     *
2940
     * @return array
2941
     *
2942
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2943
     * @psalm-mutation-free
2944
     *
2945
     * @see Arrayy::toArray()
2946
     */
2947 502
    public function getArray(
2948
        bool $convertAllArrayyElements = false,
2949
        bool $preserveKeys = true
2950
    ): array {
2951 502
        return $this->toArray(
2952 502
            $convertAllArrayyElements,
2953 502
            $preserveKeys
2954
        );
2955
    }
2956
2957
    /**
2958
     * @param string $json
2959
     *
2960
     * @return $this
2961
     */
2962 3
    public static function createFromJsonMapper(string $json)
2963
    {
2964
        // init
2965 3
        $class = static::create();
2966
2967 3
        $jsonObject = \json_decode($json, false);
2968
2969 3
        $mapper = new \Arrayy\Mapper\Json();
2970 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...
2971
            if ($class->checkPropertiesMismatchInConstructor) {
2972
                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...
2973
            }
2974
        };
2975
2976 3
        return $mapper->map($jsonObject, $class);
2977
    }
2978
2979
    /**
2980
     * @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...
2981
     *
2982
     * @internal
2983
     */
2984 6
    public function getPhpDocPropertiesFromClass()
2985
    {
2986 6
        if ($this->properties === []) {
2987 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2988
        }
2989
2990 6
        return $this->properties;
2991
    }
2992
2993
    /**
2994
     * Get the current array from the "Arrayy"-object as list.
2995
     *
2996
     * alias for "toList()"
2997
     *
2998
     * @param bool $convertAllArrayyElements <p>
2999
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3000
     *                                       </p>
3001
     *
3002
     * @return array
3003
     *
3004
     * @psalm-return array<int,mixed>|array<int,T>
3005
     * @psalm-mutation-free
3006
     *
3007
     * @see Arrayy::toList()
3008
     */
3009 1
    public function getList(bool $convertAllArrayyElements = false): array
3010
    {
3011 1
        return $this->toList($convertAllArrayyElements);
3012
    }
3013
3014
    /**
3015
     * Returns the values from a single column of the input array, identified by
3016
     * the $columnKey, can be used to extract data-columns from multi-arrays.
3017
     *
3018
     * EXAMPLE: <code>
3019
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
3020
     * </code>
3021
     *
3022
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
3023
     *       array by the values from the $indexKey column in the input array.
3024
     *
3025
     * @param int|string|null $columnKey
3026
     * @param int|string|null $indexKey
3027
     *
3028
     * @return static
3029
     *                <p>(Immutable)</p>
3030
     *
3031
     * @psalm-return static<TKey,T>
3032
     * @psalm-mutation-free
3033
     */
3034 1
    public function getColumn($columnKey = null, $indexKey = null): self
3035
    {
3036 1
        if ($columnKey === null && $indexKey === null) {
3037
            $generator = function () {
3038 1
                foreach ($this->getGenerator() as $key => $value) {
3039 1
                    yield $value;
3040
                }
3041 1
            };
3042
        } else {
3043
            $generator = function () use ($columnKey, $indexKey) {
3044 1
                foreach ($this->getGenerator() as $key => $value) {
3045
                    // reset
3046 1
                    $newKey = null;
3047 1
                    $newValue = null;
3048 1
                    $newValueFound = false;
3049
3050 1
                    if ($indexKey !== null) {
3051 1
                        foreach ($value as $keyInner => $valueInner) {
3052 1
                            if ($indexKey === $keyInner) {
3053 1
                                $newKey = $valueInner;
3054
                            }
3055
3056 1
                            if ($columnKey === $keyInner) {
3057 1
                                $newValue = $valueInner;
3058 1
                                $newValueFound = true;
3059
                            }
3060
                        }
3061
                    } else {
3062 1
                        foreach ($value as $keyInner => $valueInner) {
3063 1
                            if ($columnKey === $keyInner) {
3064 1
                                $newValue = $valueInner;
3065 1
                                $newValueFound = true;
3066
                            }
3067
                        }
3068
                    }
3069
3070 1
                    if ($newValueFound === false) {
3071 1
                        if ($newKey !== null) {
3072 1
                            yield $newKey => $value;
3073
                        } else {
3074 1
                            yield $value;
3075
                        }
3076
                    } else {
3077
                        /** @noinspection NestedPositiveIfStatementsInspection */
3078 1
                        if ($newKey !== null) {
3079 1
                            yield $newKey => $newValue;
3080
                        } else {
3081 1
                            yield $newValue;
3082
                        }
3083
                    }
3084
                }
3085 1
            };
3086
        }
3087
3088 1
        return static::create(
3089 1
            $generator,
3090 1
            $this->iteratorClass,
3091 1
            false
3092
        );
3093
    }
3094
3095
    /**
3096
     * Get the current array from the "Arrayy"-object as generator by reference.
3097
     *
3098
     * @return \Generator
3099
     *
3100
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3101
     */
3102 75
    public function &getGeneratorByReference(): \Generator
3103
    {
3104 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3105
            // -> false-positive -> see "&" from method
3106
            /** @noinspection YieldFromCanBeUsedInspection */
3107 17
            foreach ($this->generator as $key => $value) {
3108 17
                yield $key => $value;
3109
            }
3110
3111 5
            return;
3112
        }
3113
3114
        // -> false-positive -> see "&$value"
3115
        /** @noinspection YieldFromCanBeUsedInspection */
3116
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3117 59
        foreach ($this->array as $key => &$value) {
3118 54
            yield $key => $value;
3119
        }
3120 35
    }
3121
3122
    /**
3123
     * Get the current array from the "Arrayy"-object as generator.
3124
     *
3125
     * @return \Generator
3126
     *
3127
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3128
     * @psalm-mutation-free
3129
     */
3130 1001
    public function getGenerator(): \Generator
3131
    {
3132 1001
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3133 48
            yield from $this->generator;
3134
3135 48
            return;
3136
        }
3137
3138 999
        yield from $this->array;
3139 969
    }
3140
3141
    /**
3142
     * alias: for "Arrayy->keys()"
3143
     *
3144
     * @return static
3145
     *                <p>(Immutable)</p>
3146
     *
3147
     * @see          Arrayy::keys()
3148
     *
3149
     * @psalm-return static<array-key,TKey>
3150
     * @psalm-mutation-free
3151
     */
3152 2
    public function getKeys()
3153
    {
3154 2
        return $this->keys();
3155
    }
3156
3157
    /**
3158
     * Get the current array from the "Arrayy"-object as object.
3159
     *
3160
     * @return \stdClass
3161
     */
3162 4
    public function getObject(): \stdClass
3163
    {
3164 4
        return self::arrayToObject($this->toArray());
3165
    }
3166
3167
    /**
3168
     * alias: for "Arrayy->randomImmutable()"
3169
     *
3170
     * @return static
3171
     *                <p>(Immutable)</p>
3172
     *
3173
     * @see          Arrayy::randomImmutable()
3174
     *
3175
     * @psalm-return static<int|array-key,T>
3176
     */
3177 4
    public function getRandom(): self
3178
    {
3179 4
        return $this->randomImmutable();
3180
    }
3181
3182
    /**
3183
     * alias: for "Arrayy->randomKey()"
3184
     *
3185
     * @return mixed
3186
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3187
     *
3188
     * @see Arrayy::randomKey()
3189
     */
3190 3
    public function getRandomKey()
3191
    {
3192 3
        return $this->randomKey();
3193
    }
3194
3195
    /**
3196
     * alias: for "Arrayy->randomKeys()"
3197
     *
3198
     * @param int $number
3199
     *
3200
     * @return static
3201
     *                <p>(Immutable)</p>
3202
     *
3203
     * @see          Arrayy::randomKeys()
3204
     *
3205
     * @psalm-return static<TKey,T>
3206
     */
3207 8
    public function getRandomKeys(int $number): self
3208
    {
3209 8
        return $this->randomKeys($number);
3210
    }
3211
3212
    /**
3213
     * alias: for "Arrayy->randomValue()"
3214
     *
3215
     * @return mixed
3216
     *               <p>Get a random value or null if there wasn't a value.</p>
3217
     *
3218
     * @see Arrayy::randomValue()
3219
     */
3220 3
    public function getRandomValue()
3221
    {
3222 3
        return $this->randomValue();
3223
    }
3224
3225
    /**
3226
     * alias: for "Arrayy->randomValues()"
3227
     *
3228
     * @param int $number
3229
     *
3230
     * @return static
3231
     *                <p>(Immutable)</p>
3232
     *
3233
     * @see          Arrayy::randomValues()
3234
     *
3235
     * @psalm-return static<TKey,T>
3236
     */
3237 6
    public function getRandomValues(int $number): self
3238
    {
3239 6
        return $this->randomValues($number);
3240
    }
3241
3242
    /**
3243
     * Gets all values.
3244
     *
3245
     * @return static
3246
     *                <p>The values of all elements in this array, in the order they
3247
     *                appear in the array.</p>
3248
     *
3249
     * @psalm-return static<TKey,T>
3250
     */
3251 4
    public function getValues()
3252
    {
3253 4
        $this->generatorToArray(false);
3254
3255 4
        return static::create(
3256 4
            \array_values($this->array),
3257 4
            $this->iteratorClass,
3258 4
            false
3259
        );
3260
    }
3261
3262
    /**
3263
     * Gets all values via Generator.
3264
     *
3265
     * @return \Generator
3266
     *                    <p>The values of all elements in this array, in the order they
3267
     *                    appear in the array as Generator.</p>
3268
     *
3269
     * @psalm-return \Generator<TKey,T>
3270
     */
3271 4
    public function getValuesYield(): \Generator
3272
    {
3273 4
        yield from $this->getGenerator();
3274 4
    }
3275
3276
    /**
3277
     * Group values from a array according to the results of a closure.
3278
     *
3279
     * @param callable|string $grouper  <p>A callable function name.</p>
3280
     * @param bool            $saveKeys
3281
     *
3282
     * @return static
3283
     *                <p>(Immutable)</p>
3284
     *
3285
     * @psalm-return static<TKey,T>
3286
     * @psalm-mutation-free
3287
     */
3288 4
    public function group($grouper, bool $saveKeys = false): self
3289
    {
3290
        // init
3291 4
        $result = [];
3292
3293
        // Iterate over values, group by property/results from closure.
3294 4
        foreach ($this->getGenerator() as $key => $value) {
3295 4
            if (\is_callable($grouper) === true) {
3296 3
                $groupKey = $grouper($value, $key);
3297
            } else {
3298 1
                $groupKey = $this->get($grouper);
3299
            }
3300
3301 4
            $newValue = $this->get($groupKey, null, $result);
3302
3303 4
            if ($groupKey instanceof self) {
3304
                $groupKey = $groupKey->toArray();
3305
            }
3306
3307 4
            if ($newValue instanceof self) {
3308 4
                $newValue = $newValue->toArray();
3309
            }
3310
3311
            // Add to results.
3312 4
            if ($groupKey !== null) {
3313 3
                if ($saveKeys) {
3314 2
                    $result[$groupKey] = $newValue;
3315 2
                    $result[$groupKey][$key] = $value;
3316
                } else {
3317 1
                    $result[$groupKey] = $newValue;
3318 1
                    $result[$groupKey][] = $value;
3319
                }
3320
            }
3321
        }
3322
3323 4
        return static::create(
3324 4
            $result,
3325 4
            $this->iteratorClass,
3326 4
            false
3327
        );
3328
    }
3329
3330
    /**
3331
     * Check if an array has a given key.
3332
     *
3333
     * @param mixed $key
3334
     *
3335
     * @return bool
3336
     */
3337 30
    public function has($key): bool
3338
    {
3339 30
        static $UN_FOUND = null;
3340
3341 30
        if ($UN_FOUND === null) {
3342
            // Generate unique string to use as marker.
3343 1
            $UN_FOUND = \uniqid('arrayy', true);
3344
        }
3345
3346 30
        if (\is_array($key)) {
3347 1
            if ($key === []) {
3348
                return false;
3349
            }
3350
3351 1
            foreach ($key as $keyTmp) {
3352 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3353 1
                if ($found === false) {
3354 1
                    return false;
3355
                }
3356
            }
3357
3358 1
            return true;
3359
        }
3360
3361 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3362
    }
3363
3364
    /**
3365
     * Check if an array has a given value.
3366
     *
3367
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3368
     *
3369
     * @param mixed $value
3370
     *
3371
     * @return bool
3372
     */
3373 1
    public function hasValue($value): bool
3374
    {
3375 1
        return $this->contains($value);
3376
    }
3377
3378
    /**
3379
     * Implodes the values of this array.
3380
     *
3381
     * EXAMPLE: <code>
3382
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3383
     * </code>
3384
     *
3385
     * @param string $glue
3386
     * @param string $prefix
3387
     *
3388
     * @return string
3389
     * @psalm-mutation-free
3390
     */
3391 28
    public function implode(string $glue = '', string $prefix = ''): string
3392
    {
3393 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3394
    }
3395
3396
    /**
3397
     * Implodes the keys of this array.
3398
     *
3399
     * @param string $glue
3400
     *
3401
     * @return string
3402
     * @psalm-mutation-free
3403
     */
3404 8
    public function implodeKeys(string $glue = ''): string
3405
    {
3406 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3407
    }
3408
3409
    /**
3410
     * Given a list and an iterate-function that returns
3411
     * a key for each element in the list (or a property name),
3412
     * returns an object with an index of each item.
3413
     *
3414
     * @param mixed $key
3415
     *
3416
     * @return static
3417
     *                <p>(Immutable)</p>
3418
     *
3419
     * @psalm-return static<TKey,T>
3420
     * @psalm-mutation-free
3421
     */
3422 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...
3423
    {
3424
        // init
3425 4
        $results = [];
3426
3427 4
        foreach ($this->getGenerator() as $a) {
3428 4
            if (\array_key_exists($key, $a) === true) {
3429 3
                $results[$a[$key]] = $a;
3430
            }
3431
        }
3432
3433 4
        return static::create(
3434 4
            $results,
3435 4
            $this->iteratorClass,
3436 4
            false
3437
        );
3438
    }
3439
3440
    /**
3441
     * alias: for "Arrayy->searchIndex()"
3442
     *
3443
     * @param mixed $value <p>The value to search for.</p>
3444
     *
3445
     * @return false|mixed
3446
     *
3447
     * @see Arrayy::searchIndex()
3448
     */
3449 4
    public function indexOf($value)
3450
    {
3451 4
        return $this->searchIndex($value);
3452
    }
3453
3454
    /**
3455
     * Get everything but the last..$to items.
3456
     *
3457
     * EXAMPLE: <code>
3458
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3459
     * </code>
3460
     *
3461
     * @param int $to
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable)</p>
3465
     *
3466
     * @psalm-return static<TKey,T>
3467
     * @psalm-mutation-free
3468
     */
3469 12
    public function initial(int $to = 1): self
3470
    {
3471 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3472
    }
3473
3474
    /**
3475
     * Return an array with all elements found in input array.
3476
     *
3477
     * EXAMPLE: <code>
3478
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3479
     * </code>
3480
     *
3481
     * @param array $search
3482
     * @param bool  $keepKeys
3483
     *
3484
     * @return static
3485
     *                <p>(Immutable)</p>
3486
     *
3487
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3488
     * @psalm-return static<TKey,T>
3489
     * @psalm-mutation-free
3490
     */
3491 4
    public function intersection(array $search, bool $keepKeys = false): self
3492
    {
3493 4
        if ($keepKeys) {
3494
            /**
3495
             * @psalm-suppress MissingClosureReturnType
3496
             * @psalm-suppress MissingClosureParamType
3497
             */
3498 1
            return static::create(
3499 1
                \array_uintersect(
3500 1
                    $this->toArray(),
3501 1
                    $search,
3502
                    static function ($a, $b) {
3503 1
                        return $a === $b ? 0 : -1;
3504 1
                    }
3505
                ),
3506 1
                $this->iteratorClass,
3507 1
                false
3508
            );
3509
        }
3510
3511 3
        return static::create(
3512 3
            \array_values(\array_intersect($this->toArray(), $search)),
3513 3
            $this->iteratorClass,
3514 3
            false
3515
        );
3516
    }
3517
3518
    /**
3519
     * Return an array with all elements found in input array.
3520
     *
3521
     * @param array ...$array
3522
     *
3523
     * @return static
3524
     *                <p>(Immutable)</p>
3525
     *
3526
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3527
     * @psalm-return static<TKey,T>
3528
     * @psalm-mutation-free
3529
     */
3530 1
    public function intersectionMulti(...$array): self
3531
    {
3532 1
        return static::create(
3533 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3534 1
            $this->iteratorClass,
3535 1
            false
3536
        );
3537
    }
3538
3539
    /**
3540
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3541
     *
3542
     * EXAMPLE: <code>
3543
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3544
     * </code>
3545
     *
3546
     * @param array $search
3547
     *
3548
     * @return bool
3549
     *
3550
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3551
     */
3552 1
    public function intersects(array $search): bool
3553
    {
3554 1
        return $this->intersection($search)->count() > 0;
3555
    }
3556
3557
    /**
3558
     * Invoke a function on all of an array's values.
3559
     *
3560
     * @param callable $callable
3561
     * @param mixed    $arguments
3562
     *
3563
     * @return static
3564
     *                <p>(Immutable)</p>
3565
     *
3566
     * @psalm-param  callable(T=,mixed):mixed $callable
3567
     * @psalm-return static<TKey,T>
3568
     * @psalm-mutation-free
3569
     */
3570 1
    public function invoke($callable, $arguments = []): self
3571
    {
3572
        // If one argument given for each iteration, create an array for it.
3573 1
        if (!\is_array($arguments)) {
3574 1
            $arguments = \array_fill(
3575 1
                0,
3576 1
                $this->count(),
3577 1
                $arguments
3578
            );
3579
        }
3580
3581
        // If the callable has arguments, pass them.
3582 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...
3583 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3584
        } else {
3585 1
            $array = $this->map($callable);
3586
        }
3587
3588 1
        return static::create(
3589 1
            $array,
3590 1
            $this->iteratorClass,
3591 1
            false
3592
        );
3593
    }
3594
3595
    /**
3596
     * Check whether array is associative or not.
3597
     *
3598
     * EXAMPLE: <code>
3599
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3600
     * </code>
3601
     *
3602
     * @param bool $recursive
3603
     *
3604
     * @return bool
3605
     *              <p>Returns true if associative, false otherwise.</p>
3606
     */
3607 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...
3608
    {
3609 15
        if ($this->isEmpty()) {
3610 3
            return false;
3611
        }
3612
3613
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3614 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3615 13
            if ((string) $key !== $key) {
3616 11
                return false;
3617
            }
3618
        }
3619
3620 3
        return true;
3621
    }
3622
3623
    /**
3624
     * Check if a given key or keys are empty.
3625
     *
3626
     * @param int|int[]|string|string[]|null $keys
3627
     *
3628
     * @return bool
3629
     *              <p>Returns true if empty, false otherwise.</p>
3630
     * @psalm-mutation-free
3631
     */
3632 45
    public function isEmpty($keys = null): bool
3633
    {
3634 45
        if ($this->generator) {
3635
            return $this->toArray() === [];
3636
        }
3637
3638 45
        if ($keys === null) {
3639 43
            return $this->array === [];
3640
        }
3641
3642 2
        foreach ((array) $keys as $key) {
3643 2
            if (!empty($this->get($key))) {
3644 2
                return false;
3645
            }
3646
        }
3647
3648 2
        return true;
3649
    }
3650
3651
    /**
3652
     * Check if the current array is equal to the given "$array" or not.
3653
     *
3654
     * EXAMPLE: <code>
3655
     * a(['💩'])->isEqual(['💩']); // true
3656
     * </code>
3657
     *
3658
     * @param array $array
3659
     *
3660
     * @return bool
3661
     *
3662
     * @psalm-param array<mixed,mixed> $array
3663
     */
3664 1
    public function isEqual(array $array): bool
3665
    {
3666 1
        return $this->toArray() === $array;
3667
    }
3668
3669
    /**
3670
     * Check if the current array is a multi-array.
3671
     *
3672
     * EXAMPLE: <code>
3673
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3674
     * </code>
3675
     *
3676
     * @return bool
3677
     */
3678 22
    public function isMultiArray(): bool
3679
    {
3680 22
        foreach ($this->getGenerator() as $key => $value) {
3681 20
            if (\is_array($value)) {
3682 5
                return true;
3683
            }
3684
        }
3685
3686 18
        return false;
3687
    }
3688
3689
    /**
3690
     * Check whether array is numeric or not.
3691
     *
3692
     * @return bool
3693
     *              <p>Returns true if numeric, false otherwise.</p>
3694
     */
3695 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...
3696
    {
3697 5
        if ($this->isEmpty()) {
3698 2
            return false;
3699
        }
3700
3701
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3702 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3703 4
            if ((int) $key !== $key) {
3704 2
                return false;
3705
            }
3706
        }
3707
3708 2
        return true;
3709
    }
3710
3711
    /**
3712
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3713
     *
3714
     * EXAMPLE: <code>
3715
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3716
     * </code>
3717
     *
3718
     * INFO: If the array is empty we count it as non-sequential.
3719
     *
3720
     * @param bool $recursive
3721
     *
3722
     * @return bool
3723
     * @psalm-mutation-free
3724
     */
3725 10
    public function isSequential(bool $recursive = false): bool
3726
    {
3727 10
        $i = 0;
3728 10
        foreach ($this->getGenerator() as $key => $value) {
3729
            if (
3730 9
                $recursive
3731
                &&
3732 9
                (\is_array($value) || $value instanceof self)
3733
                &&
3734 9
                self::create($value)->isSequential() === false
3735
            ) {
3736 1
                return false;
3737
            }
3738
3739 9
            if ($key !== $i) {
3740 3
                return false;
3741
            }
3742
3743 8
            ++$i;
3744
        }
3745
3746
        /** @noinspection IfReturnReturnSimplificationInspection */
3747 9
        if ($i === 0) {
3748 3
            return false;
3749
        }
3750
3751 8
        return true;
3752
    }
3753
3754
    /**
3755
     * @return array
3756
     *
3757
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3758
     */
3759 2
    public function jsonSerialize(): array
3760
    {
3761 2
        return $this->toArray();
3762
    }
3763
3764
    /**
3765
     * Gets the key/index of the element at the current internal iterator position.
3766
     *
3767
     * @return int|string|null
3768
     */
3769
    public function key()
3770
    {
3771
        return \key($this->array);
3772
    }
3773
3774
    /**
3775
     * Checks if the given key exists in the provided array.
3776
     *
3777
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3778
     *       then you need to use "Arrayy->offsetExists()".
3779
     *
3780
     * @param int|string $key the key to look for
3781
     *
3782
     * @return bool
3783
     * @psalm-mutation-free
3784
     */
3785 164
    public function keyExists($key): bool
3786
    {
3787 164
        return \array_key_exists($key, $this->array);
3788
    }
3789
3790
    /**
3791
     * Get all keys from the current array.
3792
     *
3793
     * EXAMPLE: <code>
3794
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3795
     * </code>
3796
     *
3797
     * @param bool       $recursive     [optional] <p>
3798
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3799
     *                                  </p>
3800
     * @param mixed|null $search_values [optional] <p>
3801
     *                                  If specified, then only keys containing these values are returned.
3802
     *                                  </p>
3803
     * @param bool       $strict        [optional] <p>
3804
     *                                  Determines if strict comparison (===) should be used during the search.
3805
     *                                  </p>
3806
     *
3807
     * @return static
3808
     *                <p>(Immutable) An array of all the keys in input.</p>
3809
     *
3810
     * @psalm-return static<array-key,TKey>
3811
     * @psalm-mutation-free
3812
     */
3813 29
    public function keys(
3814
        bool $recursive = false,
3815
        $search_values = null,
3816
        bool $strict = true
3817
    ): self {
3818
3819
        // recursive
3820
3821 29
        if ($recursive === true) {
3822 4
            $array = $this->array_keys_recursive(
3823 4
                null,
3824 4
                $search_values,
3825 4
                $strict
3826
            );
3827
3828 4
            return static::create(
3829 4
                $array,
3830 4
                $this->iteratorClass,
3831 4
                false
3832
            );
3833
        }
3834
3835
        // non recursive
3836
3837 28
        if ($search_values === null) {
3838
            $arrayFunction = function (): \Generator {
3839 28
                foreach ($this->getGenerator() as $key => $value) {
3840 26
                    yield $key;
3841
                }
3842 28
            };
3843
        } else {
3844
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3845 1
                $is_array_tmp = \is_array($search_values);
3846
3847
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3848 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3849 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...
3850
                        (
3851 1
                            $is_array_tmp === false
3852
                            &&
3853 1
                            $strict === true
3854
                            &&
3855 1
                            $search_values === $value
3856
                        )
3857
                        ||
3858
                        (
3859 1
                            $is_array_tmp === false
3860
                            &&
3861 1
                            $strict === false
3862
                            &&
3863 1
                            $search_values == $value
3864
                        )
3865
                        ||
3866
                        (
3867 1
                            $is_array_tmp === true
3868
                            &&
3869 1
                            \in_array($value, $search_values, $strict)
3870
                        )
3871
                    ) {
3872 1
                        yield $key;
3873
                    }
3874
                }
3875 1
            };
3876
        }
3877
3878 28
        return static::create(
3879 28
            $arrayFunction,
3880 28
            $this->iteratorClass,
3881 28
            false
3882
        );
3883
    }
3884
3885
    /**
3886
     * Sort an array by key in reverse order.
3887
     *
3888
     * @param int $sort_flags [optional] <p>
3889
     *                        You may modify the behavior of the sort using the optional
3890
     *                        parameter sort_flags, for details
3891
     *                        see sort.
3892
     *                        </p>
3893
     *
3894
     * @return $this
3895
     *               <p>(Mutable) Return this Arrayy object.</p>
3896
     *
3897
     * @psalm-return static<TKey,T>
3898
     */
3899 4
    public function krsort(int $sort_flags = 0): self
3900
    {
3901 4
        $this->generatorToArray();
3902
3903 4
        \krsort($this->array, $sort_flags);
3904
3905 4
        return $this;
3906
    }
3907
3908
    /**
3909
     * Sort an array by key in reverse order.
3910
     *
3911
     * @param int $sort_flags [optional] <p>
3912
     *                        You may modify the behavior of the sort using the optional
3913
     *                        parameter sort_flags, for details
3914
     *                        see sort.
3915
     *                        </p>
3916
     *
3917
     * @return $this
3918
     *               <p>(Immutable) Return this Arrayy object.</p>
3919
     *
3920
     * @psalm-return static<TKey,T>
3921
     * @psalm-mutation-free
3922
     */
3923 4
    public function krsortImmutable(int $sort_flags = 0): self
3924
    {
3925 4
        $that = clone $this;
3926
3927
        /**
3928
         * @psalm-suppress ImpureMethodCall - object is already cloned
3929
         */
3930 4
        $that->krsort($sort_flags);
3931
3932 4
        return $that;
3933
    }
3934
3935
    /**
3936
     * Get the last value from the current array.
3937
     *
3938
     * EXAMPLE: <code>
3939
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
3940
     * </code>
3941
     *
3942
     * @return mixed|null
3943
     *                    <p>Return null if there wasn't a element.</p>
3944
     * @psalm-mutation-free
3945
     */
3946 17
    public function last()
3947
    {
3948 17
        $key_last = $this->lastKey();
3949 17
        if ($key_last === null) {
3950 2
            return null;
3951
        }
3952
3953 15
        return $this->get($key_last);
3954
    }
3955
3956
    /**
3957
     * Get the last key from the current array.
3958
     *
3959
     * @return mixed|null
3960
     *                    <p>Return null if there wasn't a element.</p>
3961
     * @psalm-mutation-free
3962
     */
3963 21
    public function lastKey()
3964
    {
3965 21
        $this->generatorToArray();
3966
3967 21
        return \array_key_last($this->array);
3968
    }
3969
3970
    /**
3971
     * Get the last value(s) from the current array.
3972
     *
3973
     * EXAMPLE: <code>
3974
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3975
     * </code>
3976
     *
3977
     * @param int|null $number
3978
     *
3979
     * @return static
3980
     *                <p>(Immutable)</p>
3981
     *
3982
     * @psalm-return static<TKey,T>
3983
     * @psalm-mutation-free
3984
     */
3985 13
    public function lastsImmutable(int $number = null): self
3986
    {
3987 13
        if ($this->isEmpty()) {
3988 1
            return static::create(
3989 1
                [],
3990 1
                $this->iteratorClass,
3991 1
                false
3992
            );
3993
        }
3994
3995 12
        if ($number === null) {
3996 8
            $poppedValue = $this->last();
3997
3998 8
            if ($poppedValue === null) {
3999 1
                $poppedValue = [$poppedValue];
4000
            } else {
4001 7
                $poppedValue = (array) $poppedValue;
4002
            }
4003
4004 8
            $arrayy = static::create(
4005 8
                $poppedValue,
4006 8
                $this->iteratorClass,
4007 8
                false
4008
            );
4009
        } else {
4010 4
            $arrayy = $this->rest(-$number);
4011
        }
4012
4013 12
        return $arrayy;
4014
    }
4015
4016
    /**
4017
     * Get the last value(s) from the current array.
4018
     *
4019
     * EXAMPLE: <code>
4020
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4021
     * </code>
4022
     *
4023
     * @param int|null $number
4024
     *
4025
     * @return $this
4026
     *               <p>(Mutable)</p>
4027
     *
4028
     * @psalm-return static<TKey,T>
4029
     */
4030 13
    public function lastsMutable(int $number = null): self
4031
    {
4032 13
        if ($this->isEmpty()) {
4033 1
            return $this;
4034
        }
4035
4036 12
        $this->array = $this->lastsImmutable($number)->toArray();
4037 12
        $this->generator = null;
4038
4039 12
        return $this;
4040
    }
4041
4042
    /**
4043
     * Count the values from the current array.
4044
     *
4045
     * alias: for "Arrayy->count()"
4046
     *
4047
     * @param int $mode
4048
     *
4049
     * @return int
4050
     *
4051
     * @see Arrayy::count()
4052
     */
4053 20
    public function length(int $mode = \COUNT_NORMAL): int
4054
    {
4055 20
        return $this->count($mode);
4056
    }
4057
4058
    /**
4059
     * Apply the given function to the every element of the array,
4060
     * collecting the results.
4061
     *
4062
     * EXAMPLE: <code>
4063
     * a(['foo', 'Foo'])->map('mb_strtoupper'); // Arrayy['FOO', 'FOO']
4064
     * </code>
4065
     *
4066
     * @param callable $callable
4067
     * @param bool     $useKeyAsSecondParameter
4068
     * @param mixed    ...$arguments
4069
     *
4070
     * @return static
4071
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4072
     *
4073
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
4074
     * @psalm-return static<TKey,T>
4075
     * @psalm-mutation-free
4076
     */
4077 6
    public function map(
4078
        callable $callable,
4079
        bool $useKeyAsSecondParameter = false,
4080
        ...$arguments
4081
    ) {
4082
        /**
4083
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4084
         */
4085 6
        $useArguments = \func_num_args() > 2;
4086
4087 6
        return static::create(
4088
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4089 6
                foreach ($this->getGenerator() as $key => $value) {
4090 5
                    if ($useArguments) {
4091 3
                        if ($useKeyAsSecondParameter) {
4092
                            yield $key => $callable($value, $key, ...$arguments);
4093
                        } else {
4094 3
                            yield $key => $callable($value, ...$arguments);
4095
                        }
4096
                    } else {
4097
                        /** @noinspection NestedPositiveIfStatementsInspection */
4098 5
                        if ($useKeyAsSecondParameter) {
4099
                            yield $key => $callable($value, $key);
4100
                        } else {
4101 5
                            yield $key => $callable($value);
4102
                        }
4103
                    }
4104
                }
4105 6
            },
4106 6
            $this->iteratorClass,
4107 6
            false
4108
        );
4109
    }
4110
4111
    /**
4112
     * Check if all items in current array match a truth test.
4113
     *
4114
     * EXAMPLE: <code>
4115
     * $closure = function ($value, $key) {
4116
     *     return ($value % 2 === 0);
4117
     * };
4118
     * a([2, 4, 8])->matches($closure); // true
4119
     * </code>
4120
     *
4121
     * @param \Closure $closure
4122
     *
4123
     * @return bool
4124
     */
4125 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...
4126
    {
4127 15
        if ($this->count() === 0) {
4128 2
            return false;
4129
        }
4130
4131 13
        foreach ($this->getGenerator() as $key => $value) {
4132 13
            $value = $closure($value, $key);
4133
4134 13
            if ($value === false) {
4135 7
                return false;
4136
            }
4137
        }
4138
4139 7
        return true;
4140
    }
4141
4142
    /**
4143
     * Check if any item in the current array matches a truth test.
4144
     *
4145
     * EXAMPLE: <code>
4146
     * $closure = function ($value, $key) {
4147
     *     return ($value % 2 === 0);
4148
     * };
4149
     * a([1, 4, 7])->matches($closure); // true
4150
     * </code>
4151
     *
4152
     * @param \Closure $closure
4153
     *
4154
     * @return bool
4155
     */
4156 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...
4157
    {
4158 14
        if ($this->count() === 0) {
4159 2
            return false;
4160
        }
4161
4162 12
        foreach ($this->getGenerator() as $key => $value) {
4163 12
            $value = $closure($value, $key);
4164
4165 12
            if ($value === true) {
4166 9
                return true;
4167
            }
4168
        }
4169
4170 4
        return false;
4171
    }
4172
4173
    /**
4174
     * Get the max value from an array.
4175
     *
4176
     * EXAMPLE: <code>
4177
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4178
     * </code>
4179
     *
4180
     * @return false|mixed
4181
     *                     <p>Will return false if there are no values.</p>
4182
     */
4183 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...
4184
    {
4185 10
        if ($this->count() === 0) {
4186 1
            return false;
4187
        }
4188
4189 9
        $max = false;
4190
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4191 9
        foreach ($this->getGeneratorByReference() as &$value) {
4192
            if (
4193 9
                $max === false
4194
                ||
4195 9
                $value > $max
4196
            ) {
4197 9
                $max = $value;
4198
            }
4199
        }
4200
4201 9
        return $max;
4202
    }
4203
4204
    /**
4205
     * Merge the new $array into the current array.
4206
     *
4207
     * - keep key,value from the current array, also if the index is in the new $array
4208
     *
4209
     * EXAMPLE: <code>
4210
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4211
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4212
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4213
     * // ---
4214
     * $array1 = [0 => 'one', 1 => 'foo'];
4215
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4216
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
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<int|TKey,T>
4227
     * @psalm-mutation-free
4228
     */
4229 33 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...
4230
    {
4231 33
        if ($recursive === true) {
4232 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4233 9
            $result = \array_replace_recursive($this->toArray(), $array);
4234
        } else {
4235 24
            $result = \array_replace($this->toArray(), $array);
4236
        }
4237
4238 33
        return static::create(
4239 33
            $result,
4240 33
            $this->iteratorClass,
4241 33
            false
4242
        );
4243
    }
4244
4245
    /**
4246
     * Merge the new $array into the current array.
4247
     *
4248
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4249
     * - create new indexes
4250
     *
4251
     * EXAMPLE: <code>
4252
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4253
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4254
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4255
     * // ---
4256
     * $array1 = [0 => 'one', 1 => 'foo'];
4257
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4258
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4259
     * </code>
4260
     *
4261
     * @param array $array
4262
     * @param bool  $recursive
4263
     *
4264
     * @return static
4265
     *                <p>(Immutable)</p>
4266
     *
4267
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4268
     * @psalm-return static<TKey,T>
4269
     * @psalm-mutation-free
4270
     */
4271 20 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...
4272
    {
4273 20
        if ($recursive === true) {
4274 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4275 5
            $result = \array_merge_recursive($this->toArray(), $array);
4276
        } else {
4277 15
            $result = \array_merge($this->toArray(), $array);
4278
        }
4279
4280 20
        return static::create(
4281 20
            $result,
4282 20
            $this->iteratorClass,
4283 20
            false
4284
        );
4285
    }
4286
4287
    /**
4288
     * Merge the the current array into the $array.
4289
     *
4290
     * - use key,value from the new $array, also if the index is in the current array
4291
     *
4292
     * EXAMPLE: <code>
4293
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4294
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4295
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4296
     * // ---
4297
     * $array1 = [0 => 'one', 1 => 'foo'];
4298
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4299
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4300
     * </code>
4301
     *
4302
     * @param array $array
4303
     * @param bool  $recursive
4304
     *
4305
     * @return static
4306
     *                <p>(Immutable)</p>
4307
     *
4308
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4309
     * @psalm-return static<TKey,T>
4310
     * @psalm-mutation-free
4311
     */
4312 17 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...
4313
    {
4314 17
        if ($recursive === true) {
4315 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4316 4
            $result = \array_replace_recursive($array, $this->toArray());
4317
        } else {
4318 13
            $result = \array_replace($array, $this->toArray());
4319
        }
4320
4321 17
        return static::create(
4322 17
            $result,
4323 17
            $this->iteratorClass,
4324 17
            false
4325
        );
4326
    }
4327
4328
    /**
4329
     * Merge the current array into the new $array.
4330
     *
4331
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4332
     * - create new indexes
4333
     *
4334
     * EXAMPLE: <code>
4335
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4336
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4337
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4338
     * // ---
4339
     * $array1 = [0 => 'one', 1 => 'foo'];
4340
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4341
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4342
     * </code>
4343
     *
4344
     * @param array $array
4345
     * @param bool  $recursive
4346
     *
4347
     * @return static
4348
     *                <p>(Immutable)</p>
4349
     *
4350
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4351
     * @psalm-return static<TKey,T>
4352
     * @psalm-mutation-free
4353
     */
4354 21 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...
4355
    {
4356 21
        if ($recursive === true) {
4357 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4358 7
            $result = \array_merge_recursive($array, $this->toArray());
4359
        } else {
4360 14
            $result = \array_merge($array, $this->toArray());
4361
        }
4362
4363 21
        return static::create(
4364 21
            $result,
4365 21
            $this->iteratorClass,
4366 21
            false
4367
        );
4368
    }
4369
4370
    /**
4371
     * @return ArrayyMeta|static
4372
     */
4373 18
    public static function meta()
4374
    {
4375 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4376
    }
4377
4378
    /**
4379
     * Get the min value from an array.
4380
     *
4381
     * EXAMPLE: <code>
4382
     * a([-9, -8, -7, 1.32])->min(); // -9
4383
     * </code>
4384
     *
4385
     * @return false|mixed
4386
     *                     <p>Will return false if there are no values.</p>
4387
     */
4388 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...
4389
    {
4390 10
        if ($this->count() === 0) {
4391 1
            return false;
4392
        }
4393
4394 9
        $min = false;
4395
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4396 9
        foreach ($this->getGeneratorByReference() as &$value) {
4397
            if (
4398 9
                $min === false
4399
                ||
4400 9
                $value < $min
4401
            ) {
4402 9
                $min = $value;
4403
            }
4404
        }
4405
4406 9
        return $min;
4407
    }
4408
4409
    /**
4410
     * Get the most used value from the array.
4411
     *
4412
     * @return mixed|null
4413
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4414
     * @psalm-mutation-free
4415
     */
4416 3
    public function mostUsedValue()
4417
    {
4418 3
        return $this->countValues()->arsortImmutable()->firstKey();
4419
    }
4420
4421
    /**
4422
     * Get the most used value from the array.
4423
     *
4424
     * @param int|null $number <p>How many values you will take?</p>
4425
     *
4426
     * @return static
4427
     *                <p>(Immutable)</p>
4428
     *
4429
     * @psalm-return static<TKey,T>
4430
     * @psalm-mutation-free
4431
     */
4432 3
    public function mostUsedValues(int $number = null): self
4433
    {
4434 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4435
    }
4436
4437
    /**
4438
     * Move an array element to a new index.
4439
     *
4440
     * EXAMPLE: <code>
4441
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4442
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4443
     * </code>
4444
     *
4445
     * @param int|string $from
4446
     * @param int        $to
4447
     *
4448
     * @return static
4449
     *                <p>(Immutable)</p>
4450
     *
4451
     * @psalm-return static<TKey,T>
4452
     * @psalm-mutation-free
4453
     */
4454 1
    public function moveElement($from, $to): self
4455
    {
4456 1
        $array = $this->toArray();
4457
4458 1
        if ((int) $from === $from) {
4459 1
            $tmp = \array_splice($array, $from, 1);
4460 1
            \array_splice($array, (int) $to, 0, $tmp);
4461 1
            $output = $array;
4462 1
        } elseif ((string) $from === $from) {
4463 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4464 1
            $itemToMove = $array[$from];
4465 1
            if ($indexToMove !== false) {
4466 1
                \array_splice($array, $indexToMove, 1);
4467
            }
4468 1
            $i = 0;
4469 1
            $output = [];
4470 1
            foreach ($array as $key => $item) {
4471 1
                if ($i === $to) {
4472 1
                    $output[$from] = $itemToMove;
4473
                }
4474 1
                $output[$key] = $item;
4475 1
                ++$i;
4476
            }
4477
        } else {
4478
            $output = [];
4479
        }
4480
4481 1
        return static::create(
4482 1
            $output,
4483 1
            $this->iteratorClass,
4484 1
            false
4485
        );
4486
    }
4487
4488
    /**
4489
     * Move an array element to the first place.
4490
     *
4491
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4492
     *       loss the keys of an indexed array.
4493
     *
4494
     * @param int|string $key
4495
     *
4496
     * @return static
4497
     *                <p>(Immutable)</p>
4498
     *
4499
     * @psalm-return static<TKey,T>
4500
     * @psalm-mutation-free
4501
     */
4502 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...
4503
    {
4504 1
        $array = $this->toArray();
4505
4506 1
        if ($this->offsetExists($key)) {
4507 1
            $tmpValue = $this->get($key);
4508 1
            unset($array[$key]);
4509 1
            $array = [$key => $tmpValue] + $array;
4510
        }
4511
4512 1
        return static::create(
4513 1
            $array,
4514 1
            $this->iteratorClass,
4515 1
            false
4516
        );
4517
    }
4518
4519
    /**
4520
     * Move an array element to the last place.
4521
     *
4522
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4523
     *       loss the keys of an indexed array.
4524
     *
4525
     * @param int|string $key
4526
     *
4527
     * @return static
4528
     *                <p>(Immutable)</p>
4529
     *
4530
     * @psalm-return static<TKey,T>
4531
     * @psalm-mutation-free
4532
     */
4533 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...
4534
    {
4535 1
        $array = $this->toArray();
4536
4537 1
        if ($this->offsetExists($key)) {
4538 1
            $tmpValue = $this->get($key);
4539 1
            unset($array[$key]);
4540 1
            $array += [$key => $tmpValue];
4541
        }
4542
4543 1
        return static::create(
4544 1
            $array,
4545 1
            $this->iteratorClass,
4546 1
            false
4547
        );
4548
    }
4549
4550
    /**
4551
     * Moves the internal iterator position to the next element and returns this element.
4552
     *
4553
     * @return false|mixed
4554
     *                     <p>(Mutable) Will return false if there are no values.</p>
4555
     */
4556
    public function next()
4557
    {
4558
        return \next($this->array);
4559
    }
4560
4561
    /**
4562
     * Get the next nth keys and values from the array.
4563
     *
4564
     * @param int $step
4565
     * @param int $offset
4566
     *
4567
     * @return static
4568
     *                <p>(Immutable)</p>
4569
     *
4570
     * @psalm-return static<TKey,T>
4571
     * @psalm-mutation-free
4572
     */
4573 1
    public function nth(int $step, int $offset = 0): self
4574
    {
4575
        $arrayFunction = function () use ($step, $offset): \Generator {
4576 1
            $position = 0;
4577 1
            foreach ($this->getGenerator() as $key => $value) {
4578 1
                if ($position++ % $step !== $offset) {
4579 1
                    continue;
4580
                }
4581
4582 1
                yield $key => $value;
4583
            }
4584 1
        };
4585
4586 1
        return static::create(
4587 1
            $arrayFunction,
4588 1
            $this->iteratorClass,
4589 1
            false
4590
        );
4591
    }
4592
4593
    /**
4594
     * Get a subset of the items from the given array.
4595
     *
4596
     * @param int[]|string[] $keys
4597
     *
4598
     * @return static
4599
     *                <p>(Immutable)</p>
4600
     *
4601
     * @psalm-param array-key[] $keys
4602
     * @psalm-return static<TKey,T>
4603
     * @psalm-mutation-free
4604
     */
4605 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...
4606
    {
4607 1
        $keys = \array_flip($keys);
4608
4609
        $generator = function () use ($keys): \Generator {
4610 1
            foreach ($this->getGenerator() as $key => $value) {
4611 1
                if (isset($keys[$key])) {
4612 1
                    yield $key => $value;
4613
                }
4614
            }
4615 1
        };
4616
4617 1
        return static::create(
4618 1
            $generator,
4619 1
            $this->iteratorClass,
4620 1
            false
4621
        );
4622
    }
4623
4624
    /**
4625
     * Pad array to the specified size with a given value.
4626
     *
4627
     * @param int   $size  <p>Size of the result array.</p>
4628
     * @param mixed $value <p>Empty value by default.</p>
4629
     *
4630
     * @return static
4631
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4632
     *
4633
     * @psalm-return static<TKey,T>
4634
     * @psalm-mutation-free
4635
     */
4636 5
    public function pad(int $size, $value): self
4637
    {
4638 5
        return static::create(
4639 5
            \array_pad($this->toArray(), $size, $value),
4640 5
            $this->iteratorClass,
4641 5
            false
4642
        );
4643
    }
4644
4645
    /**
4646
     * Partitions this array in two array according to a predicate.
4647
     * Keys are preserved in the resulting array.
4648
     *
4649
     * @param \Closure $closure
4650
     *                          <p>The predicate on which to partition.</p>
4651
     *
4652
     * @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...
4653
     *                    <p>An array with two elements. The first element contains the array
4654
     *                    of elements where the predicate returned TRUE, the second element
4655
     *                    contains the array of elements where the predicate returned FALSE.</p>
4656
     *
4657
     * @psalm-return array<int, static<TKey,T>>
4658
     */
4659 1
    public function partition(\Closure $closure): array
4660
    {
4661
        // init
4662 1
        $matches = [];
4663 1
        $noMatches = [];
4664
4665 1
        foreach ($this->getGenerator() as $key => $value) {
4666 1
            if ($closure($value, $key)) {
4667 1
                $matches[$key] = $value;
4668
            } else {
4669 1
                $noMatches[$key] = $value;
4670
            }
4671
        }
4672
4673 1
        return [self::create($matches), self::create($noMatches)];
4674
    }
4675
4676
    /**
4677
     * Pop a specified value off the end of the current array.
4678
     *
4679
     * @return mixed|null
4680
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4681
     */
4682 5
    public function pop()
4683
    {
4684 5
        $this->generatorToArray();
4685
4686 5
        return \array_pop($this->array);
4687
    }
4688
4689
    /**
4690
     * Prepend a (key) + value to the current array.
4691
     *
4692
     * EXAMPLE: <code>
4693
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4694
     * </code>
4695
     *
4696
     * @param mixed $value
4697
     * @param mixed $key
4698
     *
4699
     * @return $this
4700
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4701
     *
4702
     * @psalm-return static<TKey,T>
4703
     */
4704 11
    public function prepend($value, $key = null)
4705
    {
4706 11
        $this->generatorToArray();
4707
4708 11
        if ($this->properties !== []) {
4709 3
            $this->checkType($key, $value);
4710
        }
4711
4712 9
        if ($key === null) {
4713 8
            \array_unshift($this->array, $value);
4714
        } else {
4715 2
            $this->array = [$key => $value] + $this->array;
4716
        }
4717
4718 9
        return $this;
4719
    }
4720
4721
    /**
4722
     * Prepend a (key) + value to the current array.
4723
     *
4724
     * EXAMPLE: <code>
4725
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4726
     * </code>
4727
     *
4728
     * @param mixed $value
4729
     * @param mixed $key
4730
     *
4731
     * @return $this
4732
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4733
     *
4734
     * @psalm-return static<TKey,T>
4735
     * @psalm-mutation-free
4736
     */
4737 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...
4738
    {
4739
        $generator = function () use ($key, $value): \Generator {
4740 1
            if ($this->properties !== []) {
4741
                $this->checkType($key, $value);
4742
            }
4743
4744 1
            if ($key !== null) {
4745
                yield $key => $value;
4746
            } else {
4747 1
                yield $value;
4748
            }
4749
4750
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4751 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4752 1
                yield $keyOld => $itemOld;
4753
            }
4754 1
        };
4755
4756 1
        return static::create(
4757 1
            $generator,
4758 1
            $this->iteratorClass,
4759 1
            false
4760
        );
4761
    }
4762
4763
    /**
4764
     * Add a suffix to each key.
4765
     *
4766
     * @param mixed $suffix
4767
     *
4768
     * @return static
4769
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4770
     *
4771
     * @psalm-return static<TKey,T>
4772
     * @psalm-mutation-free
4773
     */
4774 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...
4775
    {
4776
        // init
4777 10
        $result = [];
4778
4779 10
        foreach ($this->getGenerator() as $key => $item) {
4780 9
            if ($item instanceof self) {
4781
                $result[$key] = $item->prependToEachKey($suffix);
4782 9
            } elseif (\is_array($item)) {
4783
                $result[$key] = self::create(
4784
                    $item,
4785
                    $this->iteratorClass,
4786
                    false
4787
                )->prependToEachKey($suffix)
4788
                    ->toArray();
4789
            } else {
4790 9
                $result[$key . $suffix] = $item;
4791
            }
4792
        }
4793
4794 10
        return self::create(
4795 10
            $result,
4796 10
            $this->iteratorClass,
4797 10
            false
4798
        );
4799
    }
4800
4801
    /**
4802
     * Add a suffix to each value.
4803
     *
4804
     * @param mixed $suffix
4805
     *
4806
     * @return static
4807
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4808
     *
4809
     * @psalm-return static<TKey,T>
4810
     * @psalm-mutation-free
4811
     */
4812 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...
4813
    {
4814
        // init
4815 10
        $result = [];
4816
4817 10
        foreach ($this->getGenerator() as $key => $item) {
4818 9
            if ($item instanceof self) {
4819
                $result[$key] = $item->prependToEachValue($suffix);
4820 9
            } elseif (\is_array($item)) {
4821
                $result[$key] = self::create(
4822
                    $item,
4823
                    $this->iteratorClass,
4824
                    false
4825
                )->prependToEachValue($suffix)
4826
                    ->toArray();
4827 9
            } elseif (\is_object($item) === true) {
4828 1
                $result[$key] = $item;
4829
            } else {
4830 8
                $result[$key] = $item . $suffix;
4831
            }
4832
        }
4833
4834 10
        return self::create(
4835 10
            $result,
4836 10
            $this->iteratorClass,
4837 10
            false
4838
        );
4839
    }
4840
4841
    /**
4842
     * Return the value of a given key and
4843
     * delete the key.
4844
     *
4845
     * @param int|int[]|string|string[]|null $keyOrKeys
4846
     * @param mixed                          $fallback
4847
     *
4848
     * @return mixed
4849
     */
4850 5
    public function pull($keyOrKeys = null, $fallback = null)
4851
    {
4852 5
        if ($keyOrKeys === null) {
4853 1
            $array = $this->toArray();
4854 1
            $this->clear();
4855
4856 1
            return $array;
4857
        }
4858
4859 4
        if (\is_array($keyOrKeys)) {
4860 1
            $valueOrValues = [];
4861 1
            foreach ($keyOrKeys as $key) {
4862 1
                $valueOrValues[] = $this->get($key, $fallback);
4863 1
                $this->offsetUnset($key);
4864
            }
4865
        } else {
4866 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4867 4
            $this->offsetUnset($keyOrKeys);
4868
        }
4869
4870 4
        return $valueOrValues;
4871
    }
4872
4873
    /**
4874
     * Push one or more values onto the end of array at once.
4875
     *
4876
     * @param array ...$args
4877
     *
4878
     * @return $this
4879
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4880
     *
4881
     * @noinspection ReturnTypeCanBeDeclaredInspection
4882
     *
4883
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4884
     * @psalm-return static<TKey,T>
4885
     */
4886 7
    public function push(...$args)
4887
    {
4888 7
        $this->generatorToArray();
4889
4890
        if (
4891 7
            $this->checkPropertyTypes
4892
            &&
4893 7
            $this->properties !== []
4894
        ) {
4895 1
            foreach ($args as $key => $value) {
4896 1
                $this->checkType($key, $value);
4897
            }
4898
        }
4899
4900 7
        \array_push($this->array, ...$args);
4901
4902 7
        return $this;
4903
    }
4904
4905
    /**
4906
     * Get a random value from the current array.
4907
     *
4908
     * EXAMPLE: <code>
4909
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
4910
     * </code>
4911
     *
4912
     * @param int|null $number <p>How many values you will take?</p>
4913
     *
4914
     * @return static
4915
     *                <p>(Immutable)</p>
4916
     *
4917
     * @psalm-return static<int|array-key,T>
4918
     */
4919 19
    public function randomImmutable(int $number = null): self
4920
    {
4921 19
        $this->generatorToArray();
4922
4923 19
        if ($this->count() === 0) {
4924 1
            return static::create(
4925 1
                [],
4926 1
                $this->iteratorClass,
4927 1
                false
4928
            );
4929
        }
4930
4931 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...
4932
            /** @noinspection NonSecureArrayRandUsageInspection */
4933 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4934
4935 13
            return static::create(
4936 13
                $arrayRandValue,
4937 13
                $this->iteratorClass,
4938 13
                false
4939
            );
4940
        }
4941
4942 6
        $arrayTmp = $this->array;
4943
        /** @noinspection NonSecureShuffleUsageInspection */
4944 6
        \shuffle($arrayTmp);
4945
4946 6
        return static::create(
4947 6
            $arrayTmp,
4948 6
            $this->iteratorClass,
4949 6
            false
4950 6
        )->firstsImmutable($number);
4951
    }
4952
4953
    /**
4954
     * Pick a random key/index from the keys of this array.
4955
     *
4956
     * EXAMPLE: <code>
4957
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
4958
     * $arrayy->randomKey(); // e.g. 2
4959
     * </code>
4960
     *
4961
     * @throws \RangeException If array is empty
4962
     *
4963
     * @return mixed
4964
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4965
     */
4966 4
    public function randomKey()
4967
    {
4968 4
        $result = $this->randomKeys(1);
4969
4970 4
        if (!isset($result[0])) {
4971
            $result[0] = null;
4972
        }
4973
4974 4
        return $result[0];
4975
    }
4976
4977
    /**
4978
     * Pick a given number of random keys/indexes out of this array.
4979
     *
4980
     * EXAMPLE: <code>
4981
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
4982
     * </code>
4983
     *
4984
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4985
     *
4986
     * @throws \RangeException If array is empty
4987
     *
4988
     * @return static
4989
     *                <p>(Immutable)</p>
4990
     *
4991
     * @psalm-return static<TKey,T>
4992
     */
4993 13
    public function randomKeys(int $number): self
4994
    {
4995 13
        $this->generatorToArray();
4996
4997 13
        $count = $this->count();
4998
4999
        if (
5000 13
            $number === 0
5001
            ||
5002 13
            $number > $count
5003
        ) {
5004 2
            throw new \RangeException(
5005 2
                \sprintf(
5006 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5007 2
                    $number,
5008 2
                    $count
5009
                )
5010
            );
5011
        }
5012
5013 11
        $result = (array) \array_rand($this->array, $number);
5014
5015 11
        return static::create(
5016 11
            $result,
5017 11
            $this->iteratorClass,
5018 11
            false
5019
        );
5020
    }
5021
5022
    /**
5023
     * Get a random value from the current array.
5024
     *
5025
     * EXAMPLE: <code>
5026
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5027
     * </code>
5028
     *
5029
     * @param int|null $number <p>How many values you will take?</p>
5030
     *
5031
     * @return $this
5032
     *               <p>(Mutable) Return this Arrayy object.</p>
5033
     *
5034
     * @psalm-return static<TKey,T>
5035
     */
5036 17
    public function randomMutable(int $number = null): self
5037
    {
5038 17
        $this->generatorToArray();
5039
5040 17
        if ($this->count() === 0) {
5041
            return static::create(
5042
                [],
5043
                $this->iteratorClass,
5044
                false
5045
            );
5046
        }
5047
5048 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...
5049
            /** @noinspection NonSecureArrayRandUsageInspection */
5050 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5051 7
            $this->array = $arrayRandValue;
5052
5053 7
            return $this;
5054
        }
5055
5056
        /** @noinspection NonSecureShuffleUsageInspection */
5057 11
        \shuffle($this->array);
5058
5059 11
        return $this->firstsMutable($number);
5060
    }
5061
5062
    /**
5063
     * Pick a random value from the values of this array.
5064
     *
5065
     * EXAMPLE: <code>
5066
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5067
     * </code>
5068
     *
5069
     * @return mixed
5070
     *               <p>Get a random value or null if there wasn't a value.</p>
5071
     */
5072 4
    public function randomValue()
5073
    {
5074 4
        $result = $this->randomImmutable();
5075
5076 4
        if (!isset($result[0])) {
5077
            $result[0] = null;
5078
        }
5079
5080 4
        return $result[0];
5081
    }
5082
5083
    /**
5084
     * Pick a given number of random values out of this array.
5085
     *
5086
     * EXAMPLE: <code>
5087
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5088
     * </code>
5089
     *
5090
     * @param int $number
5091
     *
5092
     * @return static
5093
     *                <p>(Mutable)</p>
5094
     *
5095
     * @psalm-return static<TKey,T>
5096
     */
5097 7
    public function randomValues(int $number): self
5098
    {
5099 7
        return $this->randomMutable($number);
5100
    }
5101
5102
    /**
5103
     * Get a random value from an array, with the ability to skew the results.
5104
     *
5105
     * EXAMPLE: <code>
5106
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5107
     * </code>
5108
     *
5109
     * @param array    $array
5110
     * @param int|null $number <p>How many values you will take?</p>
5111
     *
5112
     * @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...
5113
     *                           <p>(Immutable)</p>
5114
     *
5115
     * @psalm-param  array<mixed,mixed> $array
5116
     * @psalm-return static<int|array-key,T>
5117
     */
5118 9
    public function randomWeighted(array $array, int $number = null): self
5119
    {
5120
        // init
5121 9
        $options = [];
5122
5123 9
        foreach ($array as $option => $weight) {
5124 9
            if ($this->searchIndex($option) !== false) {
5125 2
                for ($i = 0; $i < $weight; ++$i) {
5126 1
                    $options[] = $option;
5127
                }
5128
            }
5129
        }
5130
5131 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5132
    }
5133
5134
    /**
5135
     * Reduce the current array via callable e.g. anonymous-function and return the end result.
5136
     *
5137
     * EXAMPLE: <code>
5138
     * a([1, 2, 3, 4])->reduce(
5139
     *     function ($carry, $item) {
5140
     *         return $carry * $item;
5141
     *     },
5142
     *     1
5143
     * ); // Arrayy[24]
5144
     * </code>
5145
     *
5146
     * @param callable $callable
5147
     * @param mixed    $initial
5148
     *
5149
     * @return static
5150
     *                <p>(Immutable)</p>
5151
     *
5152
     * @psalm-return static<TKey,T>
5153
     * @psalm-mutation-free
5154
     */
5155 18 View Code Duplication
    public function reduce($callable, $initial = []): 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...
5156
    {
5157 18
        foreach ($this->getGenerator() as $key => $value) {
5158 17
            $initial = $callable($initial, $value, $key);
5159
        }
5160
5161 18
        return static::create(
5162 18
            $initial,
5163 18
            $this->iteratorClass,
5164 18
            false
5165
        );
5166
    }
5167
5168
    /**
5169
     * @param bool $unique
5170
     *
5171
     * @return static
5172
     *                <p>(Immutable)</p>
5173
     *
5174
     * @psalm-return static<TKey,T>
5175
     * @psalm-mutation-free
5176
     */
5177 14
    public function reduce_dimension(bool $unique = true): self
5178
    {
5179
        // init
5180 14
        $result = [];
5181
5182 14
        foreach ($this->getGenerator() as $val) {
5183 12
            if (\is_array($val)) {
5184 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5185
            } else {
5186 12
                $result[] = [$val];
5187
            }
5188
        }
5189
5190 14
        $result = $result === [] ? [] : \array_merge(...$result);
5191
5192 14
        $resultArrayy = new static($result);
5193
5194
        /**
5195
         * @psalm-suppress ImpureMethodCall - object is already re-created
5196
         * @psalm-suppress InvalidReturnStatement - why?
5197
         */
5198 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5199
    }
5200
5201
    /**
5202
     * Create a numerically re-indexed Arrayy object.
5203
     *
5204
     * EXAMPLE: <code>
5205
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5206
     * </code>
5207
     *
5208
     * @return $this
5209
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5210
     *
5211
     * @psalm-return static<TKey,T>
5212
     */
5213 9
    public function reindex(): self
5214
    {
5215 9
        $this->generatorToArray(false);
5216
5217 9
        $this->array = \array_values($this->array);
5218
5219 9
        return $this;
5220
    }
5221
5222
    /**
5223
     * Return all items that fail the truth test.
5224
     *
5225
     * EXAMPLE: <code>
5226
     * $closure = function ($value) {
5227
     *     return $value % 2 !== 0;
5228
     * }
5229
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5230
     * </code>
5231
     *
5232
     * @param \Closure $closure
5233
     *
5234
     * @return static
5235
     *                <p>(Immutable)</p>
5236
     *
5237
     * @psalm-return static<TKey,T>
5238
     * @psalm-mutation-free
5239
     */
5240 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...
5241
    {
5242
        // init
5243 1
        $filtered = [];
5244
5245 1
        foreach ($this->getGenerator() as $key => $value) {
5246 1
            if (!$closure($value, $key)) {
5247 1
                $filtered[$key] = $value;
5248
            }
5249
        }
5250
5251 1
        return static::create(
5252 1
            $filtered,
5253 1
            $this->iteratorClass,
5254 1
            false
5255
        );
5256
    }
5257
5258
    /**
5259
     * Remove a value from the current array (optional using dot-notation).
5260
     *
5261
     * EXAMPLE: <code>
5262
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5263
     * </code>
5264
     *
5265
     * @param mixed $key
5266
     *
5267
     * @return static
5268
     *                <p>(Mutable)</p>
5269
     *
5270
     * @psalm-param  TKey $key
5271
     * @psalm-return static<TKey,T>
5272
     */
5273 22
    public function remove($key)
5274
    {
5275
        // recursive call
5276 22
        if (\is_array($key)) {
5277 1
            foreach ($key as $k) {
5278 1
                $this->internalRemove($k);
5279
            }
5280
5281 1
            return static::create(
5282 1
                $this->toArray(),
5283 1
                $this->iteratorClass,
5284 1
                false
5285
            );
5286
        }
5287
5288 21
        $this->internalRemove($key);
5289
5290 21
        return static::create(
5291 21
            $this->toArray(),
5292 21
            $this->iteratorClass,
5293 21
            false
5294
        );
5295
    }
5296
5297
    /**
5298
     * alias: for "Arrayy->removeValue()"
5299
     *
5300
     * @param mixed $element
5301
     *
5302
     * @return static
5303
     *                <p>(Immutable)</p>
5304
     *
5305
     * @psalm-param  T $element
5306
     * @psalm-return static<TKey,T>
5307
     * @psalm-mutation-free
5308
     */
5309 8
    public function removeElement($element)
5310
    {
5311 8
        return $this->removeValue($element);
5312
    }
5313
5314
    /**
5315
     * Remove the first value from the current array.
5316
     *
5317
     * EXAMPLE: <code>
5318
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5319
     * </code>
5320
     *
5321
     * @return static
5322
     *                <p>(Immutable)</p>
5323
     *
5324
     * @psalm-return static<TKey,T>
5325
     * @psalm-mutation-free
5326
     */
5327 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...
5328
    {
5329 7
        $tmpArray = $this->toArray();
5330
5331 7
        \array_shift($tmpArray);
5332
5333 7
        return static::create(
5334 7
            $tmpArray,
5335 7
            $this->iteratorClass,
5336 7
            false
5337
        );
5338
    }
5339
5340
    /**
5341
     * Remove the last value from the current array.
5342
     *
5343
     * EXAMPLE: <code>
5344
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5345
     * </code>
5346
     *
5347
     * @return static
5348
     *                <p>(Immutable)</p>
5349
     *
5350
     * @psalm-return static<TKey,T>
5351
     * @psalm-mutation-free
5352
     */
5353 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...
5354
    {
5355 7
        $tmpArray = $this->toArray();
5356
5357 7
        \array_pop($tmpArray);
5358
5359 7
        return static::create(
5360 7
            $tmpArray,
5361 7
            $this->iteratorClass,
5362 7
            false
5363
        );
5364
    }
5365
5366
    /**
5367
     * Removes a particular value from an array (numeric or associative).
5368
     *
5369
     * EXAMPLE: <code>
5370
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5371
     * </code>
5372
     *
5373
     * @param mixed $value
5374
     *
5375
     * @return static
5376
     *                <p>(Immutable)</p>
5377
     *
5378
     * @psalm-param  T $value
5379
     * @psalm-return static<TKey,T>
5380
     * @psalm-mutation-free
5381
     */
5382 8
    public function removeValue($value): self
5383
    {
5384 8
        $this->generatorToArray();
5385
5386
        // init
5387 8
        $isSequentialArray = $this->isSequential();
5388
5389 8
        foreach ($this->array as $key => $item) {
5390 7
            if ($item === $value) {
5391 7
                unset($this->array[$key]);
5392
            }
5393
        }
5394
5395 8
        if ($isSequentialArray) {
5396 6
            $this->array = \array_values($this->array);
5397
        }
5398
5399 8
        return static::create(
5400 8
            $this->array,
5401 8
            $this->iteratorClass,
5402 8
            false
5403
        );
5404
    }
5405
5406
    /**
5407
     * Generate array of repeated arrays.
5408
     *
5409
     * @param int $times <p>How many times has to be repeated.</p>
5410
     *
5411
     * @return static
5412
     *                <p>(Immutable)</p>
5413
     *
5414
     * @psalm-return static<TKey,T>
5415
     * @psalm-mutation-free
5416
     */
5417 1
    public function repeat($times): self
5418
    {
5419 1
        if ($times === 0) {
5420 1
            return static::create([], $this->iteratorClass);
5421
        }
5422
5423 1
        return static::create(
5424 1
            \array_fill(0, (int) $times, $this->toArray()),
5425 1
            $this->iteratorClass,
5426 1
            false
5427
        );
5428
    }
5429
5430
    /**
5431
     * Replace a key with a new key/value pair.
5432
     *
5433
     * EXAMPLE: <code>
5434
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5435
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5436
     * </code>
5437
     *
5438
     * @param mixed $oldKey
5439
     * @param mixed $newKey
5440
     * @param mixed $newValue
5441
     *
5442
     * @return static
5443
     *                <p>(Immutable)</p>
5444
     *
5445
     * @psalm-return static<TKey,T>
5446
     * @psalm-mutation-free
5447
     */
5448 5
    public function replace($oldKey, $newKey, $newValue): self
5449
    {
5450 5
        $that = clone $this;
5451
5452
        /**
5453
         * @psalm-suppress ImpureMethodCall - object is already cloned
5454
         */
5455 5
        return $that->remove($oldKey)
5456 5
            ->set($newKey, $newValue);
5457
    }
5458
5459
    /**
5460
     * Create an array using the current array as values and the other array as keys.
5461
     *
5462
     * EXAMPLE: <code>
5463
     * $firstArray = [
5464
     *     1 => 'one',
5465
     *     2 => 'two',
5466
     *     3 => 'three',
5467
     * ];
5468
     * $secondArray = [
5469
     *     'one' => 1,
5470
     *     1     => 'one',
5471
     *     2     => 2,
5472
     * ];
5473
     * $arrayy = a($firstArray);
5474
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5475
     * </code>
5476
     *
5477
     * @param array $keys <p>An array of keys.</p>
5478
     *
5479
     * @return static
5480
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5481
     *
5482
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5483
     * @psalm-return static<TKey,T>
5484
     * @psalm-mutation-free
5485
     */
5486 2
    public function replaceAllKeys(array $keys): self
5487
    {
5488 2
        return static::create(
5489 2
            \array_combine($keys, $this->toArray()),
5490 2
            $this->iteratorClass,
5491 2
            false
5492
        );
5493
    }
5494
5495
    /**
5496
     * Create an array using the current array as keys and the other array as values.
5497
     *
5498
     * EXAMPLE: <code>
5499
     * $firstArray = [
5500
     *     1 => 'one',
5501
     *     2 => 'two',
5502
     *     3 => 'three',
5503
     * ];
5504
     * $secondArray = [
5505
     *     'one' => 1,
5506
     *     1     => 'one',
5507
     *     2     => 2,
5508
     * ];
5509
     * $arrayy = a($firstArray);
5510
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5511
     * </code>
5512
     *
5513
     * @param array $array <p>An array of values.</p>
5514
     *
5515
     * @return static
5516
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5517
     *
5518
     * @psalm-param  array<mixed,T> $array
5519
     * @psalm-return static<TKey,T>
5520
     * @psalm-mutation-free
5521
     */
5522 2
    public function replaceAllValues(array $array): self
5523
    {
5524 2
        return static::create(
5525 2
            \array_combine($this->array, $array),
5526 2
            $this->iteratorClass,
5527 2
            false
5528
        );
5529
    }
5530
5531
    /**
5532
     * Replace the keys in an array with another set.
5533
     *
5534
     * EXAMPLE: <code>
5535
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5536
     * </code>
5537
     *
5538
     * @param array $keys <p>An array of keys matching the array's size</p>
5539
     *
5540
     * @return static
5541
     *                <p>(Immutable)</p>
5542
     *
5543
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5544
     * @psalm-return static<TKey,T>
5545
     * @psalm-mutation-free
5546
     */
5547 1
    public function replaceKeys(array $keys): self
5548
    {
5549 1
        $values = \array_values($this->toArray());
5550 1
        $result = \array_combine($keys, $values);
5551
5552 1
        return static::create(
5553 1
            $result,
5554 1
            $this->iteratorClass,
5555 1
            false
5556
        );
5557
    }
5558
5559
    /**
5560
     * Replace the first matched value in an array.
5561
     *
5562
     * EXAMPLE: <code>
5563
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5564
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5565
     * </code>
5566
     *
5567
     * @param mixed $search      <p>The value to replace.</p>
5568
     * @param mixed $replacement <p>The value to replace.</p>
5569
     *
5570
     * @return static
5571
     *                <p>(Immutable)</p>
5572
     *
5573
     * @psalm-return static<TKey,T>
5574
     * @psalm-mutation-free
5575
     */
5576 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...
5577
    {
5578 3
        $array = $this->toArray();
5579 3
        $key = \array_search($search, $array, true);
5580
5581 3
        if ($key !== false) {
5582 3
            $array[$key] = $replacement;
5583
        }
5584
5585 3
        return static::create(
5586 3
            $array,
5587 3
            $this->iteratorClass,
5588 3
            false
5589
        );
5590
    }
5591
5592
    /**
5593
     * Replace values in the current array.
5594
     *
5595
     * EXAMPLE: <code>
5596
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5597
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5598
     * </code>
5599
     *
5600
     * @param mixed $search      <p>The value to replace.</p>
5601
     * @param mixed $replacement <p>What to replace it with.</p>
5602
     *
5603
     * @return static
5604
     *                <p>(Immutable)</p>
5605
     *
5606
     * @psalm-return static<TKey,T>
5607
     * @psalm-mutation-free
5608
     */
5609 1
    public function replaceValues($search, $replacement = ''): self
5610
    {
5611
        /**
5612
         * @psalm-suppress MissingClosureReturnType
5613
         * @psalm-suppress MissingClosureParamType
5614
         */
5615 1
        return $this->each(
5616
            static function ($value) use ($search, $replacement) {
5617 1
                return \str_replace($search, $replacement, $value);
5618 1
            }
5619
        );
5620
    }
5621
5622
    /**
5623
     * Get the last elements from index $from until the end of this array.
5624
     *
5625
     * EXAMPLE: <code>
5626
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5627
     * </code>
5628
     *
5629
     * @param int $from
5630
     *
5631
     * @return static
5632
     *                <p>(Immutable)</p>
5633
     *
5634
     * @psalm-return static<TKey,T>
5635
     * @psalm-mutation-free
5636
     */
5637 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...
5638
    {
5639 15
        $tmpArray = $this->toArray();
5640
5641 15
        return static::create(
5642 15
            \array_splice($tmpArray, $from),
5643 15
            $this->iteratorClass,
5644 15
            false
5645
        );
5646
    }
5647
5648
    /**
5649
     * Return the array in the reverse order.
5650
     *
5651
     * EXAMPLE: <code>
5652
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5653
     * </code>
5654
     *
5655
     * @return $this
5656
     *               <p>(Mutable) Return this Arrayy object.</p>
5657
     *
5658
     * @psalm-return static<TKey,T>
5659
     */
5660 9
    public function reverse(): self
5661
    {
5662 9
        $this->generatorToArray();
5663
5664 9
        $this->array = \array_reverse($this->array);
5665
5666 9
        return $this;
5667
    }
5668
5669
    /**
5670
     * Sort an array in reverse order.
5671
     *
5672
     * @param int $sort_flags [optional] <p>
5673
     *                        You may modify the behavior of the sort using the optional
5674
     *                        parameter sort_flags, for details
5675
     *                        see sort.
5676
     *                        </p>
5677
     *
5678
     * @return $this
5679
     *               <p>(Mutable) Return this Arrayy object.</p>
5680
     *
5681
     * @psalm-return static<TKey,T>
5682
     */
5683 4
    public function rsort(int $sort_flags = 0): self
5684
    {
5685 4
        $this->generatorToArray();
5686
5687 4
        \rsort($this->array, $sort_flags);
5688
5689 4
        return $this;
5690
    }
5691
5692
    /**
5693
     * Sort an array in reverse order.
5694
     *
5695
     * @param int $sort_flags [optional] <p>
5696
     *                        You may modify the behavior of the sort using the optional
5697
     *                        parameter sort_flags, for details
5698
     *                        see sort.
5699
     *                        </p>
5700
     *
5701
     * @return $this
5702
     *               <p>(Immutable) Return this Arrayy object.</p>
5703
     *
5704
     * @psalm-return static<TKey,T>
5705
     * @psalm-mutation-free
5706
     */
5707 4
    public function rsortImmutable(int $sort_flags = 0): self
5708
    {
5709 4
        $that = clone $this;
5710
5711
        /**
5712
         * @psalm-suppress ImpureMethodCall - object is already cloned
5713
         */
5714 4
        $that->rsort($sort_flags);
5715
5716 4
        return $that;
5717
    }
5718
5719
    /**
5720
     * Search for the first index of the current array via $value.
5721
     *
5722
     * EXAMPLE: <code>
5723
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5724
     * </code>
5725
     *
5726
     * @param mixed $value
5727
     *
5728
     * @return false|float|int|string
5729
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5730
     * @psalm-mutation-free
5731
     */
5732 21
    public function searchIndex($value)
5733
    {
5734 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5735 20
            if ($value === $valueFromArray) {
5736 10
                return $keyFromArray;
5737
            }
5738
        }
5739
5740 11
        return false;
5741
    }
5742
5743
    /**
5744
     * Search for the value of the current array via $index.
5745
     *
5746
     * EXAMPLE: <code>
5747
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5748
     * </code>
5749
     *
5750
     * @param mixed $index
5751
     *
5752
     * @return static
5753
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5754
     *
5755
     * @psalm-return static<TKey,T>
5756
     * @psalm-mutation-free
5757
     */
5758 9
    public function searchValue($index): self
5759
    {
5760 9
        $this->generatorToArray();
5761
5762
        // init
5763 9
        $return = [];
5764
5765 9
        if ($this->array === []) {
5766
            return static::create(
5767
                [],
5768
                $this->iteratorClass,
5769
                false
5770
            );
5771
        }
5772
5773
        // php cast "bool"-index into "int"-index
5774 9
        if ((bool) $index === $index) {
5775 1
            $index = (int) $index;
5776
        }
5777
5778 9
        if ($this->offsetExists($index)) {
5779 7
            $return = [$this->array[$index]];
5780
        }
5781
5782 9
        return static::create(
5783 9
            $return,
5784 9
            $this->iteratorClass,
5785 9
            false
5786
        );
5787
    }
5788
5789
    /**
5790
     * Set a value for the current array (optional using dot-notation).
5791
     *
5792
     * EXAMPLE: <code>
5793
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5794
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5795
     * </code>
5796
     *
5797
     * @param string $key   <p>The key to set.</p>
5798
     * @param mixed  $value <p>Its value.</p>
5799
     *
5800
     * @return $this
5801
     *               <p>(Mutable) Return this Arrayy object.</p>
5802
     *
5803
     * @psalm-param  TKey $key
5804
     * @psalm-param  T $value
5805
     * @psalm-return static<TKey,T>
5806
     */
5807 28
    public function set($key, $value): self
5808
    {
5809 28
        $this->internalSet($key, $value);
5810
5811 27
        return $this;
5812
    }
5813
5814
    /**
5815
     * Get a value from a array and set it if it was not.
5816
     *
5817
     * WARNING: this method only set the value, if the $key is not already set
5818
     *
5819
     * EXAMPLE: <code>
5820
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5821
     * $arrayy->setAndGet(1, 4); // 1
5822
     * $arrayy->setAndGet(0, 4); // 4
5823
     * </code>
5824
     *
5825
     * @param mixed $key      <p>The key</p>
5826
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5827
     *
5828
     * @return mixed
5829
     *               <p>(Mutable)</p>
5830
     */
5831 11
    public function setAndGet($key, $fallback = null)
5832
    {
5833 11
        $this->generatorToArray();
5834
5835
        // If the key doesn't exist, set it.
5836 11
        if (!$this->has($key)) {
5837 4
            $this->array = $this->set($key, $fallback)->toArray();
5838
        }
5839
5840 11
        return $this->get($key);
5841
    }
5842
5843
    /**
5844
     * Shifts a specified value off the beginning of array.
5845
     *
5846
     * @return mixed
5847
     *               <p>(Mutable) A shifted element from the current array.</p>
5848
     */
5849 5
    public function shift()
5850
    {
5851 5
        $this->generatorToArray();
5852
5853 5
        return \array_shift($this->array);
5854
    }
5855
5856
    /**
5857
     * Shuffle the current array.
5858
     *
5859
     * EXAMPLE: <code>
5860
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5861
     * </code>
5862
     *
5863
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5864
     * @param array $array  [optional]
5865
     *
5866
     * @return static
5867
     *                <p>(Immutable)</p>
5868
     *
5869
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5870
     * @psalm-return static<TKey,T>
5871
     *
5872
     * @noinspection BadExceptionsProcessingInspection
5873
     * @noinspection NonSecureShuffleUsageInspection
5874
     */
5875 2
    public function shuffle(bool $secure = false, array $array = null): self
5876
    {
5877 2
        if ($array === null) {
5878 2
            $array = $this->toArray(false);
5879
        }
5880
5881 2
        if ($secure !== true) {
5882 2
            \shuffle($array);
5883
        } else {
5884 1
            $size = \count($array, \COUNT_NORMAL);
5885 1
            $keys = \array_keys($array);
5886 1
            for ($i = $size - 1; $i > 0; --$i) {
5887
                try {
5888 1
                    $r = \random_int(0, $i);
5889
                } catch (\Exception $e) {
5890
                    $r = \mt_rand(0, $i);
5891
                }
5892 1
                if ($r !== $i) {
5893 1
                    $temp = $array[$keys[$r]];
5894 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5895 1
                    $array[$keys[$i]] = $temp;
5896
                }
5897
            }
5898
        }
5899
5900 2
        foreach ($array as $key => $value) {
5901
            // check if recursive is needed
5902 2
            if (\is_array($value)) {
5903
                $array[$key] = $this->shuffle($secure, $value);
5904
            }
5905
        }
5906
5907 2
        return static::create(
5908 2
            $array,
5909 2
            $this->iteratorClass,
5910 2
            false
5911
        );
5912
    }
5913
5914
    /**
5915
     * Count the values from the current array.
5916
     *
5917
     * alias: for "Arrayy->count()"
5918
     *
5919
     * @param int $mode
5920
     *
5921
     * @return int
5922
     */
5923 20
    public function size(int $mode = \COUNT_NORMAL): int
5924
    {
5925 20
        return $this->count($mode);
5926
    }
5927
5928
    /**
5929
     * Checks whether array has exactly $size items.
5930
     *
5931
     * @param int $size
5932
     *
5933
     * @return bool
5934
     */
5935 1
    public function sizeIs(int $size): bool
5936
    {
5937
        // init
5938 1
        $itemsTempCount = 0;
5939
5940
        /** @noinspection PhpUnusedLocalVariableInspection */
5941
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5942 1
        foreach ($this->getGeneratorByReference() as &$value) {
5943 1
            ++$itemsTempCount;
5944 1
            if ($itemsTempCount > $size) {
5945 1
                return false;
5946
            }
5947
        }
5948
5949 1
        return $itemsTempCount === $size;
5950
    }
5951
5952
    /**
5953
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5954
     * smaller than $fromSize.
5955
     *
5956
     * @param int $fromSize
5957
     * @param int $toSize
5958
     *
5959
     * @return bool
5960
     */
5961 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5962
    {
5963 1
        if ($fromSize > $toSize) {
5964 1
            $tmp = $toSize;
5965 1
            $toSize = $fromSize;
5966 1
            $fromSize = $tmp;
5967
        }
5968
5969
        // init
5970 1
        $itemsTempCount = 0;
5971
5972 1
        foreach ($this->getGenerator() as $key => $value) {
5973 1
            ++$itemsTempCount;
5974 1
            if ($itemsTempCount > $toSize) {
5975 1
                return false;
5976
            }
5977
        }
5978
5979 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5980
    }
5981
5982
    /**
5983
     * Checks whether array has more than $size items.
5984
     *
5985
     * @param int $size
5986
     *
5987
     * @return bool
5988
     */
5989 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...
5990
    {
5991
        // init
5992 1
        $itemsTempCount = 0;
5993
5994 1
        foreach ($this->getGenerator() as $key => $value) {
5995 1
            ++$itemsTempCount;
5996 1
            if ($itemsTempCount > $size) {
5997 1
                return true;
5998
            }
5999
        }
6000
6001 1
        return $itemsTempCount > $size;
6002
    }
6003
6004
    /**
6005
     * Checks whether array has less than $size items.
6006
     *
6007
     * @param int $size
6008
     *
6009
     * @return bool
6010
     */
6011 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...
6012
    {
6013
        // init
6014 1
        $itemsTempCount = 0;
6015
6016 1
        foreach ($this->getGenerator() as $key => $value) {
6017 1
            ++$itemsTempCount;
6018 1
            if ($itemsTempCount > $size) {
6019 1
                return false;
6020
            }
6021
        }
6022
6023 1
        return $itemsTempCount < $size;
6024
    }
6025
6026
    /**
6027
     * Counts all elements in an array, or something in an object.
6028
     *
6029
     * <p>
6030
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6031
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6032
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6033
     * implemented and used in PHP.
6034
     * </p>
6035
     *
6036
     * @return int
6037
     *             <p>
6038
     *             The number of elements in var, which is
6039
     *             typically an array, since anything else will have one
6040
     *             element.
6041
     *             </p>
6042
     *             <p>
6043
     *             If var is not an array or an object with
6044
     *             implemented Countable interface,
6045
     *             1 will be returned.
6046
     *             There is one exception, if var is &null;,
6047
     *             0 will be returned.
6048
     *             </p>
6049
     *             <p>
6050
     *             Caution: count may return 0 for a variable that isn't set,
6051
     *             but it may also return 0 for a variable that has been initialized with an
6052
     *             empty array. Use isset to test if a variable is set.
6053
     *             </p>
6054
     */
6055 10
    public function sizeRecursive(): int
6056
    {
6057 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6058
    }
6059
6060
    /**
6061
     * Extract a slice of the array.
6062
     *
6063
     * @param int      $offset       <p>Slice begin index.</p>
6064
     * @param int|null $length       <p>Length of the slice.</p>
6065
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6066
     *
6067
     * @return static
6068
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6069
     *
6070
     * @psalm-return static<TKey,T>
6071
     * @psalm-mutation-free
6072
     */
6073 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6074
    {
6075 5
        return static::create(
6076 5
            \array_slice(
6077 5
                $this->toArray(),
6078 5
                $offset,
6079 5
                $length,
6080 5
                $preserveKeys
6081
            ),
6082 5
            $this->iteratorClass,
6083 5
            false
6084
        );
6085
    }
6086
6087
    /**
6088
     * Sort the current array and optional you can keep the keys.
6089
     *
6090
     * EXAMPLE: <code>
6091
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6092
     * </code>
6093
     *
6094
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6095
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6096
     *                              <strong>SORT_NATURAL</strong></p>
6097
     * @param bool       $keepKeys
6098
     *
6099
     * @return static
6100
     *                <p>(Mutable) Return this Arrayy object.</p>
6101
     *
6102
     * @psalm-return static<TKey,T>
6103
     */
6104 20
    public function sort(
6105
        $direction = \SORT_ASC,
6106
        int $strategy = \SORT_REGULAR,
6107
        bool $keepKeys = false
6108
    ): self {
6109 20
        $this->generatorToArray();
6110
6111 20
        return $this->sorting(
6112 20
            $this->array,
6113 20
            $direction,
6114 20
            $strategy,
6115 20
            $keepKeys
6116
        );
6117
    }
6118
6119
    /**
6120
     * Sort the current array and optional you can keep the keys.
6121
     *
6122
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6123
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6124
     *                              <strong>SORT_NATURAL</strong></p>
6125
     * @param bool       $keepKeys
6126
     *
6127
     * @return static
6128
     *                <p>(Immutable) Return this Arrayy object.</p>
6129
     *
6130
     * @psalm-return static<TKey,T>
6131
     */
6132 12
    public function sortImmutable(
6133
        $direction = \SORT_ASC,
6134
        int $strategy = \SORT_REGULAR,
6135
        bool $keepKeys = false
6136
    ): self {
6137 12
        $that = clone $this;
6138
6139 12
        $that->generatorToArray();
6140
6141 12
        return $that->sorting(
6142 12
            $that->array,
6143 12
            $direction,
6144 12
            $strategy,
6145 12
            $keepKeys
6146
        );
6147
    }
6148
6149
    /**
6150
     * Sort the current array by key.
6151
     *
6152
     * EXAMPLE: <code>
6153
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6154
     * </code>
6155
     *
6156
     * @see http://php.net/manual/en/function.ksort.php
6157
     * @see http://php.net/manual/en/function.krsort.php
6158
     *
6159
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6160
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6161
     *                              <strong>SORT_NATURAL</strong></p>
6162
     *
6163
     * @return $this
6164
     *               <p>(Mutable) Return this Arrayy object.</p>
6165
     *
6166
     * @psalm-return static<TKey,T>
6167
     */
6168 18
    public function sortKeys(
6169
        $direction = \SORT_ASC,
6170
        int $strategy = \SORT_REGULAR
6171
    ): self {
6172 18
        $this->generatorToArray();
6173
6174 18
        $this->sorterKeys($this->array, $direction, $strategy);
6175
6176 18
        return $this;
6177
    }
6178
6179
    /**
6180
     * Sort the current array by key.
6181
     *
6182
     * @see          http://php.net/manual/en/function.ksort.php
6183
     * @see          http://php.net/manual/en/function.krsort.php
6184
     *
6185
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6186
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6187
     *                              <strong>SORT_NATURAL</strong></p>
6188
     *
6189
     * @return $this
6190
     *               <p>(Immutable) Return this Arrayy object.</p>
6191
     *
6192
     * @psalm-return static<TKey,T>
6193
     * @psalm-mutation-free
6194
     */
6195 8
    public function sortKeysImmutable(
6196
        $direction = \SORT_ASC,
6197
        int $strategy = \SORT_REGULAR
6198
    ): self {
6199 8
        $that = clone $this;
6200
6201
        /**
6202
         * @psalm-suppress ImpureMethodCall - object is already cloned
6203
         */
6204 8
        $that->sortKeys($direction, $strategy);
6205
6206 8
        return $that;
6207
    }
6208
6209
    /**
6210
     * Sort the current array by value.
6211
     *
6212
     * EXAMPLE: <code>
6213
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6214
     * </code>
6215
     *
6216
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6217
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6218
     *                              <strong>SORT_NATURAL</strong></p>
6219
     *
6220
     * @return static
6221
     *                <p>(Mutable)</p>
6222
     *
6223
     * @psalm-return static<TKey,T>
6224
     */
6225 1
    public function sortValueKeepIndex(
6226
        $direction = \SORT_ASC,
6227
        int $strategy = \SORT_REGULAR
6228
    ): self {
6229 1
        return $this->sort($direction, $strategy, true);
6230
    }
6231
6232
    /**
6233
     * Sort the current array by value.
6234
     *
6235
     * EXAMPLE: <code>
6236
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6237
     * </code>
6238
     *
6239
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6240
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6241
     *                              <strong>SORT_NATURAL</strong></p>
6242
     *
6243
     * @return static
6244
     *                <p>(Mutable)</p>
6245
     *
6246
     * @psalm-return static<TKey,T>
6247
     */
6248 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6249
    {
6250 1
        return $this->sort($direction, $strategy, false);
6251
    }
6252
6253
    /**
6254
     * Sort a array by value or by a closure.
6255
     *
6256
     * - If the sorter is null, the array is sorted naturally.
6257
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6258
     *
6259
     * EXAMPLE: <code>
6260
     * $testArray = range(1, 5);
6261
     * $under = a($testArray)->sorter(
6262
     *     function ($value) {
6263
     *         return $value % 2 === 0;
6264
     *     }
6265
     * );
6266
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6267
     * </code>
6268
     *
6269
     * @param callable|string|null $sorter
6270
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6271
     *                                        <strong>SORT_DESC</strong></p>
6272
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6273
     *                                        <strong>SORT_NATURAL</strong></p>
6274
     *
6275
     * @return static
6276
     *                <p>(Immutable)</p>
6277
     *
6278
     * @psalm-return static<TKey,T>
6279
     * @psalm-mutation-free
6280
     */
6281 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6282
    {
6283 1
        $array = $this->toArray();
6284 1
        $direction = $this->getDirection($direction);
6285
6286
        // Transform all values into their results.
6287 1
        if ($sorter) {
6288 1
            $arrayy = static::create(
6289 1
                $array,
6290 1
                $this->iteratorClass,
6291 1
                false
6292
            );
6293
6294
            /**
6295
             * @psalm-suppress MissingClosureReturnType
6296
             * @psalm-suppress MissingClosureParamType
6297
             */
6298 1
            $results = $arrayy->each(
6299
                static function ($value) use ($sorter) {
6300 1
                    if (\is_callable($sorter) === true) {
6301 1
                        return $sorter($value);
6302
                    }
6303
6304 1
                    return $sorter === $value;
6305 1
                }
6306
            );
6307
6308 1
            $results = $results->toArray();
6309
        } else {
6310 1
            $results = $array;
6311
        }
6312
6313
        // Sort by the results and replace by original values
6314 1
        \array_multisort($results, $direction, $strategy, $array);
6315
6316 1
        return static::create(
6317 1
            $array,
6318 1
            $this->iteratorClass,
6319 1
            false
6320
        );
6321
    }
6322
6323
    /**
6324
     * @param int      $offset
6325
     * @param int|null $length
6326
     * @param array    $replacement
6327
     *
6328
     * @return static
6329
     *                <p>(Immutable)</p>
6330
     *
6331
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6332
     * @psalm-return static<TKey,T>
6333
     * @psalm-mutation-free
6334
     */
6335 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6336
    {
6337 1
        $tmpArray = $this->toArray();
6338
6339 1
        \array_splice(
6340 1
            $tmpArray,
6341 1
            $offset,
6342 1
            $length ?? $this->count(),
6343 1
            $replacement
6344
        );
6345
6346 1
        return static::create(
6347 1
            $tmpArray,
6348 1
            $this->iteratorClass,
6349 1
            false
6350
        );
6351
    }
6352
6353
    /**
6354
     * Split an array in the given amount of pieces.
6355
     *
6356
     * EXAMPLE: <code>
6357
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6358
     * </code>
6359
     *
6360
     * @param int  $numberOfPieces
6361
     * @param bool $keepKeys
6362
     *
6363
     * @return static
6364
     *                <p>(Immutable)</p>
6365
     *
6366
     * @psalm-return static<TKey,T>
6367
     * @psalm-mutation-free
6368
     */
6369 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6370
    {
6371 1
        if ($keepKeys) {
6372
            $generator = function () use ($numberOfPieces) {
6373 1
                $carry = [];
6374 1
                $i = 1;
6375 1
                foreach ($this->getGenerator() as $key => $value) {
6376 1
                    $carry[$key] = $value;
6377
6378 1
                    if ($i % $numberOfPieces !== 0) {
6379 1
                        ++$i;
6380
6381 1
                        continue;
6382
                    }
6383
6384 1
                    yield $carry;
6385
6386 1
                    $carry = [];
6387 1
                    $i = 1;
6388
                }
6389
6390 1
                if ($carry !== []) {
6391 1
                    yield $carry;
6392
                }
6393 1
            };
6394 View Code Duplication
        } else {
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...
6395
            $generator = function () use ($numberOfPieces) {
6396 1
                $carry = [];
6397 1
                $i = 1;
6398 1
                foreach ($this->getGenerator() as $key => $value) {
6399 1
                    $carry[] = $value;
6400
6401 1
                    if ($i % $numberOfPieces !== 0) {
6402 1
                        ++$i;
6403
6404 1
                        continue;
6405
                    }
6406
6407 1
                    yield $carry;
6408
6409 1
                    $carry = [];
6410 1
                    $i = 1;
6411
                }
6412
6413 1
                if ($carry !== []) {
6414 1
                    yield $carry;
6415
                }
6416 1
            };
6417
        }
6418
6419 1
        return static::create(
6420 1
            $generator,
6421 1
            $this->iteratorClass,
6422 1
            false
6423
        );
6424
    }
6425
6426
    /**
6427
     * Strip all empty items from the current array.
6428
     *
6429
     * EXAMPLE: <code>
6430
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6431
     * </code>
6432
     *
6433
     * @return static
6434
     *                <p>(Immutable)</p>
6435
     *
6436
     * @psalm-return static<TKey,T>
6437
     * @psalm-mutation-free
6438
     */
6439 1
    public function stripEmpty(): self
6440
    {
6441 1
        return $this->filter(
6442
            static function ($item) {
6443 1
                if ($item === null) {
6444 1
                    return false;
6445
                }
6446
6447 1
                return (bool) \trim((string) $item);
6448 1
            }
6449
        );
6450
    }
6451
6452
    /**
6453
     * Swap two values between positions by key.
6454
     *
6455
     * EXAMPLE: <code>
6456
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6457
     * </code>
6458
     *
6459
     * @param int|string $swapA <p>a key in the array</p>
6460
     * @param int|string $swapB <p>a key in the array</p>
6461
     *
6462
     * @return static
6463
     *                <p>(Immutable)</p>
6464
     *
6465
     * @psalm-return static<TKey,T>
6466
     * @psalm-mutation-free
6467
     */
6468 1
    public function swap($swapA, $swapB): self
6469
    {
6470 1
        $array = $this->toArray();
6471
6472 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6473
6474 1
        return static::create(
6475 1
            $array,
6476 1
            $this->iteratorClass,
6477 1
            false
6478
        );
6479
    }
6480
6481
    /**
6482
     * Get the current array from the "Arrayy"-object.
6483
     * alias for "getArray()"
6484
     *
6485
     * @param bool $convertAllArrayyElements <p>
6486
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6487
     *                                       </p>
6488
     * @param bool $preserveKeys             <p>
6489
     *                                       e.g.: A generator maybe return the same key more then once,
6490
     *                                       so maybe you will ignore the keys.
6491
     *                                       </p>
6492
     *
6493
     * @return array
6494
     *
6495
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6496
     * @psalm-mutation-free
6497
     */
6498 932
    public function toArray(
6499
        bool $convertAllArrayyElements = false,
6500
        bool $preserveKeys = true
6501
    ): array {
6502
        // init
6503 932
        $array = [];
6504
6505 932
        if ($convertAllArrayyElements) {
6506 2
            foreach ($this->getGenerator() as $key => $value) {
6507 2
                if ($value instanceof self) {
6508 1
                    $value = $value->toArray(
6509 1
                        $convertAllArrayyElements,
6510 1
                        $preserveKeys
6511
                    );
6512
                }
6513
6514 2
                if ($preserveKeys) {
6515 1
                    $array[$key] = $value;
6516
                } else {
6517 1
                    $array[] = $value;
6518
                }
6519
            }
6520
        } else {
6521 932
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6522
        }
6523
6524 932
        return $array;
6525
    }
6526
6527
    /**
6528
     * Get the current array from the "Arrayy"-object as list.
6529
     *
6530
     * @param bool $convertAllArrayyElements <p>
6531
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6532
     *                                       </p>
6533
     *
6534
     * @return array
6535
     *
6536
     * @psalm-return list<array<TKey,T>>
6537
     * @psalm-mutation-free
6538
     */
6539 1
    public function toList(bool $convertAllArrayyElements = false): array
6540
    {
6541 1
        return $this->toArray(
6542 1
            $convertAllArrayyElements,
6543 1
            false
6544
        );
6545
    }
6546
6547
    /**
6548
     * Convert the current array to JSON.
6549
     *
6550
     * EXAMPLE: <code>
6551
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6552
     * </code>
6553
     *
6554
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6555
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6556
     *
6557
     * @return string
6558
     */
6559 12
    public function toJson(int $options = 0, int $depth = 512): string
6560
    {
6561 12
        $return = \json_encode($this->toArray(), $options, $depth);
6562 12
        if ($return === false) {
6563
            return '';
6564
        }
6565
6566 12
        return $return;
6567
    }
6568
6569
    /**
6570
     * @param string[]|null $items  [optional]
6571
     * @param string[]      $helper [optional]
6572
     *
6573
     * @return static|static[]
6574
     *
6575
     * @psalm-return static<int, static<TKey,T>>
6576
     */
6577 1
    public function toPermutation(array $items = null, array $helper = []): self
6578
    {
6579
        // init
6580 1
        $return = [];
6581
6582 1
        if ($items === null) {
6583 1
            $items = $this->toArray();
6584
        }
6585
6586 1
        if (empty($items)) {
6587 1
            $return[] = $helper;
6588
        } else {
6589 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6590 1
                $new_items = $items;
6591 1
                $new_helper = $helper;
6592 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6593
                /** @noinspection PhpSillyAssignmentInspection */
6594
                /** @var string[] $new_items */
6595 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...
6596 1
                \array_unshift($new_helper, $tmp_helper);
6597 1
                $return = \array_merge(
6598 1
                    $return,
6599 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6600
                );
6601
            }
6602
        }
6603
6604 1
        return static::create(
6605 1
            $return,
6606 1
            $this->iteratorClass,
6607 1
            false
6608
        );
6609
    }
6610
6611
    /**
6612
     * Implodes array to a string with specified separator.
6613
     *
6614
     * @param string $separator [optional] <p>The element's separator.</p>
6615
     *
6616
     * @return string
6617
     *                <p>The string representation of array, separated by ",".</p>
6618
     */
6619 19
    public function toString(string $separator = ','): string
6620
    {
6621 19
        return $this->implode($separator);
6622
    }
6623
6624
    /**
6625
     * Return a duplicate free copy of the current array.
6626
     *
6627
     * EXAMPLE: <code>
6628
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6629
     * </code>
6630
     *
6631
     * @return $this
6632
     *               <p>(Mutable)</p>
6633
     *
6634
     * @psalm-return static<TKey,T>
6635
     */
6636 13
    public function uniqueNewIndex(): self
6637
    {
6638
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6639
6640
        /**
6641
         * @psalm-suppress MissingClosureReturnType
6642
         * @psalm-suppress MissingClosureParamType
6643
         */
6644 13
        $this->array = $this->reduce(
6645
            static function ($resultArray, $value) {
6646 12
                if (!\in_array($value, $resultArray, true)) {
6647 12
                    $resultArray[] = $value;
6648
                }
6649
6650 12
                return $resultArray;
6651 13
            },
6652 13
            []
6653 13
        )->toArray();
6654 13
        $this->generator = null;
6655
6656 13
        return $this;
6657
    }
6658
6659
    /**
6660
     * Return a duplicate free copy of the current array. (with the old keys)
6661
     *
6662
     * EXAMPLE: <code>
6663
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6664
     * </code>
6665
     *
6666
     * @return $this
6667
     *               <p>(Mutable)</p>
6668
     *
6669
     * @psalm-return static<TKey,T>
6670
     */
6671 11
    public function uniqueKeepIndex(): self
6672
    {
6673
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6674
6675
        // init
6676 11
        $array = $this->toArray();
6677
6678
        /**
6679
         * @psalm-suppress MissingClosureReturnType
6680
         * @psalm-suppress MissingClosureParamType
6681
         */
6682 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...
6683 11
            \array_keys($array),
6684
            static function ($resultArray, $key) use ($array) {
6685 10
                if (!\in_array($array[$key], $resultArray, true)) {
6686 10
                    $resultArray[$key] = $array[$key];
6687
                }
6688
6689 10
                return $resultArray;
6690 11
            },
6691 11
            []
6692
        );
6693 11
        $this->generator = null;
6694
6695 11
        return $this;
6696
    }
6697
6698
    /**
6699
     * alias: for "Arrayy->uniqueNewIndex()"
6700
     *
6701
     * @return static
6702
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6703
     *
6704
     * @see          Arrayy::unique()
6705
     *
6706
     * @psalm-return static<TKey,T>
6707
     */
6708 13
    public function unique(): self
6709
    {
6710 13
        return $this->uniqueNewIndex();
6711
    }
6712
6713
    /**
6714
     * Prepends one or more values to the beginning of array at once.
6715
     *
6716
     * @param array ...$args
6717
     *
6718
     * @return $this
6719
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6720
     *
6721
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6722
     * @psalm-return static<TKey,T>
6723
     */
6724 4
    public function unshift(...$args): self
6725
    {
6726 4
        $this->generatorToArray();
6727
6728 4
        \array_unshift($this->array, ...$args);
6729
6730 4
        return $this;
6731
    }
6732
6733
    /**
6734
     * Tests whether the given closure return something valid for all elements of this array.
6735
     *
6736
     * @param \Closure $closure the predicate
6737
     *
6738
     * @return bool
6739
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6740
     */
6741 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...
6742
    {
6743 1
        foreach ($this->getGenerator() as $key => $value) {
6744 1
            if (!$closure($value, $key)) {
6745 1
                return false;
6746
            }
6747
        }
6748
6749 1
        return true;
6750
    }
6751
6752
    /**
6753
     * Get all values from a array.
6754
     *
6755
     * EXAMPLE: <code>
6756
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6757
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6758
     * </code>
6759
     *
6760
     * @return static
6761
     *                <p>(Immutable)</p>
6762
     *
6763
     * @psalm-return static<TKey,T>
6764
     * @psalm-mutation-free
6765
     */
6766 2
    public function values(): self
6767
    {
6768 2
        return static::create(
6769
            function () {
6770
                /** @noinspection YieldFromCanBeUsedInspection */
6771 2
                foreach ($this->getGenerator() as $value) {
6772 2
                    yield $value;
6773
                }
6774 2
            },
6775 2
            $this->iteratorClass,
6776 2
            false
6777
        );
6778
    }
6779
6780
    /**
6781
     * Apply the given function to every element in the array, discarding the results.
6782
     *
6783
     * EXAMPLE: <code>
6784
     * $callable = function (&$value, $key) {
6785
     *     $value = $key;
6786
     * };
6787
     * $arrayy = a([1, 2, 3]);
6788
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6789
     * </code>
6790
     *
6791
     * @param callable $callable
6792
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6793
     * @param mixed    $userData  [optional] <p>
6794
     *                            If the optional $userData parameter is supplied,
6795
     *                            it will be passed as the third parameter to the $callable.
6796
     *                            </p>
6797
     *
6798
     * @return $this
6799
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6800
     *
6801
     * @psalm-return static<TKey,T>
6802
     */
6803 12
    public function walk(
6804
        $callable,
6805
        bool $recursive = false,
6806
        $userData = self::ARRAYY_HELPER_WALK
6807
    ): self {
6808 12
        $this->generatorToArray();
6809
6810 12
        if ($this->array !== []) {
6811 10
            if ($recursive === true) {
6812 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6813
                    \array_walk_recursive($this->array, $callable, $userData);
6814
                } else {
6815 5
                    \array_walk_recursive($this->array, $callable);
6816
                }
6817
            } else {
6818
                /** @noinspection NestedPositiveIfStatementsInspection */
6819 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6820
                    \array_walk($this->array, $callable, $userData);
6821
                } else {
6822 5
                    \array_walk($this->array, $callable);
6823
                }
6824
            }
6825
        }
6826
6827 12
        return $this;
6828
    }
6829
6830
    /**
6831
     * Returns a collection of matching items.
6832
     *
6833
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6834
     * @param mixed  $value                 the value to match
6835
     *
6836
     * @throws \InvalidArgumentException if property or method is not defined
6837
     *
6838
     * @return static
6839
     *
6840
     * @psalm-param  T $value
6841
     * @psalm-return static<TKey,T>
6842
     */
6843 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6844
    {
6845 1
        return $this->filter(
6846
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6847
                $accessorValue = $this->extractValue(
6848
                    $item,
6849
                    $keyOrPropertyOrMethod
6850
                );
6851
6852
                return $accessorValue === $value;
6853 1
            }
6854
        );
6855
    }
6856
6857
    /**
6858
     * Convert an array into a object.
6859
     *
6860
     * @param array $array
6861
     *
6862
     * @return \stdClass
6863
     *
6864
     * @psalm-param array<mixed,mixed> $array
6865
     */
6866 4
    final protected static function arrayToObject(array $array = []): \stdClass
6867
    {
6868
        // init
6869 4
        $object = new \stdClass();
6870
6871 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6872 1
            return $object;
6873
        }
6874
6875 3
        foreach ($array as $name => $value) {
6876 3
            if (\is_array($value)) {
6877 1
                $object->{$name} = static::arrayToObject($value);
6878
            } else {
6879 3
                $object->{$name} = $value;
6880
            }
6881
        }
6882
6883 3
        return $object;
6884
    }
6885
6886
    /**
6887
     * @param array|\Generator|null $input         <p>
6888
     *                                             An array containing keys to return.
6889
     *                                             </p>
6890
     * @param mixed|null            $search_values [optional] <p>
6891
     *                                             If specified, then only keys containing these values are returned.
6892
     *                                             </p>
6893
     * @param bool                  $strict        [optional] <p>
6894
     *                                             Determines if strict comparison (===) should be used during the
6895
     *                                             search.
6896
     *                                             </p>
6897
     *
6898
     * @return array
6899
     *               <p>an array of all the keys in input</p>
6900
     *
6901
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6902
     * @psalm-return array<TKey|mixed>
6903
     * @psalm-mutation-free
6904
     */
6905 11
    protected function array_keys_recursive(
6906
        $input = null,
6907
        $search_values = null,
6908
        bool $strict = true
6909
    ): array {
6910
        // init
6911 11
        $keys = [];
6912 11
        $keysTmp = [];
6913
6914 11
        if ($input === null) {
6915 4
            $input = $this->getGenerator();
6916
        }
6917
6918 11
        if ($search_values === null) {
6919 11
            foreach ($input as $key => $value) {
6920 11
                $keys[] = $key;
6921
6922
                // check if recursive is needed
6923 11
                if (\is_array($value)) {
6924 4
                    $keysTmp[] = $this->array_keys_recursive($value);
6925
                }
6926
            }
6927
        } else {
6928 1
            $is_array_tmp = \is_array($search_values);
6929
6930 1
            foreach ($input as $key => $value) {
6931 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...
6932
                    (
6933 1
                        $is_array_tmp === false
6934
                        &&
6935 1
                        $strict === true
6936
                        &&
6937 1
                        $search_values === $value
6938
                    )
6939
                    ||
6940
                    (
6941 1
                        $is_array_tmp === false
6942
                        &&
6943 1
                        $strict === false
6944
                        &&
6945 1
                        $search_values == $value
6946
                    )
6947
                    ||
6948
                    (
6949 1
                        $is_array_tmp === true
6950
                        &&
6951 1
                        \in_array($value, $search_values, $strict)
6952
                    )
6953
                ) {
6954 1
                    $keys[] = $key;
6955
                }
6956
6957
                // check if recursive is needed
6958 1
                if (\is_array($value)) {
6959 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6960
                }
6961
            }
6962
        }
6963
6964 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6965
    }
6966
6967
    /**
6968
     * @param mixed      $path
6969
     * @param callable   $callable
6970
     * @param array|null $currentOffset
6971
     *
6972
     * @return void
6973
     *
6974
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6975
     * @psalm-mutation-free
6976
     */
6977 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6978
    {
6979 10
        $this->generatorToArray();
6980
6981 10
        if ($currentOffset === null) {
6982 10
            $currentOffset = &$this->array;
6983
        }
6984
6985 10
        $explodedPath = \explode($this->pathSeparator, $path);
6986 10
        if ($explodedPath === false) {
6987
            return;
6988
        }
6989
6990 10
        $nextPath = \array_shift($explodedPath);
6991
6992 10
        if (!isset($currentOffset[$nextPath])) {
6993 1
            return;
6994
        }
6995
6996 9
        if (!empty($explodedPath)) {
6997 1
            $this->callAtPath(
6998 1
                \implode($this->pathSeparator, $explodedPath),
6999 1
                $callable,
7000 1
                $currentOffset[$nextPath]
7001
            );
7002
        } else {
7003 9
            $callable($currentOffset[$nextPath]);
7004
        }
7005 9
    }
7006
7007
    /**
7008
     * Extracts the value of the given property or method from the object.
7009
     *
7010
     * @param static $object                <p>The object to extract the value from.</p>
7011
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
7012
     *                                      value should be extracted.</p>
7013
     *
7014
     * @throws \InvalidArgumentException if the method or property is not defined
7015
     *
7016
     * @return mixed
7017
     *               <p>The value extracted from the specified property or method.</p>
7018
     *
7019
     * @psalm-param self<TKey,T> $object
7020
     */
7021 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7022
    {
7023 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7024 1
            $return = $object->get($keyOrPropertyOrMethod);
7025
7026 1
            if ($return instanceof self) {
7027
                return $return->toArray();
7028
            }
7029
7030 1
            return $return;
7031
        }
7032
7033
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7034
            return $object->{$keyOrPropertyOrMethod};
7035
        }
7036
7037
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7038
            return $object->{$keyOrPropertyOrMethod}();
7039
        }
7040
7041
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7042
    }
7043
7044
    /**
7045
     * create a fallback for array
7046
     *
7047
     * 1. use the current array, if it's a array
7048
     * 2. fallback to empty array, if there is nothing
7049
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7050
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7051
     * 5. call "__toArray()" on object, if the method exists
7052
     * 6. cast a string or object with "__toString()" into an array
7053
     * 7. throw a "InvalidArgumentException"-Exception
7054
     *
7055
     * @param mixed $data
7056
     *
7057
     * @throws \InvalidArgumentException
7058
     *
7059
     * @return array
7060
     *
7061
     * @psalm-return array<mixed,mixed>|array<TKey,T>
7062
     */
7063 1207
    protected function fallbackForArray(&$data): array
7064
    {
7065 1207
        $data = $this->internalGetArray($data);
7066
7067 1207
        if ($data === null) {
7068 2
            throw new \InvalidArgumentException('Passed value should be a array');
7069
        }
7070
7071 1205
        return $data;
7072
    }
7073
7074
    /**
7075
     * @param bool $preserveKeys <p>
7076
     *                           e.g.: A generator maybe return the same key more then once,
7077
     *                           so maybe you will ignore the keys.
7078
     *                           </p>
7079
     *
7080
     * @return bool
7081
     *
7082
     * @noinspection ReturnTypeCanBeDeclaredInspection
7083
     * @psalm-mutation-free :/
7084
     */
7085 1119
    protected function generatorToArray(bool $preserveKeys = true)
7086
    {
7087 1119
        if ($this->generator) {
7088 2
            $this->array = $this->toArray(false, $preserveKeys);
7089 2
            $this->generator = null;
7090
7091 2
            return true;
7092
        }
7093
7094 1119
        return false;
7095
    }
7096
7097
    /**
7098
     * Get correct PHP constant for direction.
7099
     *
7100
     * @param int|string $direction
7101
     *
7102
     * @return int
7103
     * @psalm-mutation-free
7104
     */
7105 43
    protected function getDirection($direction): int
7106
    {
7107 43
        if ((string) $direction === $direction) {
7108 10
            $direction = \strtolower($direction);
7109
7110 10
            if ($direction === 'desc') {
7111 2
                $direction = \SORT_DESC;
7112
            } else {
7113 9
                $direction = \SORT_ASC;
7114
            }
7115
        }
7116
7117
        if (
7118 43
            $direction !== \SORT_DESC
7119
            &&
7120 43
            $direction !== \SORT_ASC
7121
        ) {
7122
            $direction = \SORT_ASC;
7123
        }
7124
7125 43
        return $direction;
7126
    }
7127
7128
    /**
7129
     * @return TypeCheckInterface[]
7130
     *
7131
     * @noinspection ReturnTypeCanBeDeclaredInspection
7132
     */
7133 24
    protected function getPropertiesFromPhpDoc()
7134
    {
7135 24
        static $PROPERTY_CACHE = [];
7136 24
        $cacheKey = 'Class::' . static::class;
7137
7138 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7139 22
            return $PROPERTY_CACHE[$cacheKey];
7140
        }
7141
7142
        // init
7143 4
        $properties = [];
7144
7145 4
        $reflector = new \ReflectionClass($this);
7146 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7147 4
        $docComment = $reflector->getDocComment();
7148 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...
7149 3
            $docblock = $factory->create($docComment);
7150
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7151 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7152 3
                $typeName = $tag->getVariableName();
7153
                /** @var string|null $typeName */
7154 3
                if ($typeName !== null) {
7155 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7156 3
                    if ($typeCheckPhpDoc !== null) {
7157 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7158
                    }
7159
                }
7160
            }
7161
        }
7162
7163
        /** @noinspection PhpAssignmentInConditionInspection */
7164 4
        while ($reflector = $reflector->getParentClass()) {
7165 4
            $docComment = $reflector->getDocComment();
7166 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...
7167 4
                $docblock = $factory->create($docComment);
7168
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7169 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7170 1
                    $typeName = $tag->getVariableName();
7171
                    /** @var string|null $typeName */
7172 1
                    if ($typeName !== null) {
7173 1
                        if (isset($properties[$typeName])) {
7174 1
                            continue;
7175
                        }
7176
7177 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7178 1
                        if ($typeCheckPhpDoc !== null) {
7179 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7180
                        }
7181
                    }
7182
                }
7183
            }
7184
        }
7185
7186 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7187
    }
7188
7189
    /**
7190
     * @param mixed $glue
7191
     * @param mixed $pieces
7192
     * @param bool  $useKeys
7193
     *
7194
     * @return string
7195
     * @psalm-mutation-free
7196
     */
7197 36
    protected function implode_recursive(
7198
        $glue = '',
7199
        $pieces = [],
7200
        bool $useKeys = false
7201
    ): string {
7202 36
        if ($pieces instanceof self) {
7203 1
            $pieces = $pieces->toArray();
7204
        }
7205
7206 36
        if (\is_array($pieces)) {
7207 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7208 36
            $pieces_count_not_zero = $pieces_count > 0;
7209
7210 36
            return \implode(
7211 36
                $glue,
7212 36
                \array_map(
7213 36
                    [$this, 'implode_recursive'],
7214 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7215 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7216
                )
7217
            );
7218
        }
7219
7220
        if (
7221 36
            \is_scalar($pieces) === true
7222
            ||
7223 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7224
        ) {
7225 32
            return (string) $pieces;
7226
        }
7227
7228 8
        return '';
7229
    }
7230
7231
    /**
7232
     * @param mixed                 $needle   <p>
7233
     *                                        The searched value.
7234
     *                                        </p>
7235
     *                                        <p>
7236
     *                                        If needle is a string, the comparison is done
7237
     *                                        in a case-sensitive manner.
7238
     *                                        </p>
7239
     * @param array|\Generator|null $haystack <p>
7240
     *                                        The array.
7241
     *                                        </p>
7242
     * @param bool                  $strict   [optional] <p>
7243
     *                                        If the third parameter strict is set to true
7244
     *                                        then the in_array function will also check the
7245
     *                                        types of the
7246
     *                                        needle in the haystack.
7247
     *                                        </p>
7248
     *
7249
     * @return bool
7250
     *              <p>true if needle is found in the array, false otherwise</p>
7251
     *
7252
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7253
     * @psalm-mutation-free
7254
     */
7255 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7256
    {
7257 19
        if ($haystack === null) {
7258
            $haystack = $this->getGenerator();
7259
        }
7260
7261 19
        foreach ($haystack as $item) {
7262 15
            if (\is_array($item)) {
7263 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7264
            } else {
7265
                /** @noinspection NestedPositiveIfStatementsInspection */
7266 15
                if ($strict === true) {
7267 15
                    $returnTmp = $item === $needle;
7268
                } else {
7269
                    $returnTmp = $item == $needle;
7270
                }
7271
            }
7272
7273 15
            if ($returnTmp === true) {
7274 11
                return true;
7275
            }
7276
        }
7277
7278 8
        return false;
7279
    }
7280
7281
    /**
7282
     * @param mixed $data
7283
     *
7284
     * @return array|null
7285
     *
7286
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7287
     */
7288 1207
    protected function internalGetArray(&$data)
7289
    {
7290 1207
        if (\is_array($data)) {
7291 1201
            return $data;
7292
        }
7293
7294 81
        if (!$data) {
7295 7
            return [];
7296
        }
7297
7298 80
        if (\is_object($data) === true) {
7299 73
            if ($data instanceof \ArrayObject) {
7300 5
                return $data->getArrayCopy();
7301
            }
7302
7303 69
            if ($data instanceof \Generator) {
7304
                return static::createFromGeneratorImmutable($data)->toArray();
7305
            }
7306
7307 69
            if ($data instanceof \Traversable) {
7308
                return static::createFromObject($data)->toArray();
7309
            }
7310
7311 69
            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...
7312
                return (array) $data->jsonSerialize();
7313
            }
7314
7315 69
            if (\method_exists($data, '__toArray')) {
7316
                return (array) $data->__toArray();
7317
            }
7318
7319 69
            if (\method_exists($data, '__toString')) {
7320
                return [(string) $data];
7321
            }
7322
        }
7323
7324 76
        if (\is_callable($data)) {
7325
            /**
7326
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7327
             */
7328 67
            $this->generator = new ArrayyRewindableGenerator($data);
7329
7330 67
            return [];
7331
        }
7332
7333 11
        if (\is_scalar($data)) {
7334 9
            return [$data];
7335
        }
7336
7337 2
        return null;
7338
    }
7339
7340
    /**
7341
     * Internal mechanics of remove method.
7342
     *
7343
     * @param mixed $key
7344
     *
7345
     * @return bool
7346
     */
7347 22
    protected function internalRemove($key): bool
7348
    {
7349 22
        $this->generatorToArray();
7350
7351
        if (
7352 22
            $this->pathSeparator
7353
            &&
7354 22
            (string) $key === $key
7355
            &&
7356 22
            \strpos($key, $this->pathSeparator) !== false
7357
        ) {
7358
            $path = \explode($this->pathSeparator, (string) $key);
7359
7360
            if ($path !== false) {
7361
                // crawl though the keys
7362
                while (\count($path, \COUNT_NORMAL) > 1) {
7363
                    $key = \array_shift($path);
7364
7365
                    if (!$this->has($key)) {
7366
                        return false;
7367
                    }
7368
7369
                    $this->array = &$this->array[$key];
7370
                }
7371
7372
                $key = \array_shift($path);
7373
            }
7374
        }
7375
7376 22
        unset($this->array[$key]);
7377
7378 22
        return true;
7379
    }
7380
7381
    /**
7382
     * Internal mechanic of set method.
7383
     *
7384
     * @param int|string|null $key
7385
     * @param mixed           $value
7386
     * @param bool            $checkProperties
7387
     *
7388
     * @return bool
7389
     */
7390 1057
    protected function internalSet(
7391
        $key,
7392
        &$value,
7393
        bool $checkProperties = true
7394
    ): bool {
7395
        if (
7396 1057
            $checkProperties === true
7397
            &&
7398 1057
            $this->properties !== []
7399
        ) {
7400 112
            $this->checkType($key, $value);
7401
        }
7402
7403 1055
        if ($key === null) {
7404
            return false;
7405
        }
7406
7407 1055
        $this->generatorToArray();
7408
7409
        /** @psalm-var array<int|string,mixed> $array */
7410 1055
        $array = &$this->array;
7411
7412
        /**
7413
         * https://github.com/vimeo/psalm/issues/2536
7414
         *
7415
         * @psalm-suppress PossiblyInvalidArgument
7416
         * @psalm-suppress InvalidScalarArgument
7417
         */
7418
        if (
7419 1055
            $this->pathSeparator
7420
            &&
7421 1055
            (string) $key === $key
7422
            &&
7423 1055
            \strpos($key, $this->pathSeparator) !== false
7424
        ) {
7425 9
            $path = \explode($this->pathSeparator, (string) $key);
7426
7427 9
            if ($path !== false) {
7428
                // crawl through the keys
7429 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7430 9
                    $key = \array_shift($path);
7431
7432 9
                    $array = &$array[$key];
7433
                }
7434
7435 9
                $key = \array_shift($path);
7436
            }
7437
        }
7438
7439 1055
        if ($array === null) {
7440 4
            $array = [];
7441 1052
        } elseif (!\is_array($array)) {
7442 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7443
        }
7444
7445 1055
        $array[$key] = $value;
7446
7447 1055
        return true;
7448
    }
7449
7450
    /**
7451
     * Convert a object into an array.
7452
     *
7453
     * @param mixed|object $object
7454
     *
7455
     * @return array|mixed
7456
     *
7457
     * @psalm-mutation-free
7458
     */
7459 5
    protected static function objectToArray($object)
7460
    {
7461 5
        if (!\is_object($object)) {
7462 4
            return $object;
7463
        }
7464
7465 5
        $object = \get_object_vars($object);
7466
7467
        /**
7468
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7469
         */
7470 5
        return \array_map(['static', 'objectToArray'], $object);
7471
    }
7472
7473
    /**
7474
     * @param array $data
7475
     * @param bool  $checkPropertiesInConstructor
7476
     *
7477
     * @return void
7478
     *
7479
     * @psalm-param array<mixed,T> $data
7480
     */
7481 1205
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7482
    {
7483 1205
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7484
                                        &&
7485 1205
                                        $checkPropertiesInConstructor === true;
7486
7487 1205
        if ($this->properties !== []) {
7488 99
            foreach ($data as $key => &$valueInner) {
7489 99
                $this->internalSet(
7490 99
                    $key,
7491 99
                    $valueInner,
7492 99
                    $checkPropertiesInConstructor
7493
                );
7494
            }
7495
        } else {
7496
            if (
7497 1125
                $this->checkPropertyTypes === true
7498
                ||
7499 1125
                $checkPropertiesInConstructor === true
7500
            ) {
7501 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7502
            }
7503
7504
            /** @var TypeCheckInterface[] $properties */
7505 1125
            $properties = $this->properties;
7506
7507
            if (
7508 1125
                $this->checkPropertiesMismatchInConstructor === true
7509
                &&
7510 1125
                \count($data) !== 0
7511
                &&
7512 1125
                \count(\array_diff_key($properties, $data)) > 0
7513
            ) {
7514 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...
7515
            }
7516
7517 1124
            foreach ($data as $key => &$valueInner) {
7518 953
                $this->internalSet(
7519 953
                    $key,
7520 953
                    $valueInner,
7521 953
                    $checkPropertiesInConstructor
7522
                );
7523
            }
7524
        }
7525 1197
    }
7526
7527
    /**
7528
     * sorting keys
7529
     *
7530
     * @param array      $elements
7531
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7532
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7533
     *                              <strong>SORT_NATURAL</strong></p>
7534
     *
7535
     * @return $this
7536
     *               <p>(Mutable) Return this Arrayy object.</p>
7537
     *
7538
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7539
     * @psalm-return static<TKey,T>
7540
     */
7541 18
    protected function sorterKeys(
7542
        array &$elements,
7543
        $direction = \SORT_ASC,
7544
        int $strategy = \SORT_REGULAR
7545
    ): self {
7546 18
        $direction = $this->getDirection($direction);
7547
7548 18
        switch ($direction) {
7549 18
            case 'desc':
7550
            case \SORT_DESC:
7551 6
                \krsort($elements, $strategy);
7552
7553 6
                break;
7554 13
            case 'asc':
7555 13
            case \SORT_ASC:
7556
            default:
7557 13
                \ksort($elements, $strategy);
7558
        }
7559
7560 18
        return $this;
7561
    }
7562
7563
    /**
7564
     * @param array      $elements  <p>Warning: used as reference</p>
7565
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7566
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7567
     *                              <strong>SORT_NATURAL</strong></p>
7568
     * @param bool       $keepKeys
7569
     *
7570
     * @return $this
7571
     *               <p>(Mutable) Return this Arrayy object.</p>
7572
     *
7573
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7574
     * @psalm-return static<TKey,T>
7575
     */
7576 24
    protected function sorting(
7577
        array &$elements,
7578
        $direction = \SORT_ASC,
7579
        int $strategy = \SORT_REGULAR,
7580
        bool $keepKeys = false
7581
    ): self {
7582 24
        $direction = $this->getDirection($direction);
7583
7584 24
        if (!$strategy) {
7585 24
            $strategy = \SORT_REGULAR;
7586
        }
7587
7588 24
        switch ($direction) {
7589 24
            case 'desc':
7590
            case \SORT_DESC:
7591 13
                if ($keepKeys) {
7592 9
                    \arsort($elements, $strategy);
7593
                } else {
7594 4
                    \rsort($elements, $strategy);
7595
                }
7596
7597 13
                break;
7598 11
            case 'asc':
7599 11
            case \SORT_ASC:
7600
            default:
7601 11
                if ($keepKeys) {
7602 4
                    \asort($elements, $strategy);
7603
                } else {
7604 7
                    \sort($elements, $strategy);
7605
                }
7606
        }
7607
7608 24
        return $this;
7609
    }
7610
7611
    /**
7612
     * @param array $array
7613
     *
7614
     * @return array
7615
     *
7616
     * @psalm-mutation-free
7617
     */
7618 25
    private function getArrayRecursiveHelperArrayy(array $array)
7619
    {
7620 25
        if ($array === []) {
7621
            return [];
7622
        }
7623
7624 25
        \array_walk_recursive(
7625 25
            $array,
7626
            /**
7627
             * @param array|self $item
7628
             *
7629
             * @return void
7630
             */
7631
            static function (&$item) {
7632 25
                if ($item instanceof self) {
7633 1
                    $item = $item->getArray();
7634
                }
7635 25
            }
7636
        );
7637
7638 25
        return $array;
7639
    }
7640
7641
    /**
7642
     * @param int|string|null $key
7643
     * @param mixed           $value
7644
     *
7645
     * @return void
7646
     */
7647 112
    private function checkType($key, $value)
7648
    {
7649
        if (
7650 112
            $key !== null
7651
            &&
7652 112
            isset($this->properties[$key]) === false
7653
            &&
7654 112
            $this->checkPropertiesMismatch === true
7655
        ) {
7656
            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...
7657
        }
7658
7659 112
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7660 97
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7661 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7662 24
            $this->properties[$key]->checkType($value);
7663
        }
7664 110
    }
7665
}
7666