Completed
Push — master ( e8310f...cded81 )
by Lars
01:38
created

Arrayy::matchesAny()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 16
loc 16
ccs 8
cts 8
cp 1
crap 4
rs 9.7333
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 1206
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1206
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1204
        parent::__construct([], 0, $iteratorClass);
116
117 1204
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1196
        $this->setIteratorClass($iteratorClass);
120 1196
    }
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 1196
    public function setIteratorClass($iteratorClass)
848
    {
849 1196
        if (\class_exists($iteratorClass)) {
850 1196
            $this->iteratorClass = $iteratorClass;
851
852 1196
            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 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1384
    {
1385 23
        if ($recursive === true) {
1386 18
            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 723
    public static function create(
1629
        $data = [],
1630
        string $iteratorClass = ArrayyIterator::class,
1631
        bool $checkPropertiesInConstructor = true
1632
    ) {
1633 723
        return new static(
1634 723
            $data,
1635 723
            $iteratorClass,
1636 723
            $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 26
    public function createByReference(array &$array = []): self
1695
    {
1696 26
        $array = $this->fallbackForArray($array);
1697
1698 26
        $this->array = &$array;
1699
1700 26
        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 22
    public function first()
2581
    {
2582 22
        $key_first = $this->firstKey();
2583 22
        if ($key_first === null) {
2584 3
            return null;
2585
        }
2586
2587 19
        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 29
    public function firstKey()
2598
    {
2599 29
        $this->generatorToArray();
2600
2601 29
        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 129
                $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 18
                return static::create(
2786 18
                    [],
2787 18
                    $this->iteratorClass,
2788 18
                    false
2789 18
                )->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 501
    public function getArray(
2948
        bool $convertAllArrayyElements = false,
2949
        bool $preserveKeys = true
2950
    ): array {
2951 501
        return $this->toArray(
2952 501
            $convertAllArrayyElements,
2953 501
            $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 1000
    public function getGenerator(): \Generator
3131
    {
3132 1000
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3133 47
            yield from $this->generator;
3134
3135 47
            return;
3136
        }
3137
3138 998
        yield from $this->array;
3139 968
    }
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
     * @param callable $callable
4063
     * @param bool     $useKeyAsSecondParameter
4064
     * @param mixed    ...$arguments
4065
     *
4066
     * @return static
4067
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4068
     *
4069
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
4070
     * @psalm-return static<TKey,T>
4071
     * @psalm-mutation-free
4072
     */
4073 5
    public function map(
4074
        callable $callable,
4075
        bool $useKeyAsSecondParameter = false,
4076
        ...$arguments
4077
    ) {
4078
        /**
4079
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4080
         */
4081 5
        $useArguments = \func_num_args() > 2;
4082
4083 5
        return static::create(
4084
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4085 5
                foreach ($this->getGenerator() as $key => $value) {
4086 4
                    if ($useArguments) {
4087 3
                        if ($useKeyAsSecondParameter) {
4088
                            yield $key => $callable($value, $key, ...$arguments);
4089
                        } else {
4090 3
                            yield $key => $callable($value, ...$arguments);
4091
                        }
4092
                    } else {
4093
                        /** @noinspection NestedPositiveIfStatementsInspection */
4094 4
                        if ($useKeyAsSecondParameter) {
4095
                            yield $key => $callable($value, $key);
4096
                        } else {
4097 4
                            yield $key => $callable($value);
4098
                        }
4099
                    }
4100
                }
4101 5
            },
4102 5
            $this->iteratorClass,
4103 5
            false
4104
        );
4105
    }
4106
4107
    /**
4108
     * Check if all items in current array match a truth test.
4109
     *
4110
     * EXAMPLE: <code>
4111
     * $closure = function ($value, $key) {
4112
     *     return ($value % 2 === 0);
4113
     * };
4114
     * a([2, 4, 8])->matches($closure); // true
4115
     * </code>
4116
     *
4117
     * @param \Closure $closure
4118
     *
4119
     * @return bool
4120
     */
4121 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...
4122
    {
4123 15
        if ($this->count() === 0) {
4124 2
            return false;
4125
        }
4126
4127 13
        foreach ($this->getGenerator() as $key => $value) {
4128 13
            $value = $closure($value, $key);
4129
4130 13
            if ($value === false) {
4131 7
                return false;
4132
            }
4133
        }
4134
4135 7
        return true;
4136
    }
4137
4138
    /**
4139
     * Check if any item in the current array matches a truth test.
4140
     *
4141
     * EXAMPLE: <code>
4142
     * $closure = function ($value, $key) {
4143
     *     return ($value % 2 === 0);
4144
     * };
4145
     * a([1, 4, 7])->matches($closure); // true
4146
     * </code>
4147
     *
4148
     * @param \Closure $closure
4149
     *
4150
     * @return bool
4151
     */
4152 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...
4153
    {
4154 14
        if ($this->count() === 0) {
4155 2
            return false;
4156
        }
4157
4158 12
        foreach ($this->getGenerator() as $key => $value) {
4159 12
            $value = $closure($value, $key);
4160
4161 12
            if ($value === true) {
4162 9
                return true;
4163
            }
4164
        }
4165
4166 4
        return false;
4167
    }
4168
4169
    /**
4170
     * Get the max value from an array.
4171
     *
4172
     * EXAMPLE: <code>
4173
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4174
     * </code>
4175
     *
4176
     * @return false|mixed
4177
     *                     <p>Will return false if there are no values.</p>
4178
     */
4179 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...
4180
    {
4181 10
        if ($this->count() === 0) {
4182 1
            return false;
4183
        }
4184
4185 9
        $max = false;
4186
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4187 9
        foreach ($this->getGeneratorByReference() as &$value) {
4188
            if (
4189 9
                $max === false
4190
                ||
4191 9
                $value > $max
4192
            ) {
4193 9
                $max = $value;
4194
            }
4195
        }
4196
4197 9
        return $max;
4198
    }
4199
4200
    /**
4201
     * Merge the new $array into the current array.
4202
     *
4203
     * - keep key,value from the current array, also if the index is in the new $array
4204
     *
4205
     * EXAMPLE: <code>
4206
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4207
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4208
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4209
     * // ---
4210
     * $array1 = [0 => 'one', 1 => 'foo'];
4211
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4212
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4213
     * </code>
4214
     *
4215
     * @param array $array
4216
     * @param bool  $recursive
4217
     *
4218
     * @return static
4219
     *                <p>(Immutable)</p>
4220
     *
4221
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4222
     * @psalm-return static<int|TKey,T>
4223
     * @psalm-mutation-free
4224
     */
4225 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...
4226
    {
4227 33
        if ($recursive === true) {
4228 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4229 9
            $result = \array_replace_recursive($this->toArray(), $array);
4230
        } else {
4231 24
            $result = \array_replace($this->toArray(), $array);
4232
        }
4233
4234 33
        return static::create(
4235 33
            $result,
4236 33
            $this->iteratorClass,
4237 33
            false
4238
        );
4239
    }
4240
4241
    /**
4242
     * Merge the new $array into the current array.
4243
     *
4244
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4245
     * - create new indexes
4246
     *
4247
     * EXAMPLE: <code>
4248
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4249
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4250
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4251
     * // ---
4252
     * $array1 = [0 => 'one', 1 => 'foo'];
4253
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4254
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4255
     * </code>
4256
     *
4257
     * @param array $array
4258
     * @param bool  $recursive
4259
     *
4260
     * @return static
4261
     *                <p>(Immutable)</p>
4262
     *
4263
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4264
     * @psalm-return static<TKey,T>
4265
     * @psalm-mutation-free
4266
     */
4267 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...
4268
    {
4269 20
        if ($recursive === true) {
4270 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4271 5
            $result = \array_merge_recursive($this->toArray(), $array);
4272
        } else {
4273 15
            $result = \array_merge($this->toArray(), $array);
4274
        }
4275
4276 20
        return static::create(
4277 20
            $result,
4278 20
            $this->iteratorClass,
4279 20
            false
4280
        );
4281
    }
4282
4283
    /**
4284
     * Merge the the current array into the $array.
4285
     *
4286
     * - use key,value from the new $array, also if the index is in the current array
4287
     *
4288
     * EXAMPLE: <code>
4289
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4290
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4291
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4292
     * // ---
4293
     * $array1 = [0 => 'one', 1 => 'foo'];
4294
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4295
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4296
     * </code>
4297
     *
4298
     * @param array $array
4299
     * @param bool  $recursive
4300
     *
4301
     * @return static
4302
     *                <p>(Immutable)</p>
4303
     *
4304
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4305
     * @psalm-return static<TKey,T>
4306
     * @psalm-mutation-free
4307
     */
4308 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...
4309
    {
4310 17
        if ($recursive === true) {
4311 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4312 4
            $result = \array_replace_recursive($array, $this->toArray());
4313
        } else {
4314 13
            $result = \array_replace($array, $this->toArray());
4315
        }
4316
4317 17
        return static::create(
4318 17
            $result,
4319 17
            $this->iteratorClass,
4320 17
            false
4321
        );
4322
    }
4323
4324
    /**
4325
     * Merge the current array into the new $array.
4326
     *
4327
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4328
     * - create new indexes
4329
     *
4330
     * EXAMPLE: <code>
4331
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4332
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4333
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4334
     * // ---
4335
     * $array1 = [0 => 'one', 1 => 'foo'];
4336
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4337
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4338
     * </code>
4339
     *
4340
     * @param array $array
4341
     * @param bool  $recursive
4342
     *
4343
     * @return static
4344
     *                <p>(Immutable)</p>
4345
     *
4346
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4347
     * @psalm-return static<TKey,T>
4348
     * @psalm-mutation-free
4349
     */
4350 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...
4351
    {
4352 21
        if ($recursive === true) {
4353 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4354 7
            $result = \array_merge_recursive($array, $this->toArray());
4355
        } else {
4356 14
            $result = \array_merge($array, $this->toArray());
4357
        }
4358
4359 21
        return static::create(
4360 21
            $result,
4361 21
            $this->iteratorClass,
4362 21
            false
4363
        );
4364
    }
4365
4366
    /**
4367
     * @return ArrayyMeta|static
4368
     */
4369 18
    public static function meta()
4370
    {
4371 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4372
    }
4373
4374
    /**
4375
     * Get the min value from an array.
4376
     *
4377
     * EXAMPLE: <code>
4378
     * a([-9, -8, -7, 1.32])->min(); // -9
4379
     * </code>
4380
     *
4381
     * @return false|mixed
4382
     *                     <p>Will return false if there are no values.</p>
4383
     */
4384 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...
4385
    {
4386 10
        if ($this->count() === 0) {
4387 1
            return false;
4388
        }
4389
4390 9
        $min = false;
4391
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4392 9
        foreach ($this->getGeneratorByReference() as &$value) {
4393
            if (
4394 9
                $min === false
4395
                ||
4396 9
                $value < $min
4397
            ) {
4398 9
                $min = $value;
4399
            }
4400
        }
4401
4402 9
        return $min;
4403
    }
4404
4405
    /**
4406
     * Get the most used value from the array.
4407
     *
4408
     * @return mixed|null
4409
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4410
     * @psalm-mutation-free
4411
     */
4412 3
    public function mostUsedValue()
4413
    {
4414 3
        return $this->countValues()->arsortImmutable()->firstKey();
4415
    }
4416
4417
    /**
4418
     * Get the most used value from the array.
4419
     *
4420
     * @param int|null $number <p>How many values you will take?</p>
4421
     *
4422
     * @return static
4423
     *                <p>(Immutable)</p>
4424
     *
4425
     * @psalm-return static<TKey,T>
4426
     * @psalm-mutation-free
4427
     */
4428 3
    public function mostUsedValues(int $number = null): self
4429
    {
4430 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4431
    }
4432
4433
    /**
4434
     * Move an array element to a new index.
4435
     *
4436
     * EXAMPLE: <code>
4437
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4438
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4439
     * </code>
4440
     *
4441
     * @param int|string $from
4442
     * @param int        $to
4443
     *
4444
     * @return static
4445
     *                <p>(Immutable)</p>
4446
     *
4447
     * @psalm-return static<TKey,T>
4448
     * @psalm-mutation-free
4449
     */
4450 1
    public function moveElement($from, $to): self
4451
    {
4452 1
        $array = $this->toArray();
4453
4454 1
        if ((int) $from === $from) {
4455 1
            $tmp = \array_splice($array, $from, 1);
4456 1
            \array_splice($array, (int) $to, 0, $tmp);
4457 1
            $output = $array;
4458 1
        } elseif ((string) $from === $from) {
4459 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4460 1
            $itemToMove = $array[$from];
4461 1
            if ($indexToMove !== false) {
4462 1
                \array_splice($array, $indexToMove, 1);
4463
            }
4464 1
            $i = 0;
4465 1
            $output = [];
4466 1
            foreach ($array as $key => $item) {
4467 1
                if ($i === $to) {
4468 1
                    $output[$from] = $itemToMove;
4469
                }
4470 1
                $output[$key] = $item;
4471 1
                ++$i;
4472
            }
4473
        } else {
4474
            $output = [];
4475
        }
4476
4477 1
        return static::create(
4478 1
            $output,
4479 1
            $this->iteratorClass,
4480 1
            false
4481
        );
4482
    }
4483
4484
    /**
4485
     * Move an array element to the first place.
4486
     *
4487
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4488
     *       loss the keys of an indexed array.
4489
     *
4490
     * @param int|string $key
4491
     *
4492
     * @return static
4493
     *                <p>(Immutable)</p>
4494
     *
4495
     * @psalm-return static<TKey,T>
4496
     * @psalm-mutation-free
4497
     */
4498 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...
4499
    {
4500 1
        $array = $this->toArray();
4501
4502 1
        if ($this->offsetExists($key)) {
4503 1
            $tmpValue = $this->get($key);
4504 1
            unset($array[$key]);
4505 1
            $array = [$key => $tmpValue] + $array;
4506
        }
4507
4508 1
        return static::create(
4509 1
            $array,
4510 1
            $this->iteratorClass,
4511 1
            false
4512
        );
4513
    }
4514
4515
    /**
4516
     * Move an array element to the last place.
4517
     *
4518
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4519
     *       loss the keys of an indexed array.
4520
     *
4521
     * @param int|string $key
4522
     *
4523
     * @return static
4524
     *                <p>(Immutable)</p>
4525
     *
4526
     * @psalm-return static<TKey,T>
4527
     * @psalm-mutation-free
4528
     */
4529 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...
4530
    {
4531 1
        $array = $this->toArray();
4532
4533 1
        if ($this->offsetExists($key)) {
4534 1
            $tmpValue = $this->get($key);
4535 1
            unset($array[$key]);
4536 1
            $array += [$key => $tmpValue];
4537
        }
4538
4539 1
        return static::create(
4540 1
            $array,
4541 1
            $this->iteratorClass,
4542 1
            false
4543
        );
4544
    }
4545
4546
    /**
4547
     * Moves the internal iterator position to the next element and returns this element.
4548
     *
4549
     * @return false|mixed
4550
     *                     <p>(Mutable) Will return false if there are no values.</p>
4551
     */
4552
    public function next()
4553
    {
4554
        return \next($this->array);
4555
    }
4556
4557
    /**
4558
     * Get the next nth keys and values from the array.
4559
     *
4560
     * @param int $step
4561
     * @param int $offset
4562
     *
4563
     * @return static
4564
     *                <p>(Immutable)</p>
4565
     *
4566
     * @psalm-return static<TKey,T>
4567
     * @psalm-mutation-free
4568
     */
4569 1
    public function nth(int $step, int $offset = 0): self
4570
    {
4571
        $arrayFunction = function () use ($step, $offset): \Generator {
4572 1
            $position = 0;
4573 1
            foreach ($this->getGenerator() as $key => $value) {
4574 1
                if ($position++ % $step !== $offset) {
4575 1
                    continue;
4576
                }
4577
4578 1
                yield $key => $value;
4579
            }
4580 1
        };
4581
4582 1
        return static::create(
4583 1
            $arrayFunction,
4584 1
            $this->iteratorClass,
4585 1
            false
4586
        );
4587
    }
4588
4589
    /**
4590
     * Get a subset of the items from the given array.
4591
     *
4592
     * @param int[]|string[] $keys
4593
     *
4594
     * @return static
4595
     *                <p>(Immutable)</p>
4596
     *
4597
     * @psalm-param array-key[] $keys
4598
     * @psalm-return static<TKey,T>
4599
     * @psalm-mutation-free
4600
     */
4601 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...
4602
    {
4603 1
        $keys = \array_flip($keys);
4604
4605
        $generator = function () use ($keys): \Generator {
4606 1
            foreach ($this->getGenerator() as $key => $value) {
4607 1
                if (isset($keys[$key])) {
4608 1
                    yield $key => $value;
4609
                }
4610
            }
4611 1
        };
4612
4613 1
        return static::create(
4614 1
            $generator,
4615 1
            $this->iteratorClass,
4616 1
            false
4617
        );
4618
    }
4619
4620
    /**
4621
     * Pad array to the specified size with a given value.
4622
     *
4623
     * @param int   $size  <p>Size of the result array.</p>
4624
     * @param mixed $value <p>Empty value by default.</p>
4625
     *
4626
     * @return static
4627
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4628
     *
4629
     * @psalm-return static<TKey,T>
4630
     * @psalm-mutation-free
4631
     */
4632 5
    public function pad(int $size, $value): self
4633
    {
4634 5
        return static::create(
4635 5
            \array_pad($this->toArray(), $size, $value),
4636 5
            $this->iteratorClass,
4637 5
            false
4638
        );
4639
    }
4640
4641
    /**
4642
     * Partitions this array in two array according to a predicate.
4643
     * Keys are preserved in the resulting array.
4644
     *
4645
     * @param \Closure $closure
4646
     *                          <p>The predicate on which to partition.</p>
4647
     *
4648
     * @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...
4649
     *                    <p>An array with two elements. The first element contains the array
4650
     *                    of elements where the predicate returned TRUE, the second element
4651
     *                    contains the array of elements where the predicate returned FALSE.</p>
4652
     *
4653
     * @psalm-return array<int, static<TKey,T>>
4654
     */
4655 1
    public function partition(\Closure $closure): array
4656
    {
4657
        // init
4658 1
        $matches = [];
4659 1
        $noMatches = [];
4660
4661 1
        foreach ($this->getGenerator() as $key => $value) {
4662 1
            if ($closure($value, $key)) {
4663 1
                $matches[$key] = $value;
4664
            } else {
4665 1
                $noMatches[$key] = $value;
4666
            }
4667
        }
4668
4669 1
        return [self::create($matches), self::create($noMatches)];
4670
    }
4671
4672
    /**
4673
     * Pop a specified value off the end of the current array.
4674
     *
4675
     * @return mixed|null
4676
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4677
     */
4678 5
    public function pop()
4679
    {
4680 5
        $this->generatorToArray();
4681
4682 5
        return \array_pop($this->array);
4683
    }
4684
4685
    /**
4686
     * Prepend a (key) + value to the current array.
4687
     *
4688
     * EXAMPLE: <code>
4689
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4690
     * </code>
4691
     *
4692
     * @param mixed $value
4693
     * @param mixed $key
4694
     *
4695
     * @return $this
4696
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4697
     *
4698
     * @psalm-return static<TKey,T>
4699
     */
4700 11
    public function prepend($value, $key = null)
4701
    {
4702 11
        $this->generatorToArray();
4703
4704 11
        if ($this->properties !== []) {
4705 3
            $this->checkType($key, $value);
4706
        }
4707
4708 9
        if ($key === null) {
4709 8
            \array_unshift($this->array, $value);
4710
        } else {
4711 2
            $this->array = [$key => $value] + $this->array;
4712
        }
4713
4714 9
        return $this;
4715
    }
4716
4717
    /**
4718
     * Prepend a (key) + value to the current array.
4719
     *
4720
     * EXAMPLE: <code>
4721
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4722
     * </code>
4723
     *
4724
     * @param mixed $value
4725
     * @param mixed $key
4726
     *
4727
     * @return $this
4728
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4729
     *
4730
     * @psalm-return static<TKey,T>
4731
     * @psalm-mutation-free
4732
     */
4733 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...
4734
    {
4735
        $generator = function () use ($key, $value): \Generator {
4736 1
            if ($this->properties !== []) {
4737
                $this->checkType($key, $value);
4738
            }
4739
4740 1
            if ($key !== null) {
4741
                yield $key => $value;
4742
            } else {
4743 1
                yield $value;
4744
            }
4745
4746
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4747 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4748 1
                yield $keyOld => $itemOld;
4749
            }
4750 1
        };
4751
4752 1
        return static::create(
4753 1
            $generator,
4754 1
            $this->iteratorClass,
4755 1
            false
4756
        );
4757
    }
4758
4759
    /**
4760
     * Add a suffix to each key.
4761
     *
4762
     * @param mixed $suffix
4763
     *
4764
     * @return static
4765
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4766
     *
4767
     * @psalm-return static<TKey,T>
4768
     * @psalm-mutation-free
4769
     */
4770 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...
4771
    {
4772
        // init
4773 10
        $result = [];
4774
4775 10
        foreach ($this->getGenerator() as $key => $item) {
4776 9
            if ($item instanceof self) {
4777
                $result[$key] = $item->prependToEachKey($suffix);
4778 9
            } elseif (\is_array($item)) {
4779
                $result[$key] = self::create(
4780
                    $item,
4781
                    $this->iteratorClass,
4782
                    false
4783
                )->prependToEachKey($suffix)
4784
                    ->toArray();
4785
            } else {
4786 9
                $result[$key . $suffix] = $item;
4787
            }
4788
        }
4789
4790 10
        return self::create(
4791 10
            $result,
4792 10
            $this->iteratorClass,
4793 10
            false
4794
        );
4795
    }
4796
4797
    /**
4798
     * Add a suffix to each value.
4799
     *
4800
     * @param mixed $suffix
4801
     *
4802
     * @return static
4803
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4804
     *
4805
     * @psalm-return static<TKey,T>
4806
     * @psalm-mutation-free
4807
     */
4808 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...
4809
    {
4810
        // init
4811 10
        $result = [];
4812
4813 10
        foreach ($this->getGenerator() as $key => $item) {
4814 9
            if ($item instanceof self) {
4815
                $result[$key] = $item->prependToEachValue($suffix);
4816 9
            } elseif (\is_array($item)) {
4817
                $result[$key] = self::create(
4818
                    $item,
4819
                    $this->iteratorClass,
4820
                    false
4821
                )->prependToEachValue($suffix)
4822
                    ->toArray();
4823 9
            } elseif (\is_object($item) === true) {
4824 1
                $result[$key] = $item;
4825
            } else {
4826 8
                $result[$key] = $item . $suffix;
4827
            }
4828
        }
4829
4830 10
        return self::create(
4831 10
            $result,
4832 10
            $this->iteratorClass,
4833 10
            false
4834
        );
4835
    }
4836
4837
    /**
4838
     * Return the value of a given key and
4839
     * delete the key.
4840
     *
4841
     * @param int|int[]|string|string[]|null $keyOrKeys
4842
     * @param mixed                          $fallback
4843
     *
4844
     * @return mixed
4845
     */
4846 5
    public function pull($keyOrKeys = null, $fallback = null)
4847
    {
4848 5
        if ($keyOrKeys === null) {
4849 1
            $array = $this->toArray();
4850 1
            $this->clear();
4851
4852 1
            return $array;
4853
        }
4854
4855 4
        if (\is_array($keyOrKeys)) {
4856 1
            $valueOrValues = [];
4857 1
            foreach ($keyOrKeys as $key) {
4858 1
                $valueOrValues[] = $this->get($key, $fallback);
4859 1
                $this->offsetUnset($key);
4860
            }
4861
        } else {
4862 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4863 4
            $this->offsetUnset($keyOrKeys);
4864
        }
4865
4866 4
        return $valueOrValues;
4867
    }
4868
4869
    /**
4870
     * Push one or more values onto the end of array at once.
4871
     *
4872
     * @param array ...$args
4873
     *
4874
     * @return $this
4875
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4876
     *
4877
     * @noinspection ReturnTypeCanBeDeclaredInspection
4878
     *
4879
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4880
     * @psalm-return static<TKey,T>
4881
     */
4882 7
    public function push(...$args)
4883
    {
4884 7
        $this->generatorToArray();
4885
4886
        if (
4887 7
            $this->checkPropertyTypes
4888
            &&
4889 7
            $this->properties !== []
4890
        ) {
4891 1
            foreach ($args as $key => $value) {
4892 1
                $this->checkType($key, $value);
4893
            }
4894
        }
4895
4896 7
        \array_push($this->array, ...$args);
4897
4898 7
        return $this;
4899
    }
4900
4901
    /**
4902
     * Get a random value from the current array.
4903
     *
4904
     * EXAMPLE: <code>
4905
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
4906
     * </code>
4907
     *
4908
     * @param int|null $number <p>How many values you will take?</p>
4909
     *
4910
     * @return static
4911
     *                <p>(Immutable)</p>
4912
     *
4913
     * @psalm-return static<int|array-key,T>
4914
     */
4915 19
    public function randomImmutable(int $number = null): self
4916
    {
4917 19
        $this->generatorToArray();
4918
4919 19
        if ($this->count() === 0) {
4920 1
            return static::create(
4921 1
                [],
4922 1
                $this->iteratorClass,
4923 1
                false
4924
            );
4925
        }
4926
4927 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...
4928
            /** @noinspection NonSecureArrayRandUsageInspection */
4929 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4930
4931 13
            return static::create(
4932 13
                $arrayRandValue,
4933 13
                $this->iteratorClass,
4934 13
                false
4935
            );
4936
        }
4937
4938 6
        $arrayTmp = $this->array;
4939
        /** @noinspection NonSecureShuffleUsageInspection */
4940 6
        \shuffle($arrayTmp);
4941
4942 6
        return static::create(
4943 6
            $arrayTmp,
4944 6
            $this->iteratorClass,
4945 6
            false
4946 6
        )->firstsImmutable($number);
4947
    }
4948
4949
    /**
4950
     * Pick a random key/index from the keys of this array.
4951
     *
4952
     * EXAMPLE: <code>
4953
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
4954
     * $arrayy->randomKey(); // e.g. 2
4955
     * </code>
4956
     *
4957
     * @throws \RangeException If array is empty
4958
     *
4959
     * @return mixed
4960
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4961
     */
4962 4
    public function randomKey()
4963
    {
4964 4
        $result = $this->randomKeys(1);
4965
4966 4
        if (!isset($result[0])) {
4967
            $result[0] = null;
4968
        }
4969
4970 4
        return $result[0];
4971
    }
4972
4973
    /**
4974
     * Pick a given number of random keys/indexes out of this array.
4975
     *
4976
     * EXAMPLE: <code>
4977
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
4978
     * </code>
4979
     *
4980
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4981
     *
4982
     * @throws \RangeException If array is empty
4983
     *
4984
     * @return static
4985
     *                <p>(Immutable)</p>
4986
     *
4987
     * @psalm-return static<TKey,T>
4988
     */
4989 13
    public function randomKeys(int $number): self
4990
    {
4991 13
        $this->generatorToArray();
4992
4993 13
        $count = $this->count();
4994
4995
        if (
4996 13
            $number === 0
4997
            ||
4998 13
            $number > $count
4999
        ) {
5000 2
            throw new \RangeException(
5001 2
                \sprintf(
5002 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5003 2
                    $number,
5004 2
                    $count
5005
                )
5006
            );
5007
        }
5008
5009 11
        $result = (array) \array_rand($this->array, $number);
5010
5011 11
        return static::create(
5012 11
            $result,
5013 11
            $this->iteratorClass,
5014 11
            false
5015
        );
5016
    }
5017
5018
    /**
5019
     * Get a random value from the current array.
5020
     *
5021
     * EXAMPLE: <code>
5022
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5023
     * </code>
5024
     *
5025
     * @param int|null $number <p>How many values you will take?</p>
5026
     *
5027
     * @return $this
5028
     *               <p>(Mutable) Return this Arrayy object.</p>
5029
     *
5030
     * @psalm-return static<TKey,T>
5031
     */
5032 17
    public function randomMutable(int $number = null): self
5033
    {
5034 17
        $this->generatorToArray();
5035
5036 17
        if ($this->count() === 0) {
5037
            return static::create(
5038
                [],
5039
                $this->iteratorClass,
5040
                false
5041
            );
5042
        }
5043
5044 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...
5045
            /** @noinspection NonSecureArrayRandUsageInspection */
5046 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5047 7
            $this->array = $arrayRandValue;
5048
5049 7
            return $this;
5050
        }
5051
5052
        /** @noinspection NonSecureShuffleUsageInspection */
5053 11
        \shuffle($this->array);
5054
5055 11
        return $this->firstsMutable($number);
5056
    }
5057
5058
    /**
5059
     * Pick a random value from the values of this array.
5060
     *
5061
     * EXAMPLE: <code>
5062
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5063
     * </code>
5064
     *
5065
     * @return mixed
5066
     *               <p>Get a random value or null if there wasn't a value.</p>
5067
     */
5068 4
    public function randomValue()
5069
    {
5070 4
        $result = $this->randomImmutable();
5071
5072 4
        if (!isset($result[0])) {
5073
            $result[0] = null;
5074
        }
5075
5076 4
        return $result[0];
5077
    }
5078
5079
    /**
5080
     * Pick a given number of random values out of this array.
5081
     *
5082
     * EXAMPLE: <code>
5083
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5084
     * </code>
5085
     *
5086
     * @param int $number
5087
     *
5088
     * @return static
5089
     *                <p>(Mutable)</p>
5090
     *
5091
     * @psalm-return static<TKey,T>
5092
     */
5093 7
    public function randomValues(int $number): self
5094
    {
5095 7
        return $this->randomMutable($number);
5096
    }
5097
5098
    /**
5099
     * Get a random value from an array, with the ability to skew the results.
5100
     *
5101
     * EXAMPLE: <code>
5102
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5103
     * </code>
5104
     *
5105
     * @param array    $array
5106
     * @param int|null $number <p>How many values you will take?</p>
5107
     *
5108
     * @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...
5109
     *                           <p>(Immutable)</p>
5110
     *
5111
     * @psalm-param  array<mixed,mixed> $array
5112
     * @psalm-return static<int|array-key,T>
5113
     */
5114 9
    public function randomWeighted(array $array, int $number = null): self
5115
    {
5116
        // init
5117 9
        $options = [];
5118
5119 9
        foreach ($array as $option => $weight) {
5120 9
            if ($this->searchIndex($option) !== false) {
5121 2
                for ($i = 0; $i < $weight; ++$i) {
5122 1
                    $options[] = $option;
5123
                }
5124
            }
5125
        }
5126
5127 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5128
    }
5129
5130
    /**
5131
     * Reduce the current array via callable e.g. anonymous-function and return the end result.
5132
     *
5133
     * EXAMPLE: <code>
5134
     * function myReducer($resultArray, $value) {
5135
     *     if ($value == 'foo') {
5136
     *         $resultArray[] = $value;
5137
     *     }
5138
     *     return $resultArray;
5139
     * };
5140
     * a(['foo', 'bar'])->reduce('myReducer'); // Arrayy['foo']
5141
     * </code>
5142
     *
5143
     * @param callable $callable
5144
     * @param mixed    $initial
5145
     *
5146
     * @return static
5147
     *                <p>(Immutable)</p>
5148
     *
5149
     * @psalm-return static<TKey,T>
5150
     * @psalm-mutation-free
5151
     */
5152 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...
5153
    {
5154 18
        foreach ($this->getGenerator() as $key => $value) {
5155 17
            $initial = $callable($initial, $value, $key);
5156
        }
5157
5158 18
        return static::create(
5159 18
            $initial,
5160 18
            $this->iteratorClass,
5161 18
            false
5162
        );
5163
    }
5164
5165
    /**
5166
     * @param bool $unique
5167
     *
5168
     * @return static
5169
     *                <p>(Immutable)</p>
5170
     *
5171
     * @psalm-return static<TKey,T>
5172
     * @psalm-mutation-free
5173
     */
5174 14
    public function reduce_dimension(bool $unique = true): self
5175
    {
5176
        // init
5177 14
        $result = [];
5178
5179 14
        foreach ($this->getGenerator() as $val) {
5180 12
            if (\is_array($val)) {
5181 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5182
            } else {
5183 12
                $result[] = [$val];
5184
            }
5185
        }
5186
5187 14
        $result = $result === [] ? [] : \array_merge(...$result);
5188
5189 14
        $resultArrayy = new static($result);
5190
5191
        /**
5192
         * @psalm-suppress ImpureMethodCall - object is already re-created
5193
         * @psalm-suppress InvalidReturnStatement - why?
5194
         */
5195 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5196
    }
5197
5198
    /**
5199
     * Create a numerically re-indexed Arrayy object.
5200
     *
5201
     * EXAMPLE: <code>
5202
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5203
     * </code>
5204
     *
5205
     * @return $this
5206
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5207
     *
5208
     * @psalm-return static<TKey,T>
5209
     */
5210 9
    public function reindex(): self
5211
    {
5212 9
        $this->generatorToArray(false);
5213
5214 9
        $this->array = \array_values($this->array);
5215
5216 9
        return $this;
5217
    }
5218
5219
    /**
5220
     * Return all items that fail the truth test.
5221
     *
5222
     * EXAMPLE: <code>
5223
     * $closure = function ($value) {
5224
     *     return $value % 2 !== 0;
5225
     * }
5226
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5227
     * </code>
5228
     *
5229
     * @param \Closure $closure
5230
     *
5231
     * @return static
5232
     *                <p>(Immutable)</p>
5233
     *
5234
     * @psalm-return static<TKey,T>
5235
     * @psalm-mutation-free
5236
     */
5237 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...
5238
    {
5239
        // init
5240 1
        $filtered = [];
5241
5242 1
        foreach ($this->getGenerator() as $key => $value) {
5243 1
            if (!$closure($value, $key)) {
5244 1
                $filtered[$key] = $value;
5245
            }
5246
        }
5247
5248 1
        return static::create(
5249 1
            $filtered,
5250 1
            $this->iteratorClass,
5251 1
            false
5252
        );
5253
    }
5254
5255
    /**
5256
     * Remove a value from the current array (optional using dot-notation).
5257
     *
5258
     * EXAMPLE: <code>
5259
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5260
     * </code>
5261
     *
5262
     * @param mixed $key
5263
     *
5264
     * @return static
5265
     *                <p>(Mutable)</p>
5266
     *
5267
     * @psalm-param  TKey $key
5268
     * @psalm-return static<TKey,T>
5269
     */
5270 22
    public function remove($key)
5271
    {
5272
        // recursive call
5273 22
        if (\is_array($key)) {
5274 1
            foreach ($key as $k) {
5275 1
                $this->internalRemove($k);
5276
            }
5277
5278 1
            return static::create(
5279 1
                $this->toArray(),
5280 1
                $this->iteratorClass,
5281 1
                false
5282
            );
5283
        }
5284
5285 21
        $this->internalRemove($key);
5286
5287 21
        return static::create(
5288 21
            $this->toArray(),
5289 21
            $this->iteratorClass,
5290 21
            false
5291
        );
5292
    }
5293
5294
    /**
5295
     * alias: for "Arrayy->removeValue()"
5296
     *
5297
     * @param mixed $element
5298
     *
5299
     * @return static
5300
     *                <p>(Immutable)</p>
5301
     *
5302
     * @psalm-param  T $element
5303
     * @psalm-return static<TKey,T>
5304
     * @psalm-mutation-free
5305
     */
5306 8
    public function removeElement($element)
5307
    {
5308 8
        return $this->removeValue($element);
5309
    }
5310
5311
    /**
5312
     * Remove the first value from the current array.
5313
     *
5314
     * EXAMPLE: <code>
5315
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5316
     * </code>
5317
     *
5318
     * @return static
5319
     *                <p>(Immutable)</p>
5320
     *
5321
     * @psalm-return static<TKey,T>
5322
     * @psalm-mutation-free
5323
     */
5324 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...
5325
    {
5326 7
        $tmpArray = $this->toArray();
5327
5328 7
        \array_shift($tmpArray);
5329
5330 7
        return static::create(
5331 7
            $tmpArray,
5332 7
            $this->iteratorClass,
5333 7
            false
5334
        );
5335
    }
5336
5337
    /**
5338
     * Remove the last value from the current array.
5339
     *
5340
     * EXAMPLE: <code>
5341
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5342
     * </code>
5343
     *
5344
     * @return static
5345
     *                <p>(Immutable)</p>
5346
     *
5347
     * @psalm-return static<TKey,T>
5348
     * @psalm-mutation-free
5349
     */
5350 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...
5351
    {
5352 7
        $tmpArray = $this->toArray();
5353
5354 7
        \array_pop($tmpArray);
5355
5356 7
        return static::create(
5357 7
            $tmpArray,
5358 7
            $this->iteratorClass,
5359 7
            false
5360
        );
5361
    }
5362
5363
    /**
5364
     * Removes a particular value from an array (numeric or associative).
5365
     *
5366
     * EXAMPLE: <code>
5367
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5368
     * </code>
5369
     *
5370
     * @param mixed $value
5371
     *
5372
     * @return static
5373
     *                <p>(Immutable)</p>
5374
     *
5375
     * @psalm-param  T $value
5376
     * @psalm-return static<TKey,T>
5377
     * @psalm-mutation-free
5378
     */
5379 8
    public function removeValue($value): self
5380
    {
5381 8
        $this->generatorToArray();
5382
5383
        // init
5384 8
        $isSequentialArray = $this->isSequential();
5385
5386 8
        foreach ($this->array as $key => $item) {
5387 7
            if ($item === $value) {
5388 7
                unset($this->array[$key]);
5389
            }
5390
        }
5391
5392 8
        if ($isSequentialArray) {
5393 6
            $this->array = \array_values($this->array);
5394
        }
5395
5396 8
        return static::create(
5397 8
            $this->array,
5398 8
            $this->iteratorClass,
5399 8
            false
5400
        );
5401
    }
5402
5403
    /**
5404
     * Generate array of repeated arrays.
5405
     *
5406
     * @param int $times <p>How many times has to be repeated.</p>
5407
     *
5408
     * @return static
5409
     *                <p>(Immutable)</p>
5410
     *
5411
     * @psalm-return static<TKey,T>
5412
     * @psalm-mutation-free
5413
     */
5414 1
    public function repeat($times): self
5415
    {
5416 1
        if ($times === 0) {
5417 1
            return static::create([], $this->iteratorClass);
5418
        }
5419
5420 1
        return static::create(
5421 1
            \array_fill(0, (int) $times, $this->toArray()),
5422 1
            $this->iteratorClass,
5423 1
            false
5424
        );
5425
    }
5426
5427
    /**
5428
     * Replace a key with a new key/value pair.
5429
     *
5430
     * EXAMPLE: <code>
5431
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5432
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5433
     * </code>
5434
     *
5435
     * @param mixed $oldKey
5436
     * @param mixed $newKey
5437
     * @param mixed $newValue
5438
     *
5439
     * @return static
5440
     *                <p>(Immutable)</p>
5441
     *
5442
     * @psalm-return static<TKey,T>
5443
     * @psalm-mutation-free
5444
     */
5445 5
    public function replace($oldKey, $newKey, $newValue): self
5446
    {
5447 5
        $that = clone $this;
5448
5449
        /**
5450
         * @psalm-suppress ImpureMethodCall - object is already cloned
5451
         */
5452 5
        return $that->remove($oldKey)
5453 5
            ->set($newKey, $newValue);
5454
    }
5455
5456
    /**
5457
     * Create an array using the current array as values and the other array as keys.
5458
     *
5459
     * EXAMPLE: <code>
5460
     * $firstArray = [
5461
     *     1 => 'one',
5462
     *     2 => 'two',
5463
     *     3 => 'three',
5464
     * ];
5465
     * $secondArray = [
5466
     *     'one' => 1,
5467
     *     1     => 'one',
5468
     *     2     => 2,
5469
     * ];
5470
     * $arrayy = a($firstArray);
5471
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5472
     * </code>
5473
     *
5474
     * @param array $keys <p>An array of keys.</p>
5475
     *
5476
     * @return static
5477
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5478
     *
5479
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5480
     * @psalm-return static<TKey,T>
5481
     * @psalm-mutation-free
5482
     */
5483 2
    public function replaceAllKeys(array $keys): self
5484
    {
5485 2
        return static::create(
5486 2
            \array_combine($keys, $this->toArray()),
5487 2
            $this->iteratorClass,
5488 2
            false
5489
        );
5490
    }
5491
5492
    /**
5493
     * Create an array using the current array as keys and the other array as values.
5494
     *
5495
     * EXAMPLE: <code>
5496
     * $firstArray = [
5497
     *     1 => 'one',
5498
     *     2 => 'two',
5499
     *     3 => 'three',
5500
     * ];
5501
     * $secondArray = [
5502
     *     'one' => 1,
5503
     *     1     => 'one',
5504
     *     2     => 2,
5505
     * ];
5506
     * $arrayy = a($firstArray);
5507
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5508
     * </code>
5509
     *
5510
     * @param array $array <p>An array of values.</p>
5511
     *
5512
     * @return static
5513
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5514
     *
5515
     * @psalm-param  array<mixed,T> $array
5516
     * @psalm-return static<TKey,T>
5517
     * @psalm-mutation-free
5518
     */
5519 2
    public function replaceAllValues(array $array): self
5520
    {
5521 2
        return static::create(
5522 2
            \array_combine($this->array, $array),
5523 2
            $this->iteratorClass,
5524 2
            false
5525
        );
5526
    }
5527
5528
    /**
5529
     * Replace the keys in an array with another set.
5530
     *
5531
     * EXAMPLE: <code>
5532
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5533
     * </code>
5534
     *
5535
     * @param array $keys <p>An array of keys matching the array's size</p>
5536
     *
5537
     * @return static
5538
     *                <p>(Immutable)</p>
5539
     *
5540
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5541
     * @psalm-return static<TKey,T>
5542
     * @psalm-mutation-free
5543
     */
5544 1
    public function replaceKeys(array $keys): self
5545
    {
5546 1
        $values = \array_values($this->toArray());
5547 1
        $result = \array_combine($keys, $values);
5548
5549 1
        return static::create(
5550 1
            $result,
5551 1
            $this->iteratorClass,
5552 1
            false
5553
        );
5554
    }
5555
5556
    /**
5557
     * Replace the first matched value in an array.
5558
     *
5559
     * EXAMPLE: <code>
5560
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5561
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5562
     * </code>
5563
     *
5564
     * @param mixed $search      <p>The value to replace.</p>
5565
     * @param mixed $replacement <p>The value to replace.</p>
5566
     *
5567
     * @return static
5568
     *                <p>(Immutable)</p>
5569
     *
5570
     * @psalm-return static<TKey,T>
5571
     * @psalm-mutation-free
5572
     */
5573 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...
5574
    {
5575 3
        $array = $this->toArray();
5576 3
        $key = \array_search($search, $array, true);
5577
5578 3
        if ($key !== false) {
5579 3
            $array[$key] = $replacement;
5580
        }
5581
5582 3
        return static::create(
5583 3
            $array,
5584 3
            $this->iteratorClass,
5585 3
            false
5586
        );
5587
    }
5588
5589
    /**
5590
     * Replace values in the current array.
5591
     *
5592
     * EXAMPLE: <code>
5593
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5594
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5595
     * </code>
5596
     *
5597
     * @param mixed $search      <p>The value to replace.</p>
5598
     * @param mixed $replacement <p>What to replace it with.</p>
5599
     *
5600
     * @return static
5601
     *                <p>(Immutable)</p>
5602
     *
5603
     * @psalm-return static<TKey,T>
5604
     * @psalm-mutation-free
5605
     */
5606 1
    public function replaceValues($search, $replacement = ''): self
5607
    {
5608
        /**
5609
         * @psalm-suppress MissingClosureReturnType
5610
         * @psalm-suppress MissingClosureParamType
5611
         */
5612 1
        return $this->each(
5613
            static function ($value) use ($search, $replacement) {
5614 1
                return \str_replace($search, $replacement, $value);
5615 1
            }
5616
        );
5617
    }
5618
5619
    /**
5620
     * Get the last elements from index $from until the end of this array.
5621
     *
5622
     * EXAMPLE: <code>
5623
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5624
     * </code>
5625
     *
5626
     * @param int $from
5627
     *
5628
     * @return static
5629
     *                <p>(Immutable)</p>
5630
     *
5631
     * @psalm-return static<TKey,T>
5632
     * @psalm-mutation-free
5633
     */
5634 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...
5635
    {
5636 15
        $tmpArray = $this->toArray();
5637
5638 15
        return static::create(
5639 15
            \array_splice($tmpArray, $from),
5640 15
            $this->iteratorClass,
5641 15
            false
5642
        );
5643
    }
5644
5645
    /**
5646
     * Return the array in the reverse order.
5647
     *
5648
     * EXAMPLE: <code>
5649
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5650
     * </code>
5651
     *
5652
     * @return $this
5653
     *               <p>(Mutable) Return this Arrayy object.</p>
5654
     *
5655
     * @psalm-return static<TKey,T>
5656
     */
5657 9
    public function reverse(): self
5658
    {
5659 9
        $this->generatorToArray();
5660
5661 9
        $this->array = \array_reverse($this->array);
5662
5663 9
        return $this;
5664
    }
5665
5666
    /**
5667
     * Sort an array in reverse order.
5668
     *
5669
     * @param int $sort_flags [optional] <p>
5670
     *                        You may modify the behavior of the sort using the optional
5671
     *                        parameter sort_flags, for details
5672
     *                        see sort.
5673
     *                        </p>
5674
     *
5675
     * @return $this
5676
     *               <p>(Mutable) Return this Arrayy object.</p>
5677
     *
5678
     * @psalm-return static<TKey,T>
5679
     */
5680 4
    public function rsort(int $sort_flags = 0): self
5681
    {
5682 4
        $this->generatorToArray();
5683
5684 4
        \rsort($this->array, $sort_flags);
5685
5686 4
        return $this;
5687
    }
5688
5689
    /**
5690
     * Sort an array in reverse order.
5691
     *
5692
     * @param int $sort_flags [optional] <p>
5693
     *                        You may modify the behavior of the sort using the optional
5694
     *                        parameter sort_flags, for details
5695
     *                        see sort.
5696
     *                        </p>
5697
     *
5698
     * @return $this
5699
     *               <p>(Immutable) Return this Arrayy object.</p>
5700
     *
5701
     * @psalm-return static<TKey,T>
5702
     * @psalm-mutation-free
5703
     */
5704 4
    public function rsortImmutable(int $sort_flags = 0): self
5705
    {
5706 4
        $that = clone $this;
5707
5708
        /**
5709
         * @psalm-suppress ImpureMethodCall - object is already cloned
5710
         */
5711 4
        $that->rsort($sort_flags);
5712
5713 4
        return $that;
5714
    }
5715
5716
    /**
5717
     * Search for the first index of the current array via $value.
5718
     *
5719
     * EXAMPLE: <code>
5720
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5721
     * </code>
5722
     *
5723
     * @param mixed $value
5724
     *
5725
     * @return false|float|int|string
5726
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5727
     * @psalm-mutation-free
5728
     */
5729 21
    public function searchIndex($value)
5730
    {
5731 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5732 20
            if ($value === $valueFromArray) {
5733 10
                return $keyFromArray;
5734
            }
5735
        }
5736
5737 11
        return false;
5738
    }
5739
5740
    /**
5741
     * Search for the value of the current array via $index.
5742
     *
5743
     * EXAMPLE: <code>
5744
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5745
     * </code>
5746
     *
5747
     * @param mixed $index
5748
     *
5749
     * @return static
5750
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5751
     *
5752
     * @psalm-return static<TKey,T>
5753
     * @psalm-mutation-free
5754
     */
5755 9
    public function searchValue($index): self
5756
    {
5757 9
        $this->generatorToArray();
5758
5759
        // init
5760 9
        $return = [];
5761
5762 9
        if ($this->array === []) {
5763
            return static::create(
5764
                [],
5765
                $this->iteratorClass,
5766
                false
5767
            );
5768
        }
5769
5770
        // php cast "bool"-index into "int"-index
5771 9
        if ((bool) $index === $index) {
5772 1
            $index = (int) $index;
5773
        }
5774
5775 9
        if ($this->offsetExists($index)) {
5776 7
            $return = [$this->array[$index]];
5777
        }
5778
5779 9
        return static::create(
5780 9
            $return,
5781 9
            $this->iteratorClass,
5782 9
            false
5783
        );
5784
    }
5785
5786
    /**
5787
     * Set a value for the current array (optional using dot-notation).
5788
     *
5789
     * EXAMPLE: <code>
5790
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5791
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5792
     * </code>
5793
     *
5794
     * @param string $key   <p>The key to set.</p>
5795
     * @param mixed  $value <p>Its value.</p>
5796
     *
5797
     * @return $this
5798
     *               <p>(Mutable) Return this Arrayy object.</p>
5799
     *
5800
     * @psalm-param  TKey $key
5801
     * @psalm-param  T $value
5802
     * @psalm-return static<TKey,T>
5803
     */
5804 28
    public function set($key, $value): self
5805
    {
5806 28
        $this->internalSet($key, $value);
5807
5808 27
        return $this;
5809
    }
5810
5811
    /**
5812
     * Get a value from a array and set it if it was not.
5813
     *
5814
     * WARNING: this method only set the value, if the $key is not already set
5815
     *
5816
     * EXAMPLE: <code>
5817
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5818
     * $arrayy->setAndGet(1, 4); // 1
5819
     * $arrayy->setAndGet(0, 4); // 4
5820
     * </code>
5821
     *
5822
     * @param mixed $key      <p>The key</p>
5823
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5824
     *
5825
     * @return mixed
5826
     *               <p>(Mutable)</p>
5827
     */
5828 11
    public function setAndGet($key, $fallback = null)
5829
    {
5830 11
        $this->generatorToArray();
5831
5832
        // If the key doesn't exist, set it.
5833 11
        if (!$this->has($key)) {
5834 4
            $this->array = $this->set($key, $fallback)->toArray();
5835
        }
5836
5837 11
        return $this->get($key);
5838
    }
5839
5840
    /**
5841
     * Shifts a specified value off the beginning of array.
5842
     *
5843
     * @return mixed
5844
     *               <p>(Mutable) A shifted element from the current array.</p>
5845
     */
5846 5
    public function shift()
5847
    {
5848 5
        $this->generatorToArray();
5849
5850 5
        return \array_shift($this->array);
5851
    }
5852
5853
    /**
5854
     * Shuffle the current array.
5855
     *
5856
     * EXAMPLE: <code>
5857
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5858
     * </code>
5859
     *
5860
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5861
     * @param array $array  [optional]
5862
     *
5863
     * @return static
5864
     *                <p>(Immutable)</p>
5865
     *
5866
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5867
     * @psalm-return static<TKey,T>
5868
     *
5869
     * @noinspection BadExceptionsProcessingInspection
5870
     * @noinspection NonSecureShuffleUsageInspection
5871
     */
5872 2
    public function shuffle(bool $secure = false, array $array = null): self
5873
    {
5874 2
        if ($array === null) {
5875 2
            $array = $this->toArray(false);
5876
        }
5877
5878 2
        if ($secure !== true) {
5879 2
            \shuffle($array);
5880
        } else {
5881 1
            $size = \count($array, \COUNT_NORMAL);
5882 1
            $keys = \array_keys($array);
5883 1
            for ($i = $size - 1; $i > 0; --$i) {
5884
                try {
5885 1
                    $r = \random_int(0, $i);
5886
                } catch (\Exception $e) {
5887
                    $r = \mt_rand(0, $i);
5888
                }
5889 1
                if ($r !== $i) {
5890 1
                    $temp = $array[$keys[$r]];
5891 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5892 1
                    $array[$keys[$i]] = $temp;
5893
                }
5894
            }
5895
        }
5896
5897 2
        foreach ($array as $key => $value) {
5898
            // check if recursive is needed
5899 2
            if (\is_array($value)) {
5900
                $array[$key] = $this->shuffle($secure, $value);
5901
            }
5902
        }
5903
5904 2
        return static::create(
5905 2
            $array,
5906 2
            $this->iteratorClass,
5907 2
            false
5908
        );
5909
    }
5910
5911
    /**
5912
     * Count the values from the current array.
5913
     *
5914
     * alias: for "Arrayy->count()"
5915
     *
5916
     * @param int $mode
5917
     *
5918
     * @return int
5919
     */
5920 20
    public function size(int $mode = \COUNT_NORMAL): int
5921
    {
5922 20
        return $this->count($mode);
5923
    }
5924
5925
    /**
5926
     * Checks whether array has exactly $size items.
5927
     *
5928
     * @param int $size
5929
     *
5930
     * @return bool
5931
     */
5932 1
    public function sizeIs(int $size): bool
5933
    {
5934
        // init
5935 1
        $itemsTempCount = 0;
5936
5937
        /** @noinspection PhpUnusedLocalVariableInspection */
5938
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5939 1
        foreach ($this->getGeneratorByReference() as &$value) {
5940 1
            ++$itemsTempCount;
5941 1
            if ($itemsTempCount > $size) {
5942 1
                return false;
5943
            }
5944
        }
5945
5946 1
        return $itemsTempCount === $size;
5947
    }
5948
5949
    /**
5950
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5951
     * smaller than $fromSize.
5952
     *
5953
     * @param int $fromSize
5954
     * @param int $toSize
5955
     *
5956
     * @return bool
5957
     */
5958 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5959
    {
5960 1
        if ($fromSize > $toSize) {
5961 1
            $tmp = $toSize;
5962 1
            $toSize = $fromSize;
5963 1
            $fromSize = $tmp;
5964
        }
5965
5966
        // init
5967 1
        $itemsTempCount = 0;
5968
5969 1
        foreach ($this->getGenerator() as $key => $value) {
5970 1
            ++$itemsTempCount;
5971 1
            if ($itemsTempCount > $toSize) {
5972 1
                return false;
5973
            }
5974
        }
5975
5976 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5977
    }
5978
5979
    /**
5980
     * Checks whether array has more than $size items.
5981
     *
5982
     * @param int $size
5983
     *
5984
     * @return bool
5985
     */
5986 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...
5987
    {
5988
        // init
5989 1
        $itemsTempCount = 0;
5990
5991 1
        foreach ($this->getGenerator() as $key => $value) {
5992 1
            ++$itemsTempCount;
5993 1
            if ($itemsTempCount > $size) {
5994 1
                return true;
5995
            }
5996
        }
5997
5998 1
        return $itemsTempCount > $size;
5999
    }
6000
6001
    /**
6002
     * Checks whether array has less than $size items.
6003
     *
6004
     * @param int $size
6005
     *
6006
     * @return bool
6007
     */
6008 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...
6009
    {
6010
        // init
6011 1
        $itemsTempCount = 0;
6012
6013 1
        foreach ($this->getGenerator() as $key => $value) {
6014 1
            ++$itemsTempCount;
6015 1
            if ($itemsTempCount > $size) {
6016 1
                return false;
6017
            }
6018
        }
6019
6020 1
        return $itemsTempCount < $size;
6021
    }
6022
6023
    /**
6024
     * Counts all elements in an array, or something in an object.
6025
     *
6026
     * <p>
6027
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6028
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6029
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6030
     * implemented and used in PHP.
6031
     * </p>
6032
     *
6033
     * @return int
6034
     *             <p>
6035
     *             The number of elements in var, which is
6036
     *             typically an array, since anything else will have one
6037
     *             element.
6038
     *             </p>
6039
     *             <p>
6040
     *             If var is not an array or an object with
6041
     *             implemented Countable interface,
6042
     *             1 will be returned.
6043
     *             There is one exception, if var is &null;,
6044
     *             0 will be returned.
6045
     *             </p>
6046
     *             <p>
6047
     *             Caution: count may return 0 for a variable that isn't set,
6048
     *             but it may also return 0 for a variable that has been initialized with an
6049
     *             empty array. Use isset to test if a variable is set.
6050
     *             </p>
6051
     */
6052 10
    public function sizeRecursive(): int
6053
    {
6054 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6055
    }
6056
6057
    /**
6058
     * Extract a slice of the array.
6059
     *
6060
     * @param int      $offset       <p>Slice begin index.</p>
6061
     * @param int|null $length       <p>Length of the slice.</p>
6062
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6063
     *
6064
     * @return static
6065
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6066
     *
6067
     * @psalm-return static<TKey,T>
6068
     * @psalm-mutation-free
6069
     */
6070 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6071
    {
6072 5
        return static::create(
6073 5
            \array_slice(
6074 5
                $this->toArray(),
6075 5
                $offset,
6076 5
                $length,
6077 5
                $preserveKeys
6078
            ),
6079 5
            $this->iteratorClass,
6080 5
            false
6081
        );
6082
    }
6083
6084
    /**
6085
     * Sort the current array and optional you can keep the keys.
6086
     *
6087
     * EXAMPLE: <code>
6088
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6089
     * </code>
6090
     *
6091
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6092
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6093
     *                              <strong>SORT_NATURAL</strong></p>
6094
     * @param bool       $keepKeys
6095
     *
6096
     * @return static
6097
     *                <p>(Mutable) Return this Arrayy object.</p>
6098
     *
6099
     * @psalm-return static<TKey,T>
6100
     */
6101 20
    public function sort(
6102
        $direction = \SORT_ASC,
6103
        int $strategy = \SORT_REGULAR,
6104
        bool $keepKeys = false
6105
    ): self {
6106 20
        $this->generatorToArray();
6107
6108 20
        return $this->sorting(
6109 20
            $this->array,
6110 20
            $direction,
6111 20
            $strategy,
6112 20
            $keepKeys
6113
        );
6114
    }
6115
6116
    /**
6117
     * Sort the current array and optional you can keep the keys.
6118
     *
6119
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6120
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6121
     *                              <strong>SORT_NATURAL</strong></p>
6122
     * @param bool       $keepKeys
6123
     *
6124
     * @return static
6125
     *                <p>(Immutable) Return this Arrayy object.</p>
6126
     *
6127
     * @psalm-return static<TKey,T>
6128
     */
6129 12
    public function sortImmutable(
6130
        $direction = \SORT_ASC,
6131
        int $strategy = \SORT_REGULAR,
6132
        bool $keepKeys = false
6133
    ): self {
6134 12
        $that = clone $this;
6135
6136 12
        $that->generatorToArray();
6137
6138 12
        return $that->sorting(
6139 12
            $that->array,
6140 12
            $direction,
6141 12
            $strategy,
6142 12
            $keepKeys
6143
        );
6144
    }
6145
6146
    /**
6147
     * Sort the current array by key.
6148
     *
6149
     * EXAMPLE: <code>
6150
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6151
     * </code>
6152
     *
6153
     * @see http://php.net/manual/en/function.ksort.php
6154
     * @see http://php.net/manual/en/function.krsort.php
6155
     *
6156
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6157
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6158
     *                              <strong>SORT_NATURAL</strong></p>
6159
     *
6160
     * @return $this
6161
     *               <p>(Mutable) Return this Arrayy object.</p>
6162
     *
6163
     * @psalm-return static<TKey,T>
6164
     */
6165 18
    public function sortKeys(
6166
        $direction = \SORT_ASC,
6167
        int $strategy = \SORT_REGULAR
6168
    ): self {
6169 18
        $this->generatorToArray();
6170
6171 18
        $this->sorterKeys($this->array, $direction, $strategy);
6172
6173 18
        return $this;
6174
    }
6175
6176
    /**
6177
     * Sort the current array by key.
6178
     *
6179
     * @see          http://php.net/manual/en/function.ksort.php
6180
     * @see          http://php.net/manual/en/function.krsort.php
6181
     *
6182
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6183
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6184
     *                              <strong>SORT_NATURAL</strong></p>
6185
     *
6186
     * @return $this
6187
     *               <p>(Immutable) Return this Arrayy object.</p>
6188
     *
6189
     * @psalm-return static<TKey,T>
6190
     * @psalm-mutation-free
6191
     */
6192 8
    public function sortKeysImmutable(
6193
        $direction = \SORT_ASC,
6194
        int $strategy = \SORT_REGULAR
6195
    ): self {
6196 8
        $that = clone $this;
6197
6198
        /**
6199
         * @psalm-suppress ImpureMethodCall - object is already cloned
6200
         */
6201 8
        $that->sortKeys($direction, $strategy);
6202
6203 8
        return $that;
6204
    }
6205
6206
    /**
6207
     * Sort the current array by value.
6208
     *
6209
     * EXAMPLE: <code>
6210
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6211
     * </code>
6212
     *
6213
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6214
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6215
     *                              <strong>SORT_NATURAL</strong></p>
6216
     *
6217
     * @return static
6218
     *                <p>(Mutable)</p>
6219
     *
6220
     * @psalm-return static<TKey,T>
6221
     */
6222 1
    public function sortValueKeepIndex(
6223
        $direction = \SORT_ASC,
6224
        int $strategy = \SORT_REGULAR
6225
    ): self {
6226 1
        return $this->sort($direction, $strategy, true);
6227
    }
6228
6229
    /**
6230
     * Sort the current array by value.
6231
     *
6232
     * EXAMPLE: <code>
6233
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6234
     * </code>
6235
     *
6236
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6237
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6238
     *                              <strong>SORT_NATURAL</strong></p>
6239
     *
6240
     * @return static
6241
     *                <p>(Mutable)</p>
6242
     *
6243
     * @psalm-return static<TKey,T>
6244
     */
6245 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6246
    {
6247 1
        return $this->sort($direction, $strategy, false);
6248
    }
6249
6250
    /**
6251
     * Sort a array by value or by a closure.
6252
     *
6253
     * - If the sorter is null, the array is sorted naturally.
6254
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6255
     *
6256
     * EXAMPLE: <code>
6257
     * $testArray = range(1, 5);
6258
     * $under = a($testArray)->sorter(
6259
     *     function ($value) {
6260
     *         return $value % 2 === 0;
6261
     *     }
6262
     * );
6263
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6264
     * </code>
6265
     *
6266
     * @param callable|string|null $sorter
6267
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6268
     *                                        <strong>SORT_DESC</strong></p>
6269
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6270
     *                                        <strong>SORT_NATURAL</strong></p>
6271
     *
6272
     * @return static
6273
     *                <p>(Immutable)</p>
6274
     *
6275
     * @psalm-return static<TKey,T>
6276
     * @psalm-mutation-free
6277
     */
6278 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6279
    {
6280 1
        $array = $this->toArray();
6281 1
        $direction = $this->getDirection($direction);
6282
6283
        // Transform all values into their results.
6284 1
        if ($sorter) {
6285 1
            $arrayy = static::create(
6286 1
                $array,
6287 1
                $this->iteratorClass,
6288 1
                false
6289
            );
6290
6291
            /**
6292
             * @psalm-suppress MissingClosureReturnType
6293
             * @psalm-suppress MissingClosureParamType
6294
             */
6295 1
            $results = $arrayy->each(
6296
                static function ($value) use ($sorter) {
6297 1
                    if (\is_callable($sorter) === true) {
6298 1
                        return $sorter($value);
6299
                    }
6300
6301 1
                    return $sorter === $value;
6302 1
                }
6303
            );
6304
6305 1
            $results = $results->toArray();
6306
        } else {
6307 1
            $results = $array;
6308
        }
6309
6310
        // Sort by the results and replace by original values
6311 1
        \array_multisort($results, $direction, $strategy, $array);
6312
6313 1
        return static::create(
6314 1
            $array,
6315 1
            $this->iteratorClass,
6316 1
            false
6317
        );
6318
    }
6319
6320
    /**
6321
     * @param int      $offset
6322
     * @param int|null $length
6323
     * @param array    $replacement
6324
     *
6325
     * @return static
6326
     *                <p>(Immutable)</p>
6327
     *
6328
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6329
     * @psalm-return static<TKey,T>
6330
     * @psalm-mutation-free
6331
     */
6332 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6333
    {
6334 1
        $tmpArray = $this->toArray();
6335
6336 1
        \array_splice(
6337 1
            $tmpArray,
6338 1
            $offset,
6339 1
            $length ?? $this->count(),
6340 1
            $replacement
6341
        );
6342
6343 1
        return static::create(
6344 1
            $tmpArray,
6345 1
            $this->iteratorClass,
6346 1
            false
6347
        );
6348
    }
6349
6350
    /**
6351
     * Split an array in the given amount of pieces.
6352
     *
6353
     * EXAMPLE: <code>
6354
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6355
     * </code>
6356
     *
6357
     * @param int  $numberOfPieces
6358
     * @param bool $keepKeys
6359
     *
6360
     * @return static
6361
     *                <p>(Immutable)</p>
6362
     *
6363
     * @psalm-return static<TKey,T>
6364
     * @psalm-mutation-free
6365
     */
6366 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6367
    {
6368 1
        if ($keepKeys) {
6369
            $generator = function () use ($numberOfPieces) {
6370 1
                $carry = [];
6371 1
                $i = 1;
6372 1
                foreach ($this->getGenerator() as $key => $value) {
6373 1
                    $carry[$key] = $value;
6374
6375 1
                    if ($i % $numberOfPieces !== 0) {
6376 1
                        ++$i;
6377
6378 1
                        continue;
6379
                    }
6380
6381 1
                    yield $carry;
6382
6383 1
                    $carry = [];
6384 1
                    $i = 1;
6385
                }
6386
6387 1
                if ($carry !== []) {
6388 1
                    yield $carry;
6389
                }
6390 1
            };
6391 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...
6392
            $generator = function () use ($numberOfPieces) {
6393 1
                $carry = [];
6394 1
                $i = 1;
6395 1
                foreach ($this->getGenerator() as $key => $value) {
6396 1
                    $carry[] = $value;
6397
6398 1
                    if ($i % $numberOfPieces !== 0) {
6399 1
                        ++$i;
6400
6401 1
                        continue;
6402
                    }
6403
6404 1
                    yield $carry;
6405
6406 1
                    $carry = [];
6407 1
                    $i = 1;
6408
                }
6409
6410 1
                if ($carry !== []) {
6411 1
                    yield $carry;
6412
                }
6413 1
            };
6414
        }
6415
6416 1
        return static::create(
6417 1
            $generator,
6418 1
            $this->iteratorClass,
6419 1
            false
6420
        );
6421
    }
6422
6423
    /**
6424
     * Strip all empty items from the current array.
6425
     *
6426
     * EXAMPLE: <code>
6427
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6428
     * </code>
6429
     *
6430
     * @return static
6431
     *                <p>(Immutable)</p>
6432
     *
6433
     * @psalm-return static<TKey,T>
6434
     * @psalm-mutation-free
6435
     */
6436 1
    public function stripEmpty(): self
6437
    {
6438 1
        return $this->filter(
6439
            static function ($item) {
6440 1
                if ($item === null) {
6441 1
                    return false;
6442
                }
6443
6444 1
                return (bool) \trim((string) $item);
6445 1
            }
6446
        );
6447
    }
6448
6449
    /**
6450
     * Swap two values between positions by key.
6451
     *
6452
     * EXAMPLE: <code>
6453
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6454
     * </code>
6455
     *
6456
     * @param int|string $swapA <p>a key in the array</p>
6457
     * @param int|string $swapB <p>a key in the array</p>
6458
     *
6459
     * @return static
6460
     *                <p>(Immutable)</p>
6461
     *
6462
     * @psalm-return static<TKey,T>
6463
     * @psalm-mutation-free
6464
     */
6465 1
    public function swap($swapA, $swapB): self
6466
    {
6467 1
        $array = $this->toArray();
6468
6469 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6470
6471 1
        return static::create(
6472 1
            $array,
6473 1
            $this->iteratorClass,
6474 1
            false
6475
        );
6476
    }
6477
6478
    /**
6479
     * Get the current array from the "Arrayy"-object.
6480
     * alias for "getArray()"
6481
     *
6482
     * @param bool $convertAllArrayyElements <p>
6483
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6484
     *                                       </p>
6485
     * @param bool $preserveKeys             <p>
6486
     *                                       e.g.: A generator maybe return the same key more then once,
6487
     *                                       so maybe you will ignore the keys.
6488
     *                                       </p>
6489
     *
6490
     * @return array
6491
     *
6492
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6493
     * @psalm-mutation-free
6494
     */
6495 931
    public function toArray(
6496
        bool $convertAllArrayyElements = false,
6497
        bool $preserveKeys = true
6498
    ): array {
6499
        // init
6500 931
        $array = [];
6501
6502 931
        if ($convertAllArrayyElements) {
6503 2
            foreach ($this->getGenerator() as $key => $value) {
6504 2
                if ($value instanceof self) {
6505 1
                    $value = $value->toArray(
6506 1
                        $convertAllArrayyElements,
6507 1
                        $preserveKeys
6508
                    );
6509
                }
6510
6511 2
                if ($preserveKeys) {
6512 1
                    $array[$key] = $value;
6513
                } else {
6514 1
                    $array[] = $value;
6515
                }
6516
            }
6517
        } else {
6518 931
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6519
        }
6520
6521 931
        return $array;
6522
    }
6523
6524
    /**
6525
     * Get the current array from the "Arrayy"-object as list.
6526
     *
6527
     * @param bool $convertAllArrayyElements <p>
6528
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6529
     *                                       </p>
6530
     *
6531
     * @return array
6532
     *
6533
     * @psalm-return list<array<TKey,T>>
6534
     * @psalm-mutation-free
6535
     */
6536 1
    public function toList(bool $convertAllArrayyElements = false): array
6537
    {
6538 1
        return $this->toArray(
6539 1
            $convertAllArrayyElements,
6540 1
            false
6541
        );
6542
    }
6543
6544
    /**
6545
     * Convert the current array to JSON.
6546
     *
6547
     * EXAMPLE: <code>
6548
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6549
     * </code>
6550
     *
6551
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6552
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6553
     *
6554
     * @return string
6555
     */
6556 12
    public function toJson(int $options = 0, int $depth = 512): string
6557
    {
6558 12
        $return = \json_encode($this->toArray(), $options, $depth);
6559 12
        if ($return === false) {
6560
            return '';
6561
        }
6562
6563 12
        return $return;
6564
    }
6565
6566
    /**
6567
     * @param string[]|null $items  [optional]
6568
     * @param string[]      $helper [optional]
6569
     *
6570
     * @return static|static[]
6571
     *
6572
     * @psalm-return static<int, static<TKey,T>>
6573
     */
6574 1
    public function toPermutation(array $items = null, array $helper = []): self
6575
    {
6576
        // init
6577 1
        $return = [];
6578
6579 1
        if ($items === null) {
6580 1
            $items = $this->toArray();
6581
        }
6582
6583 1
        if (empty($items)) {
6584 1
            $return[] = $helper;
6585
        } else {
6586 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6587 1
                $new_items = $items;
6588 1
                $new_helper = $helper;
6589 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6590
                /** @noinspection PhpSillyAssignmentInspection */
6591
                /** @var string[] $new_items */
6592 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...
6593 1
                \array_unshift($new_helper, $tmp_helper);
6594 1
                $return = \array_merge(
6595 1
                    $return,
6596 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6597
                );
6598
            }
6599
        }
6600
6601 1
        return static::create(
6602 1
            $return,
6603 1
            $this->iteratorClass,
6604 1
            false
6605
        );
6606
    }
6607
6608
    /**
6609
     * Implodes array to a string with specified separator.
6610
     *
6611
     * @param string $separator [optional] <p>The element's separator.</p>
6612
     *
6613
     * @return string
6614
     *                <p>The string representation of array, separated by ",".</p>
6615
     */
6616 19
    public function toString(string $separator = ','): string
6617
    {
6618 19
        return $this->implode($separator);
6619
    }
6620
6621
    /**
6622
     * Return a duplicate free copy of the current array.
6623
     *
6624
     * EXAMPLE: <code>
6625
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6626
     * </code>
6627
     *
6628
     * @return $this
6629
     *               <p>(Mutable)</p>
6630
     *
6631
     * @psalm-return static<TKey,T>
6632
     */
6633 13
    public function uniqueNewIndex(): self
6634
    {
6635
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6636
6637
        /**
6638
         * @psalm-suppress MissingClosureReturnType
6639
         * @psalm-suppress MissingClosureParamType
6640
         */
6641 13
        $this->array = $this->reduce(
6642
            static function ($resultArray, $value) {
6643 12
                if (!\in_array($value, $resultArray, true)) {
6644 12
                    $resultArray[] = $value;
6645
                }
6646
6647 12
                return $resultArray;
6648 13
            },
6649 13
            []
6650 13
        )->toArray();
6651 13
        $this->generator = null;
6652
6653 13
        return $this;
6654
    }
6655
6656
    /**
6657
     * Return a duplicate free copy of the current array. (with the old keys)
6658
     *
6659
     * EXAMPLE: <code>
6660
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6661
     * </code>
6662
     *
6663
     * @return $this
6664
     *               <p>(Mutable)</p>
6665
     *
6666
     * @psalm-return static<TKey,T>
6667
     */
6668 11
    public function uniqueKeepIndex(): self
6669
    {
6670
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6671
6672
        // init
6673 11
        $array = $this->toArray();
6674
6675
        /**
6676
         * @psalm-suppress MissingClosureReturnType
6677
         * @psalm-suppress MissingClosureParamType
6678
         */
6679 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...
6680 11
            \array_keys($array),
6681
            static function ($resultArray, $key) use ($array) {
6682 10
                if (!\in_array($array[$key], $resultArray, true)) {
6683 10
                    $resultArray[$key] = $array[$key];
6684
                }
6685
6686 10
                return $resultArray;
6687 11
            },
6688 11
            []
6689
        );
6690 11
        $this->generator = null;
6691
6692 11
        return $this;
6693
    }
6694
6695
    /**
6696
     * alias: for "Arrayy->uniqueNewIndex()"
6697
     *
6698
     * @return static
6699
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6700
     *
6701
     * @see          Arrayy::unique()
6702
     *
6703
     * @psalm-return static<TKey,T>
6704
     */
6705 13
    public function unique(): self
6706
    {
6707 13
        return $this->uniqueNewIndex();
6708
    }
6709
6710
    /**
6711
     * Prepends one or more values to the beginning of array at once.
6712
     *
6713
     * @param array ...$args
6714
     *
6715
     * @return $this
6716
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6717
     *
6718
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6719
     * @psalm-return static<TKey,T>
6720
     */
6721 4
    public function unshift(...$args): self
6722
    {
6723 4
        $this->generatorToArray();
6724
6725 4
        \array_unshift($this->array, ...$args);
6726
6727 4
        return $this;
6728
    }
6729
6730
    /**
6731
     * Tests whether the given closure return something valid for all elements of this array.
6732
     *
6733
     * @param \Closure $closure the predicate
6734
     *
6735
     * @return bool
6736
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6737
     */
6738 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...
6739
    {
6740 1
        foreach ($this->getGenerator() as $key => $value) {
6741 1
            if (!$closure($value, $key)) {
6742 1
                return false;
6743
            }
6744
        }
6745
6746 1
        return true;
6747
    }
6748
6749
    /**
6750
     * Get all values from a array.
6751
     *
6752
     * EXAMPLE: <code>
6753
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6754
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6755
     * </code>
6756
     *
6757
     * @return static
6758
     *                <p>(Immutable)</p>
6759
     *
6760
     * @psalm-return static<TKey,T>
6761
     * @psalm-mutation-free
6762
     */
6763 2
    public function values(): self
6764
    {
6765 2
        return static::create(
6766
            function () {
6767
                /** @noinspection YieldFromCanBeUsedInspection */
6768 2
                foreach ($this->getGenerator() as $value) {
6769 2
                    yield $value;
6770
                }
6771 2
            },
6772 2
            $this->iteratorClass,
6773 2
            false
6774
        );
6775
    }
6776
6777
    /**
6778
     * Apply the given function to every element in the array, discarding the results.
6779
     *
6780
     * EXAMPLE: <code>
6781
     * $callable = function (&$value, $key) {
6782
     *     $value = $key;
6783
     * };
6784
     * $arrayy = a([1, 2, 3]);
6785
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6786
     * </code>
6787
     *
6788
     * @param callable $callable
6789
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6790
     * @param mixed    $userData  [optional] <p>
6791
     *                            If the optional $userData parameter is supplied,
6792
     *                            it will be passed as the third parameter to the $callable.
6793
     *                            </p>
6794
     *
6795
     * @return $this
6796
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6797
     *
6798
     * @psalm-return static<TKey,T>
6799
     */
6800 12
    public function walk(
6801
        $callable,
6802
        bool $recursive = false,
6803
        $userData = self::ARRAYY_HELPER_WALK
6804
    ): self {
6805 12
        $this->generatorToArray();
6806
6807 12
        if ($this->array !== []) {
6808 10
            if ($recursive === true) {
6809 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6810
                    \array_walk_recursive($this->array, $callable, $userData);
6811
                } else {
6812 5
                    \array_walk_recursive($this->array, $callable);
6813
                }
6814
            } else {
6815
                /** @noinspection NestedPositiveIfStatementsInspection */
6816 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6817
                    \array_walk($this->array, $callable, $userData);
6818
                } else {
6819 5
                    \array_walk($this->array, $callable);
6820
                }
6821
            }
6822
        }
6823
6824 12
        return $this;
6825
    }
6826
6827
    /**
6828
     * Returns a collection of matching items.
6829
     *
6830
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6831
     * @param mixed  $value                 the value to match
6832
     *
6833
     * @throws \InvalidArgumentException if property or method is not defined
6834
     *
6835
     * @return static
6836
     *
6837
     * @psalm-param  T $value
6838
     * @psalm-return static<TKey,T>
6839
     */
6840 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6841
    {
6842 1
        return $this->filter(
6843
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6844
                $accessorValue = $this->extractValue(
6845
                    $item,
6846
                    $keyOrPropertyOrMethod
6847
                );
6848
6849
                return $accessorValue === $value;
6850 1
            }
6851
        );
6852
    }
