Completed
Push — master ( 4be620...1c1d16 )
by Lars
01:37
created

Arrayy::add()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

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

This error could be the result of:

1. Missing dependencies

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

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

2. Missing use statement

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

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

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

Loading history...
7322
                return (array) $data->jsonSerialize();
7323
            }
7324
7325 69
            if (\method_exists($data, '__toArray')) {
7326
                return (array) $data->__toArray();
7327
            }
7328
7329 69
            if (\method_exists($data, '__toString')) {
7330
                return [(string) $data];
7331
            }
7332
        }
7333
7334 76
        if (\is_callable($data)) {
7335
            /**
7336
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7337
             */
7338 67
            $this->generator = new ArrayyRewindableGenerator($data);
7339
7340 67
            return [];
7341
        }
7342
7343 11
        if (\is_scalar($data)) {
7344 9
            return [$data];
7345
        }
7346
7347 2
        return null;
7348
    }
7349
7350
    /**
7351
     * Internal mechanics of remove method.
7352
     *
7353
     * @param mixed $key
7354
     *
7355
     * @return bool
7356
     */
7357 22
    protected function internalRemove($key): bool
7358
    {
7359 22
        $this->generatorToArray();
7360
7361
        if (
7362 22
            $this->pathSeparator
7363
            &&
7364 22
            (string) $key === $key
7365
            &&
7366 22
            \strpos($key, $this->pathSeparator) !== false
7367
        ) {
7368
            $path = \explode($this->pathSeparator, (string) $key);
7369
7370
            if ($path !== false) {
7371
                // crawl though the keys
7372
                while (\count($path, \COUNT_NORMAL) > 1) {
7373
                    $key = \array_shift($path);
7374
7375
                    if (!$this->has($key)) {
7376
                        return false;
7377
                    }
7378
7379
                    $this->array = &$this->array[$key];
7380
                }
7381
7382
                $key = \array_shift($path);
7383
            }
7384
        }
7385
7386 22
        unset($this->array[$key]);
7387
7388 22
        return true;
7389
    }
7390
7391
    /**
7392
     * Internal mechanic of set method.
7393
     *
7394
     * @param int|string|null $key
7395
     * @param mixed           $value
7396
     * @param bool            $checkProperties
7397
     *
7398
     * @return bool
7399
     */
7400 1058
    protected function internalSet(
7401
        $key,
7402
        &$value,
7403
        bool $checkProperties = true
7404
    ): bool {
7405
        if (
7406 1058
            $checkProperties === true
7407
            &&
7408 1058
            $this->properties !== []
7409
        ) {
7410 113
            $this->checkType($key, $value);
7411
        }
7412
7413 1056
        if ($key === null) {
7414
            return false;
7415
        }
7416
7417 1056
        $this->generatorToArray();
7418
7419
        /** @psalm-var array<int|string,mixed> $array */
7420 1056
        $array = &$this->array;
7421
7422
        /**
7423
         * https://github.com/vimeo/psalm/issues/2536
7424
         *
7425
         * @psalm-suppress PossiblyInvalidArgument
7426
         * @psalm-suppress InvalidScalarArgument
7427
         */
7428
        if (
7429 1056
            $this->pathSeparator
7430
            &&
7431 1056
            (string) $key === $key
7432
            &&
7433 1056
            \strpos($key, $this->pathSeparator) !== false
7434
        ) {
7435 9
            $path = \explode($this->pathSeparator, (string) $key);
7436
7437 9
            if ($path !== false) {
7438
                // crawl through the keys
7439 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7440 9
                    $key = \array_shift($path);
7441
7442 9
                    $array = &$array[$key];
7443
                }
7444
7445 9
                $key = \array_shift($path);
7446
            }
7447
        }
7448
7449 1056
        if ($array === null) {
7450 4
            $array = [];
7451 1053
        } elseif (!\is_array($array)) {
7452 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7453
        }
7454
7455 1056
        $array[$key] = $value;
7456
7457 1056
        return true;
7458
    }
7459
7460
    /**
7461
     * Convert a object into an array.
7462
     *
7463
     * @param mixed|object $object
7464
     *
7465
     * @return array|mixed
7466
     *
7467
     * @psalm-mutation-free
7468
     */
7469 5
    protected static function objectToArray($object)
7470
    {
7471 5
        if (!\is_object($object)) {
7472 4
            return $object;
7473
        }
7474
7475 5
        $object = \get_object_vars($object);
7476
7477
        /**
7478
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7479
         */
7480 5
        return \array_map(['static', 'objectToArray'], $object);
7481
    }
7482
7483
    /**
7484
     * @param array $data
7485
     * @param bool  $checkPropertiesInConstructor
7486
     *
7487
     * @return void
7488
     *
7489
     * @psalm-param array<mixed,T> $data
7490
     */