6853
6854
    /**
6855
     * Convert an array into a object.
6856
     *
6857
     * @param array $array
6858
     *
6859
     * @return \stdClass
6860
     *
6861
     * @psalm-param array<mixed,mixed> $array
6862
     */
6863 4
    final protected static function arrayToObject(array $array = []): \stdClass
6864
    {
6865
        // init
6866 4
        $object = new \stdClass();
6867
6868 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6869 1
            return $object;
6870
        }
6871
6872 3
        foreach ($array as $name => $value) {
6873 3
            if (\is_array($value)) {
6874 1
                $object->{$name} = static::arrayToObject($value);
6875
            } else {
6876 3
                $object->{$name} = $value;
6877
            }
6878
        }
6879
6880 3
        return $object;
6881
    }
6882
6883
    /**
6884
     * @param array|\Generator|null $input         <p>
6885
     *                                             An array containing keys to return.
6886
     *                                             </p>
6887
     * @param mixed|null            $search_values [optional] <p>
6888
     *                                             If specified, then only keys containing these values are returned.
6889
     *                                             </p>
6890
     * @param bool                  $strict        [optional] <p>
6891
     *                                             Determines if strict comparison (===) should be used during the
6892
     *                                             search.
6893
     *                                             </p>
6894
     *
6895
     * @return array
6896
     *               <p>an array of all the keys in input</p>
6897
     *
6898
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6899
     * @psalm-return array<TKey|mixed>
6900
     * @psalm-mutation-free
6901
     */
6902 11
    protected function array_keys_recursive(
6903
        $input = null,
6904
        $search_values = null,
6905
        bool $strict = true
6906
    ): array {
6907
        // init
6908 11
        $keys = [];
6909 11
        $keysTmp = [];
6910
6911 11
        if ($input === null) {
6912 4
            $input = $this->getGenerator();
6913
        }
6914
6915 11
        if ($search_values === null) {
6916 11
            foreach ($input as $key => $value) {
6917 11
                $keys[] = $key;
6918
6919
                // check if recursive is needed
6920 11
                if (\is_array($value)) {
6921 4
                    $keysTmp[] = $this->array_keys_recursive($value);
6922
                }
6923
            }
6924
        } else {
6925 1
            $is_array_tmp = \is_array($search_values);
6926
6927 1
            foreach ($input as $key => $value) {
6928 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...
6929
                    (
6930 1
                        $is_array_tmp === false
6931
                        &&
6932 1
                        $strict === true
6933
                        &&
6934 1
                        $search_values === $value
6935
                    )
6936
                    ||
6937
                    (
6938 1
                        $is_array_tmp === false
6939
                        &&
6940 1
                        $strict === false
6941
                        &&
6942 1
                        $search_values == $value
6943
                    )
6944
                    ||
6945
                    (
6946 1
                        $is_array_tmp === true
6947
                        &&
6948 1
                        \in_array($value, $search_values, $strict)
6949
                    )
6950
                ) {
6951 1
                    $keys[] = $key;
6952
                }
6953
6954
                // check if recursive is needed
6955 1
                if (\is_array($value)) {
6956 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6957
                }
6958
            }
6959
        }