7491 1206
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7492
    {
7493 1206
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7494
                                        &&
7495 1206
                                        $checkPropertiesInConstructor === true;
7496
7497 1206
        if ($this->properties !== []) {
7498 100
            foreach ($data as $key => &$valueInner) {
7499 100
                $this->internalSet(
7500 100
                    $key,
7501 100
                    $valueInner,
7502 100
                    $checkPropertiesInConstructor
7503
                );
7504
            }
7505
        } else {
7506
            if (
7507 1125
                $this->checkPropertyTypes === true
7508
                ||
7509 1125
                $checkPropertiesInConstructor === true
7510
            ) {
7511 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7512
            }
7513
7514
            /** @var TypeCheckInterface[] $properties */
7515 1125
            $properties = $this->properties;
7516
7517
            if (
7518 1125
                $this->checkPropertiesMismatchInConstructor === true
7519
                &&
7520 1125
                \count($data) !== 0
7521
                &&
7522 1125
                \count(\array_diff_key($properties, $data)) > 0
7523
            ) {
7524 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...
7525
            }
7526
7527 1124
            foreach ($data as $key => &$valueInner) {
7528 953
                $this->internalSet(
7529 953
                    $key,
7530 953
                    $valueInner,
7531 953
                    $checkPropertiesInConstructor
7532
                );
7533
            }
7534
        }
7535 1198
    }
7536
7537
    /**
7538
     * sorting keys
7539
     *
7540
     * @param array      $elements
7541
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7542
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7543
     *                              <strong>SORT_NATURAL</strong></p>
7544
     *
7545
     * @return $this
7546
     *               <p>(Mutable) Return this Arrayy object.</p>
7547
     *
7548
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7549
     * @psalm-return static<TKey,T>
7550
     */
7551 18
    protected function sorterKeys(
7552
        array &$elements,
7553
        $direction = \SORT_ASC,
7554
        int $strategy = \SORT_REGULAR
7555
    ): self {
7556 18
        $direction = $this->getDirection($direction);
7557
7558 18
        switch ($direction) {
7559 18
            case 'desc':
7560
            case \SORT_DESC:
7561 6
                \krsort($elements, $strategy);
7562
7563 6
                break;
7564 13
            case 'asc':
7565 13
            case \SORT_ASC:
7566
            default:
7567 13
                \ksort($elements, $strategy);
7568
        }
7569
7570 18
        return $this;
7571
    }
7572
7573
    /**
7574
     * @param array      $elements  <p>Warning: used as reference</p>
7575
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7576
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7577
     *                              <strong>SORT_NATURAL</strong></p>
7578
     * @param bool       $keepKeys
7579
     *
7580
     * @return $this
7581
     *               <p>(Mutable) Return this Arrayy object.</p>
7582
     *
7583
     * @psalm-param array<mixed,mixed>|array<mixed|TKey,T> $elements
7584
     * @psalm-return static<TKey,T>
7585
     */
7586 24
    protected function sorting(
7587
        array &$elements,
7588
        $direction = \SORT_ASC,
7589
        int $strategy = \SORT_REGULAR,
7590
        bool $keepKeys = false
7591
    ): self {
7592 24
        $direction = $this->getDirection($direction);
7593
7594 24
        if (!$strategy) {
7595 24
            $strategy = \SORT_REGULAR;
7596
        }
7597
7598 24
        switch ($direction) {
7599 24
            case 'desc':
7600
            case \SORT_DESC:
7601 13
                if ($keepKeys) {
7602 9
                    \arsort($elements, $strategy);
7603
                } else {
7604 4
                    \rsort($elements, $strategy);
7605
                }
7606
7607 13
                break;
7608 11
            case 'asc':
7609 11
            case \SORT_ASC:
7610
            default:
7611 11
                if ($keepKeys) {
7612 4
                    \asort($elements, $strategy);
7613
                } else {
7614 7
                    \sort($elements, $strategy);
7615
                }
7616
        }
7617
7618 24
        return $this;
7619
    }
7620
7621
    /**
7622
     * @param array $array
7623
     *
7624
     * @return array
7625
     *
7626
     * @psalm-mutation-free
7627
     */
7628 25
    private function getArrayRecursiveHelperArrayy(array $array)
7629
    {
7630 25
        if ($array === []) {
7631
            return [];
7632
        }
7633
7634 25
        \array_walk_recursive(
7635 25
            $array,
7636
            /**
7637
             * @param array|self $item
7638
             *
7639
             * @return void
7640
             */
7641
            static function (&$item) {
7642 25
                if ($item instanceof self) {
7643 1
                    $item = $item->getArray();
7644
                }
7645 25
            }
7646
        );
7647
7648 25
        return $array;
7649
    }
7650
7651
    /**
7652
     * @param int|string|null $key
7653
     * @param mixed           $value
7654
     *
7655
     * @return void
7656
     */
7657 113
    private function checkType($key, $value)
7658
    {
7659
        if (
7660 113
            $key !== null
7661
            &&
7662 113
            isset($this->properties[$key]) === false
7663
            &&
7664 113
            $this->checkPropertiesMismatch === true
7665
        ) {
7666
            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...
7667
        }
7668
7669 113
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7670 98
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7671 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7672 24
            $this->properties[$key]->checkType($value);
7673
        }
7674 111
    }
7675
}
7676