6960
6961 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6962
    }
6963
6964
    /**
6965
     * @param mixed      $path
6966
     * @param callable   $callable
6967
     * @param array|null $currentOffset
6968
     *
6969
     * @return void
6970
     *
6971
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6972
     * @psalm-mutation-free
6973
     */
6974 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6975
    {
6976 10
        $this->generatorToArray();
6977
6978 10
        if ($currentOffset === null) {
6979 10
            $currentOffset = &$this->array;
6980
        }
6981
6982 10
        $explodedPath = \explode($this->pathSeparator, $path);
6983 10
        if ($explodedPath === false) {
6984
            return;
6985
        }
6986
6987 10
        $nextPath = \array_shift($explodedPath);
6988
6989 10
        if (!isset($currentOffset[$nextPath])) {
6990 1
            return;
6991
        }
6992
6993 9
        if (!empty($explodedPath)) {
6994 1
            $this->callAtPath(
6995 1
                \implode($this->pathSeparator, $explodedPath),
6996 1
                $callable,
6997 1
                $currentOffset[$nextPath]
6998
            );
6999
        } else {
7000 9
            $callable($currentOffset[$nextPath]);
7001
        }
7002 9
    }
7003
7004
    /**
7005
     * Extracts the value of the given property or method from the object.
7006
     *
7007
     * @param static $object                <p>The object to extract the value from.</p>
7008
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
7009
     *                                      value should be extracted.</p>
7010
     *
7011
     * @throws \InvalidArgumentException if the method or property is not defined
7012
     *
7013
     * @return mixed
7014
     *               <p>The value extracted from the specified property or method.</p>
7015
     *
7016
     * @psalm-param self<TKey,T> $object
7017
     */
7018 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7019
    {
7020 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7021 1
            $return = $object->get($keyOrPropertyOrMethod);
7022
7023 1
            if ($return instanceof self) {
7024
                return $return->toArray();
7025
            }
7026
7027 1
            return $return;
7028
        }
7029
7030
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7031
            return $object->{$keyOrPropertyOrMethod};
7032
        }
7033
7034
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7035
            return $object->{$keyOrPropertyOrMethod}();
7036
        }
7037
7038
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7039
    }
7040
7041
    /**
7042
     * create a fallback for array
7043
     *
7044
     * 1. use the current array, if it's a array
7045
     * 2. fallback to empty array, if there is nothing
7046
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7047
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7048
     * 5. call "__toArray()" on object, if the method exists
7049
     * 6. cast a string or object with "__toString()" into an array
7050
     * 7. throw a "InvalidArgumentException"-Exception
7051
     *
7052
     * @param mixed $data
7053
     *
7054
     * @throws \InvalidArgumentException
7055
     *
7056
     * @return array
7057
     *
7058
     * @psalm-return array<mixed,mixed>|array<TKey,T>
7059
     */
7060 1206
    protected function fallbackForArray(&$data): array
7061
    {
7062 1206
        $data = $this->internalGetArray($data);
7063
7064 1206
        if ($data === null) {
7065 2
            throw new \InvalidArgumentException('Passed value should be a array');
7066
        }
7067
7068 1204
        return $data;
7069
    }
7070
7071
    /**
7072
     * @param bool $preserveKeys <p>
7073
     *                           e.g.: A generator maybe return the same key more then once,
7074
     *                           so maybe you will ignore the keys.
7075
     *                           </p>
7076
     *
7077
     * @return bool
7078
     *
7079
     * @noinspection ReturnTypeCanBeDeclaredInspection
7080
     * @psalm-mutation-free :/
7081
     */
7082 1118
    protected function generatorToArray(bool $preserveKeys = true)
7083
    {
7084 1118
        if ($this->generator) {
7085 2
            $this->array = $this->toArray(false, $preserveKeys);
7086 2
            $this->generator = null;
7087
7088 2
            return true;
7089
        }
7090
7091 1118
        return false;
7092
    }
7093
7094
    /**
7095
     * Get correct PHP constant for direction.
7096
     *
7097
     * @param int|string $direction
7098
     *
7099
     * @return int
7100
     * @psalm-mutation-free
7101
     */
7102 43
    protected function getDirection($direction): int
7103
    {
7104 43
        if ((string) $direction === $direction) {
7105 10
            $direction = \strtolower($direction);
7106
7107 10
            if ($direction === 'desc') {
7108 2
                $direction = \SORT_DESC;
7109
            } else {
7110 9
                $direction = \SORT_ASC;
7111
            }
7112
        }
7113
7114
        if (
7115 43
            $direction !== \SORT_DESC
7116
            &&
7117 43
            $direction !== \SORT_ASC
7118
        ) {
7119
            $direction = \SORT_ASC;
7120
        }
7121
7122 43
        return $direction;
7123
    }
7124
7125
    /**
7126
     * @return TypeCheckInterface[]
7127
     *
7128
     * @noinspection ReturnTypeCanBeDeclaredInspection
7129
     */
7130 24
    protected function getPropertiesFromPhpDoc()
7131
    {
7132 24
        static $PROPERTY_CACHE = [];
7133 24
        $cacheKey = 'Class::' . static::class;
7134
7135 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7136 22
            return $PROPERTY_CACHE[$cacheKey];
7137
        }
7138
7139
        // init
7140 4
        $properties = [];
7141
7142 4
        $reflector = new \ReflectionClass($this);
7143 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7144 4
        $docComment = $reflector->getDocComment();
7145 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...
7146 3
            $docblock = $factory->create($docComment);
7147
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7148 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7149 3
                $typeName = $tag->getVariableName();
7150
                /** @var string|null $typeName */
7151 3
                if ($typeName !== null) {
7152 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7153 3
                    if ($typeCheckPhpDoc !== null) {
7154 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7155
                    }
7156
                }
7157
            }
7158
        }
7159
7160
        /** @noinspection PhpAssignmentInConditionInspection */
7161 4
        while ($reflector = $reflector->getParentClass()) {
7162 4
            $docComment = $reflector->getDocComment();
7163 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...
7164 4
                $docblock = $factory->create($docComment);
7165
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7166 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7167 1
                    $typeName = $tag->getVariableName();
7168
                    /** @var string|null $typeName */
7169 1
                    if ($typeName !== null) {
7170 1
                        if (isset($properties[$typeName])) {
7171 1
                            continue;
7172
                        }
7173
7174 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7175 1
                        if ($typeCheckPhpDoc !== null) {
7176 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7177
                        }
7178
                    }
7179
                }
7180
            }
7181
        }
7182
7183 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7184
    }
7185
7186
    /**
7187
     * @param mixed $glue
7188
     * @param mixed $pieces
7189
     * @param bool  $useKeys
7190
     *
7191
     * @return string
7192
     * @psalm-mutation-free
7193
     */
7194 36
    protected function implode_recursive(
7195
        $glue = '',
7196
        $pieces = [],
7197
        bool $useKeys = false
7198
    ): string {
7199 36
        if ($pieces instanceof self) {
7200 1
            $pieces = $pieces->toArray();
7201
        }
7202
7203 36
        if (\is_array($pieces)) {
7204 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7205 36
            $pieces_count_not_zero = $pieces_count > 0;
7206
7207 36
            return \implode(
7208 36
                $glue,
7209 36
                \array_map(
7210 36
                    [$this, 'implode_recursive'],
7211 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7212 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7213
                )
7214
            );
7215
        }
7216
7217
        if (
7218 36
            \is_scalar($pieces) === true
7219
            ||
7220 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7221
        ) {
7222 32
            return (string) $pieces;
7223
        }
7224
7225 8
        return '';
7226
    }
7227
7228
    /**
7229
     * @param mixed                 $needle   <p>
7230
     *                                        The searched value.
7231
     *                                        </p>
7232
     *                                        <p>
7233
     *                                        If needle is a string, the comparison is done
7234
     *                                        in a case-sensitive manner.
7235
     *                                        </p>
7236
     * @param array|\Generator|null $haystack <p>
7237
     *                                        The array.
7238
     *                                        </p>
7239
     * @param bool                  $strict   [optional] <p>
7240
     *                                        If the third parameter strict is set to true
7241
     *                                        then the in_array function will also check the
7242
     *                                        types of the
7243
     *                                        needle in the haystack.
7244
     *                                        </p>
7245
     *
7246
     * @return bool
7247
     *              <p>true if needle is found in the array, false otherwise</p>
7248
     *
7249
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7250
     * @psalm-mutation-free
7251
     */
7252 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7253
    {
7254 18
        if ($haystack === null) {
7255
            $haystack = $this->getGenerator();
7256
        }
7257
7258 18
        foreach ($haystack as $item) {
7259 14
            if (\is_array($item)) {
7260 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7261
            } else {
7262
                /** @noinspection NestedPositiveIfStatementsInspection */
7263 14
                if ($strict === true) {
7264 14
                    $returnTmp = $item === $needle;
7265
                } else {
7266
                    $returnTmp = $item == $needle;
7267
                }
7268
            }
7269
7270 14
            if ($returnTmp === true) {
7271 10
                return true;
7272
            }
7273
        }
7274
7275 8
        return false;
7276
    }
7277
7278
    /**
7279
     * @param mixed $data
7280
     *
7281
     * @return array|null
7282
     *
7283
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7284
     */
7285 1206
    protected function internalGetArray(&$data)
7286
    {
7287 1206
        if (\is_array($data)) {
7288 1200
            return $data;
7289
        }
7290
7291 80
        if (!$data) {
7292 7
            return [];
7293
        }
7294
7295 79
        if (\is_object($data) === true) {
7296 72
            if ($data instanceof \ArrayObject) {
7297 5
                return $data->getArrayCopy();
7298
            }
7299
7300 68
            if ($data instanceof \Generator) {
7301
                return static::createFromGeneratorImmutable($data)->toArray();
7302
            }
7303
7304 68
            if ($data instanceof \Traversable) {
7305
                return static::createFromObject($data)->toArray();
7306
            }
7307
7308 68
            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...
7309
                return (array) $data->jsonSerialize();
7310
            }
7311
7312 68
            if (\method_exists($data, '__toArray')) {
7313
                return (array) $data->__toArray();
7314
            }
7315
7316 68
            if (\method_exists($data, '__toString')) {
7317
                return [(string) $data];
7318
            }
7319
        }
7320
7321 75
        if (\is_callable($data)) {
7322
            /**
7323
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7324
             */
7325 66
            $this->generator = new ArrayyRewindableGenerator($data);
7326
7327 66
            return [];
7328
        }
7329
7330 11
        if (\is_scalar($data)) {
7331 9
            return [$data];
7332
        }
7333
7334 2
        return null;
7335
    }
7336
7337
    /**
7338
     * Internal mechanics of remove method.
7339
     *
7340
     * @param mixed $key
7341
     *
7342
     * @return bool
7343
     */
7344 22
    protected function internalRemove($key): bool
7345
    {
7346 22
        $this->generatorToArray();
7347
7348
        if (
7349 22
            $this->pathSeparator
7350
            &&
7351 22
            (string) $key === $key
7352
            &&
7353 22
            \strpos($key, $this->pathSeparator) !== false
7354
        ) {
7355
            $path = \explode($this->pathSeparator, (string) $key);
7356
7357
            if ($path !== false) {
7358
                // crawl though the keys
7359
                while (\count($path, \COUNT_NORMAL) > 1) {
7360
                    $key = \array_shift($path);
7361
7362
                    if (!$this->has($key)) {
7363
                        return false;
7364
                    }
7365
7366
                    $this->array = &$this->array[$key];
7367
                }
7368
7369
                $key = \array_shift($path);
7370
            }
7371
        }
7372
7373 22
        unset($this->array[$key]);
7374
7375 22
        return true;
7376
    }
7377
7378
    /**
7379
     * Internal mechanic of set method.
7380
     *
7381
     * @param int|string|null $key
7382
     * @param mixed           $value
7383
     * @param bool            $checkProperties
7384
     *
7385
     * @return bool
7386
     */
7387 1056
    protected function internalSet(
7388
        $key,
7389
        &$value,
7390
        bool $checkProperties = true
7391
    ): bool {
7392
        if (
7393 1056
            $checkProperties === true
7394
            &&
7395 1056
            $this->properties !== []
7396
        ) {
7397 112
            $this->checkType($key, $value);
7398
        }
7399
7400 1054
        if ($key === null) {
7401
            return false;
7402
        }
7403
7404 1054
        $this->generatorToArray();
7405
7406
        /** @psalm-var array<int|string,mixed> $array */
7407 1054
        $array = &$this->array;
7408
7409
        /**
7410
         * https://github.com/vimeo/psalm/issues/2536
7411
         *
7412
         * @psalm-suppress PossiblyInvalidArgument
7413
         * @psalm-suppress InvalidScalarArgument
7414
         */
7415
        if (
7416 1054
            $this->pathSeparator
7417
            &&
7418 1054
            (string) $key === $key
7419
            &&
7420 1054
            \strpos($key, $this->pathSeparator) !== false
7421
        ) {
7422 9
            $path = \explode($this->pathSeparator, (string) $key);
7423
7424 9
            if ($path !== false) {
7425
                // crawl through the keys
7426 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7427 9
                    $key = \array_shift($path);
7428
7429 9
                    $array = &$array[$key];
7430
                }
7431
7432 9
                $key = \array_shift($path);
7433
            }
7434
        }
7435
7436 1054
        if ($array === null) {
7437 4
            $array = [];
7438 1051
        } elseif (!\is_array($array)) {
7439 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7440
        }
7441
7442 1054
        $array[$key] = $value;
7443
7444 1054
        return true;
7445
    }
7446
7447
    /**
7448
     * Convert a object into an array.
7449
     *
7450
     * @param mixed|object $object
7451
     *
7452
     * @return array|mixed
7453
     *
7454
     * @psalm-mutation-free
7455
     */
7456 5
    protected static function objectToArray($object)
7457
    {
7458 5
        if (!\is_object($object)) {
7459 4
            return $object;
7460
        }
7461
7462 5
        $object = \get_object_vars($object);
7463
7464
        /**
7465
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7466
         */
7467 5
        return \array_map(['static', 'objectToArray'], $object);
7468
    }
7469
7470
    /**
7471
     * @param array $data
7472
     * @param bool  $checkPropertiesInConstructor
7473
     *
7474
     * @return void
7475
     *
7476
     * @psalm-param array<mixed,T> $data
7477
     */
7478 1204
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7479
    {
7480 1204
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7481
                                        &&
7482 1204
                                        $checkPropertiesInConstructor === true;
7483
7484 1204
        if ($this->properties !== []) {
7485 99
            foreach ($data as $key => &$valueInner) {
7486 99
                $this->internalSet(
7487 99
                    $key,
7488 99
                    $valueInner,
7489 99
                    $checkPropertiesInConstructor
7490
                );
7491
            }
7492
        } else {
7493
            if (
7494 1124
                $this->checkPropertyTypes === true
7495
                ||
7496 1124
                $checkPropertiesInConstructor === true
7497
            ) {
7498 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7499
            }
7500
7501
            /** @var TypeCheckInterface[] $properties */
7502 1124
            $properties = $this->properties;
7503
7504
            if (
7505 1124
                $this->checkPropertiesMismatchInConstructor === true
7506
                &&
7507 1124
                \count($data) !== 0
7508
                &&
7509 1124
                \count(\array_diff_key($properties, $data)) > 0
7510
            ) {
7511 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...
7512
            }
7513
7514 1123
            foreach ($data as $key => &$valueInner) {
7515 952
                $this->internalSet(
7516 952
                    $key,
7517 952
                    $valueInner,
7518 952
                    $checkPropertiesInConstructor
7519
                );
7520
            }
7521
        }
7522 1196
    }
7523
7524
    /**
7525
     * sorting keys
7526
     *
7527
     * @param array      $elements
7528
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7529
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7530
     *                              <strong>SORT_NATURAL</strong></p>
7531
     *
7532
     * @return $this
7533
     *               <p>(Mutable) Return this Arrayy object.</p>
7534
     *
7535
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7536
     * @psalm-return static<TKey,T>
7537
     */
7538 18
    protected function sorterKeys(
7539
        array &$elements,
7540
        $direction = \SORT_ASC,
7541
        int $strategy = \SORT_REGULAR
7542
    ): self {
7543 18
        $direction = $this->getDirection($direction);
7544
7545 18
        switch ($direction) {
7546 18
            case 'desc':
7547
            case \SORT_DESC:
7548 6
                \krsort($elements, $strategy);
7549
7550 6
                break;
7551 13
            case 'asc':
7552 13
            case \SORT_ASC:
7553
            default:
7554 13
                \ksort($elements, $strategy);
7555
        }
7556
7557 18
        return $this;
7558
    }
7559
7560
    /**
7561
     * @param array      $elements  <p>Warning: used as reference</p>
7562
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7563
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7564
     *                              <strong>SORT_NATURAL</strong></p>
7565
     * @param bool       $keepKeys
7566
     *
7567
     * @return $this
7568
     *               <p>(Mutable) Return this Arrayy object.</p>
7569
     *
7570
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7571
     * @psalm-return static<TKey,T>
7572
     */
7573 24
    protected function sorting(
7574
        array &$elements,
7575
        $direction = \SORT_ASC,
7576
        int $strategy = \SORT_REGULAR,
7577
        bool $keepKeys = false
7578
    ): self {
7579 24
        $direction = $this->getDirection($direction);
7580
7581 24
        if (!$strategy) {
7582 24
            $strategy = \SORT_REGULAR;
7583
        }
7584
7585 24
        switch ($direction) {
7586 24
            case 'desc':
7587
            case \SORT_DESC:
7588 13
                if ($keepKeys) {
7589 9
                    \arsort($elements, $strategy);
7590
                } else {
7591 4
                    \rsort($elements, $strategy);
7592
                }
7593
7594 13
                break;
7595 11
            case 'asc':
7596 11
            case \SORT_ASC:
7597
            default:
7598 11
                if ($keepKeys) {
7599 4
                    \asort($elements, $strategy);
7600
                } else {
7601 7
                    \sort($elements, $strategy);
7602
                }
7603
        }
7604
7605 24
        return $this;
7606
    }
7607
7608
    /**
7609
     * @param array $array
7610
     *
7611
     * @return array
7612
     *
7613
     * @psalm-mutation-free
7614
     */
7615 25
    private function getArrayRecursiveHelperArrayy(array $array)
7616
    {
7617 25
        if ($array === []) {
7618
            return [];
7619
        }
7620
7621 25
        \array_walk_recursive(
7622 25
            $array,
7623
            /**
7624
             * @param array|self $item
7625
             *
7626
             * @return void
7627
             */
7628
            static function (&$item) {
7629 25
                if ($item instanceof self) {
7630 1
                    $item = $item->getArray();
7631
                }
7632 25
            }
7633
        );
7634
7635 25
        return $array;
7636
    }
7637
7638
    /**
7639
     * @param int|string|null $key
7640
     * @param mixed           $value
7641
     *
7642
     * @return void
7643
     */
7644 112
    private function checkType($key, $value)
7645
    {
7646
        if (
7647 112
            $key !== null
7648
            &&
7649 112
            isset($this->properties[$key]) === false
7650
            &&
7651 112
            $this->checkPropertiesMismatch === true
7652
        ) {
7653
            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...
7654
        }
7655
7656 112
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7657 97
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7658 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7659 24
            $this->properties[$key]->checkType($value);
7660
        }
7661 110
    }
7662
}
7663