Completed
Push — master ( e8a46f...e8310f )
by Lars
02:03
created

Arrayy::toArray()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

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

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 128
    public function &__get($key)
209
    {
210 128
        $return = $this->get($key, null, null, true);
211
212 128
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 128
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     *
231
     * @psalm-mutation-free
232
     */
233 13
    public function add($value, $key = null)
234
    {
235 13
        if ($key !== null) {
236 5
            $get = $this->get($key);
237 5
            if ($get !== null) {
238 1
                $value = \array_merge_recursive(
239 1
                    !$get instanceof self ? [$get] : $get->getArray(),
240 1
                    !\is_array($value) ? [$value] : $value
241
                );
242
            }
243
244 5
            $this->internalSet($key, $value);
245
246 4
            return $this;
247
        }
248
249 8
        return $this->append($value);
250
    }
251
252
    /**
253
     * Append a (key) + value to the current array.
254
     *
255
     * EXAMPLE: <code>
256
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
257
     * </code>
258
     *
259
     * @param mixed $value
260
     * @param mixed $key
261
     *
262
     * @return $this
263
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
264
     *
265
     * @psalm-return static<TKey,T>
266
     */
267 20
    public function append($value, $key = null): self
268
    {
269 20
        $this->generatorToArray();
270
271 20
        if ($this->properties !== []) {
272 6
            $this->checkType($key, $value);
273
        }
274
275 19
        if ($key !== null) {
276
            if (
277 2
                isset($this->array[$key])
278
                &&
279 2
                \is_array($this->array[$key])
280
            ) {
281
                $this->array[$key][] = $value;
282
            } else {
283 2
                $this->array[$key] = $value;
284
            }
285
        } else {
286 17
            $this->array[] = $value;
287
        }
288
289 19
        return $this;
290
    }
291
292
    /**
293
     * Append a (key) + value to the current array.
294
     *
295
     * EXAMPLE: <code>
296
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
297
     * </code>
298
     *
299
     * @param mixed $value
300
     * @param mixed $key
301
     *
302
     * @return $this
303
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
304
     *
305
     * @psalm-return static<TKey,T>
306
     * @psalm-mutation-free
307
     */
308 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 1
        $generator = function () use ($key, $value): \Generator {
311 1
            if ($this->properties !== []) {
312
                $this->checkType($key, $value);
313
            }
314
315
            /** @noinspection YieldFromCanBeUsedInspection - FP */
316 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
317 1
                yield $keyOld => $itemOld;
318
            }
319
320 1
            if ($key !== null) {
321
                yield $key => $value;
322
            } else {
323 1
                yield $value;
324
            }
325 1
        };
326
327 1
        return static::create(
328 1
            $generator,
329 1
            $this->iteratorClass,
330 1
            false
331
        );
332
    }
333
334
    /**
335
     * Sort the entries by value.
336
     *
337
     * @param int $sort_flags [optional] <p>
338
     *                        You may modify the behavior of the sort using the optional
339
     *                        parameter sort_flags, for details
340
     *                        see sort.
341
     *                        </p>
342
     *
343
     * @return $this
344
     *               <p>(Mutable) Return this Arrayy object.</p>
345
     *
346
     * @psalm-return static<TKey,T>
347
     */
348 4
    public function asort(int $sort_flags = 0): self
349
    {
350 4
        $this->generatorToArray();
351
352 4
        \asort($this->array, $sort_flags);
353
354 4
        return $this;
355
    }
356
357
    /**
358
     * Sort the entries by value.
359
     *
360
     * @param int $sort_flags [optional] <p>
361
     *                        You may modify the behavior of the sort using the optional
362
     *                        parameter sort_flags, for details
363
     *                        see sort.
364
     *                        </p>
365
     *
366
     * @return $this
367
     *               <p>(Immutable) Return this Arrayy object.</p>
368
     *
369
     * @psalm-return static<TKey,T>
370
     * @psalm-mutation-free
371
     */
372 4
    public function asortImmutable(int $sort_flags = 0): self
373
    {
374 4
        $that = clone $this;
375
376
        /**
377
         * @psalm-suppress ImpureMethodCall - object is already cloned
378
         */
379 4
        $that->asort($sort_flags);
380
381 4
        return $that;
382
    }
383
384
    /**
385
     * Counts all elements in an array, or something in an object.
386
     *
387
     * EXAMPLE: <code>
388
     * a([-9, -8, -7, 1.32])->count(); // 4
389
     * </code>
390
     *
391
     * <p>
392
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
393
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
394
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
395
     * implemented and used in PHP.
396
     * </p>
397
     *
398
     * @see http://php.net/manual/en/function.count.php
399
     *
400
     * @param int $mode [optional] If the optional mode parameter is set to
401
     *                  COUNT_RECURSIVE (or 1), count
402
     *                  will recursively count the array. This is particularly useful for
403
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
404
     *
405
     * @return int
406
     *             <p>
407
     *             The number of elements in var, which is
408
     *             typically an array, since anything else will have one
409
     *             element.
410
     *             </p>
411
     *             <p>
412
     *             If var is not an array or an object with
413
     *             implemented Countable interface,
414
     *             1 will be returned.
415
     *             There is one exception, if var is &null;,
416
     *             0 will be returned.
417
     *             </p>
418
     *             <p>
419
     *             Caution: count may return 0 for a variable that isn't set,
420
     *             but it may also return 0 for a variable that has been initialized with an
421
     *             empty array. Use isset to test if a variable is set.
422
     *             </p>
423
     * @psalm-mutation-free
424
     */
425 147
    public function count(int $mode = \COUNT_NORMAL): int
426
    {
427
        if (
428 147
            $this->generator
429
            &&
430 147
            $mode === \COUNT_NORMAL
431
        ) {
432 4
            return \iterator_count($this->generator);
433
        }
434
435 143
        return \count($this->toArray(), $mode);
436
    }
437
438
    /**
439
     * Exchange the array for another one.
440
     *
441
     * @param array|static $data
442
     *
443
     * @return array
444
     *
445
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
446
     * @psalm-return array<mixed,mixed>|array<TKey,T>
447
     */
448 1
    public function exchangeArray($data): array
449
    {
450 1
        $this->array = $this->fallbackForArray($data);
451
452 1
        return $this->array;
453
    }
454
455
    /**
456
     * Creates a copy of the ArrayyObject.
457
     *
458
     * @return array
459
     *
460
     * @psalm-return array<mixed,mixed>|array<TKey,T>
461
     */
462 6
    public function getArrayCopy(): array
463
    {
464 6
        $this->generatorToArray();
465
466 6
        return $this->array;
467
    }
468
469
    /**
470
     * Returns a new iterator, thus implementing the \Iterator interface.
471
     *
472
     * EXAMPLE: <code>
473
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
474
     * </code>
475
     *
476
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
477
     *                          <p>An iterator for the values in the array.</p>
478
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
479
     */
480 27
    public function getIterator(): \Iterator
481
    {
482 27
        if ($this->generator instanceof ArrayyRewindableGenerator) {
483 1
            return $this->generator;
484
        }
485
486 26
        $iterator = $this->getIteratorClass();
487
488 26
        if ($iterator === ArrayyIterator::class) {
489 26
            return new $iterator($this->toArray(), 0, static::class);
490
        }
491
492
        $return = new $iterator($this->toArray());
493
        \assert($return instanceof \Iterator);
494
495
        return $return;
496
    }
497
498
    /**
499
     * Gets the iterator classname for the ArrayObject.
500
     *
501
     * @return string
502
     *
503
     * @psalm-return class-string
504
     */
505 26
    public function getIteratorClass(): string
506
    {
507 26
        return $this->iteratorClass;
508
    }
509
510
    /**
511
     * Sort the entries by key.
512
     *
513
     * @param int $sort_flags [optional] <p>
514
     *                        You may modify the behavior of the sort using the optional
515
     *                        parameter sort_flags, for details
516
     *                        see sort.
517
     *                        </p>
518
     *
519
     * @return $this
520
     *               <p>(Mutable) Return this Arrayy object.</p>
521
     *
522
     * @psalm-return static<TKey,T>
523
     */
524 4
    public function ksort(int $sort_flags = 0): self
525
    {
526 4
        $this->generatorToArray();
527
528 4
        \ksort($this->array, $sort_flags);
529
530 4
        return $this;
531
    }
532
533
    /**
534
     * Sort the entries by key.
535
     *
536
     * @param int $sort_flags [optional] <p>
537
     *                        You may modify the behavior of the sort using the optional
538
     *                        parameter sort_flags, for details
539
     *                        see sort.
540
     *                        </p>
541
     *
542
     * @return $this
543
     *               <p>(Immutable) Return this Arrayy object.</p>
544
     *
545
     * @psalm-return static<TKey,T>
546
     */
547 4
    public function ksortImmutable(int $sort_flags = 0): self
548
    {
549 4
        $that = clone $this;
550
551
        /**
552
         * @psalm-suppress ImpureMethodCall - object is already cloned
553
         */
554 4
        $that->ksort($sort_flags);
555
556 4
        return $that;
557
    }
558
559
    /**
560
     * Sort an array using a case insensitive "natural order" algorithm.
561
     *
562
     * @return $this
563
     *               <p>(Mutable) Return this Arrayy object.</p>
564
     *
565
     * @psalm-return static<TKey,T>
566
     */
567 8
    public function natcasesort(): self
568
    {
569 8
        $this->generatorToArray();
570
571 8
        \natcasesort($this->array);
572
573 8
        return $this;
574
    }
575
576
    /**
577
     * Sort an array using a case insensitive "natural order" algorithm.
578
     *
579
     * @return $this
580
     *               <p>(Immutable) Return this Arrayy object.</p>
581
     *
582
     * @psalm-return static<TKey,T>
583
     * @psalm-mutation-free
584
     */
585 4
    public function natcasesortImmutable(): self
586
    {
587 4
        $that = clone $this;
588
589
        /**
590
         * @psalm-suppress ImpureMethodCall - object is already cloned
591
         */
592 4
        $that->natcasesort();
593
594 4
        return $that;
595
    }
596
597
    /**
598
     * Sort entries using a "natural order" algorithm.
599
     *
600
     * @return $this
601
     *               <p>(Mutable) Return this Arrayy object.</p>
602
     *
603
     * @psalm-return static<TKey,T>
604
     */
605 10
    public function natsort(): self
606
    {
607 10
        $this->generatorToArray();
608
609 10
        \natsort($this->array);
610
611 10
        return $this;
612
    }
613
614
    /**
615
     * Sort entries using a "natural order" algorithm.
616
     *
617
     * @return $this
618
     *               <p>(Immutable) Return this Arrayy object.</p>
619
     *
620
     * @psalm-return static<TKey,T>
621
     * @psalm-mutation-free
622
     */
623 4
    public function natsortImmutable(): self
624
    {
625 4
        $that = clone $this;
626
627
        /**
628
         * @psalm-suppress ImpureMethodCall - object is already cloned
629
         */
630 4
        $that->natsort();
631
632 4
        return $that;
633
    }
634
635
    /**
636
     * Whether or not an offset exists.
637
     *
638
     * @param bool|int|string $offset
639
     *
640
     * @return bool
641
     *
642
     * @noinspection PhpSillyAssignmentInspection
643
     *
644
     * @psalm-mutation-free
645
     */
646 159
    public function offsetExists($offset): bool
647
    {
648 159
        $this->generatorToArray();
649
650 159
        if ($this->array === []) {
651 8
            return false;
652
        }
653
654
        // php cast "bool"-index into "int"-index
655 153
        if ((bool) $offset === $offset) {
656 1
            $offset = (int) $offset;
657
        }
658
659
        /** @var int|string $offset - hint for phpstan */
660 153
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

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

This assignement can be removed without consequences.

Loading history...
661
662 153
        $tmpReturn = $this->keyExists($offset);
663
664
        if (
665 153
            $tmpReturn === true
666
            ||
667 153
            \strpos((string) $offset, $this->pathSeparator) === false
668
        ) {
669 150
            return $tmpReturn;
670
        }
671
672 4
        $offsetExists = false;
673
674
        /**
675
         * https://github.com/vimeo/psalm/issues/2536
676
         *
677
         * @psalm-suppress PossiblyInvalidArgument
678
         * @psalm-suppress InvalidScalarArgument
679
         */
680 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
681 4
            $this->pathSeparator
682
            &&
683 4
            (string) $offset === $offset
684
            &&
685 4
            \strpos($offset, $this->pathSeparator) !== false
686
        ) {
687 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
688 4
            if ($explodedPath !== false) {
689
                /** @var string $lastOffset - helper for phpstan */
690 4
                $lastOffset = \array_pop($explodedPath);
691 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
692
693
                /**
694
                 * @psalm-suppress MissingClosureReturnType
695
                 * @psalm-suppress MissingClosureParamType
696
                 */
697 4
                $this->callAtPath(
698 4
                    $containerPath,
699 4
                    static function ($container) use ($lastOffset, &$offsetExists) {
700 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
701 4
                    }
702
                );
703
            }
704
        }
705
706 4
        return $offsetExists;
707
    }
708
709
    /**
710
     * Returns the value at specified offset.
711
     *
712
     * @param int|string $offset
713
     *
714
     * @return mixed
715
     *               <p>Will return null if the offset did not exists.</p>
716
     */
717 128
    public function &offsetGet($offset)
718
    {
719
        // init
720 128
        $value = null;
721
722 128
        if ($this->offsetExists($offset)) {
723 126
            $value = &$this->__get($offset);
724
        }
725
726 128
        return $value;
727
    }
728
729
    /**
730
     * Assigns a value to the specified offset + check the type.
731
     *
732
     * @param int|string|null $offset
733
     * @param mixed           $value
734
     *
735
     * @return void
736
     */
737 28
    public function offsetSet($offset, $value)
738
    {
739 28
        $this->generatorToArray();
740
741 28
        if ($offset === null) {
742 7
            if ($this->properties !== []) {
743 2
                $this->checkType(null, $value);
744
            }
745
746 6
            $this->array[] = $value;
747
        } else {
748 21
            $this->internalSet(
749 21
                $offset,
750 21
                $value,
751 21
                true
752
            );
753
        }
754 27
    }
755
756
    /**
757
     * Unset an offset.
758
     *
759
     * @param int|string $offset
760
     *
761
     * @return void
762
     *              <p>(Mutable) Return nothing.</p>
763
     */
764 25
    public function offsetUnset($offset)
765
    {
766 25
        $this->generatorToArray();
767
768 25
        if ($this->array === []) {
769 6
            return;
770
        }
771
772 20
        if ($this->keyExists($offset)) {
773 13
            unset($this->array[$offset]);
774
775 13
            return;
776
        }
777
778
        /**
779
         * https://github.com/vimeo/psalm/issues/2536
780
         *
781
         * @psalm-suppress PossiblyInvalidArgument
782
         * @psalm-suppress InvalidScalarArgument
783
         */
784 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
785 10
            $this->pathSeparator
786
            &&
787 10
            (string) $offset === $offset
788
            &&
789 10
            \strpos($offset, $this->pathSeparator) !== false
790
        ) {
791 7
            $path = \explode($this->pathSeparator, (string) $offset);
792
793 7
            if ($path !== false) {
794 7
                $pathToUnset = \array_pop($path);
795
796
                /**
797
                 * @psalm-suppress MissingClosureReturnType
798
                 * @psalm-suppress MissingClosureParamType
799
                 */
800 7
                $this->callAtPath(
801 7
                    \implode($this->pathSeparator, $path),
802 7
                    static function (&$offset) use ($pathToUnset) {
803 6
                        if (\is_array($offset)) {
804 5
                            unset($offset[$pathToUnset]);
805
                        } else {
806 1
                            $offset = null;
807
                        }
808 7
                    }
809
                );
810
            }
811
        }
812
813 10
        unset($this->array[$offset]);
814 10
    }
815
816
    /**
817
     * Serialize the current "Arrayy"-object.
818
     *
819
     * EXAMPLE: <code>
820
     * a([1, 4, 7])->serialize();
821
     * </code>
822
     *
823
     * @return string
824
     */
825 2
    public function serialize(): string
826
    {
827 2
        $this->generatorToArray();
828
829 2
        if (\PHP_VERSION_ID < 70400) {
830 2
            return parent::serialize();
831
        }
832
833
        return \serialize($this);
834
    }
835
836
    /**
837
     * Sets the iterator classname for the current "Arrayy"-object.
838
     *
839
     * @param string $iteratorClass
840
     *
841
     * @throws \InvalidArgumentException
842
     *
843
     * @return void
844
     *
845
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
846
     */
847 1196
    public function setIteratorClass($iteratorClass)
848
    {
849 1196
        if (\class_exists($iteratorClass)) {
850 1196
            $this->iteratorClass = $iteratorClass;
851
852 1196
            return;
853
        }
854
855
        if (\strpos($iteratorClass, '\\') === 0) {
856
            $iteratorClass = '\\' . $iteratorClass;
857
            if (\class_exists($iteratorClass)) {
858
                /**
859
                 * @psalm-suppress PropertyTypeCoercion
860
                 */
861
                $this->iteratorClass = $iteratorClass;
862
863
                return;
864
            }
865
        }
866
867
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
868
    }
869
870
    /**
871
     * Sort the entries with a user-defined comparison function and maintain key association.
872
     *
873
     * @param callable $function
874
     *
875
     * @throws \InvalidArgumentException
876
     *
877
     * @return $this
878
     *               <p>(Mutable) Return this Arrayy object.</p>
879
     *
880
     * @psalm-return static<TKey,T>
881
     */
882 8 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
883
    {
884 8
        if (!\is_callable($function)) {
885
            throw new \InvalidArgumentException('Passed function must be callable');
886
        }
887
888 8
        $this->generatorToArray();
889
890 8
        \uasort($this->array, $function);
891
892 8
        return $this;
893
    }
894
895
    /**
896
     * Sort the entries with a user-defined comparison function and maintain key association.
897
     *
898
     * @param callable $function
899
     *
900
     * @throws \InvalidArgumentException
901
     *
902
     * @return $this
903
     *               <p>(Immutable) Return this Arrayy object.</p>
904
     *
905
     * @psalm-return static<TKey,T>
906
     * @psalm-mutation-free
907
     */
908 4
    public function uasortImmutable($function): self
909
    {
910 4
        $that = clone $this;
911
912
        /**
913
         * @psalm-suppress ImpureMethodCall - object is already cloned
914
         */
915 4
        $that->uasort($function);
916
917 4
        return $that;
918
    }
919
920
    /**
921
     * Sort the entries by keys using a user-defined comparison function.
922
     *
923
     * @param callable $function
924
     *
925
     * @throws \InvalidArgumentException
926
     *
927
     * @return static
928
     *                <p>(Mutable) Return this Arrayy object.</p>
929
     *
930
     * @psalm-return static<TKey,T>
931
     */
932 5
    public function uksort($function): self
933
    {
934 5
        return $this->customSortKeys($function);
935
    }
936
937
    /**
938
     * Sort the entries by keys using a user-defined comparison function.
939
     *
940
     * @param callable $function
941
     *
942
     * @throws \InvalidArgumentException
943
     *
944
     * @return static
945
     *                <p>(Immutable) Return this Arrayy object.</p>
946
     *
947
     * @psalm-return static<TKey,T>
948
     * @psalm-mutation-free
949
     */
950 1
    public function uksortImmutable($function): self
951
    {
952 1
        return $this->customSortKeysImmutable($function);
953
    }
954
955
    /**
956
     * Unserialize an string and return the instance of the "Arrayy"-class.
957
     *
958
     * EXAMPLE: <code>
959
     * $serialized = a([1, 4, 7])->serialize();
960
     * a()->unserialize($serialized);
961
     * </code>
962
     *
963
     * @param string $string
964
     *
965
     * @return $this
966
     *
967
     * @psalm-return static<TKey,T>
968
     */
969 2
    public function unserialize($string): self
970
    {
971 2
        if (\PHP_VERSION_ID < 70400) {
972 2
            parent::unserialize($string);
973
974 2
            return $this;
975
        }
976
977
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
978
    }
979
980
    /**
981
     * Append a (key) + values to the current array.
982
     *
983
     * EXAMPLE: <code>
984
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
985
     * </code>
986
     *
987
     * @param array $values
988
     * @param mixed $key
989
     *
990
     * @return $this
991
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
992
     *
993
     * @psalm-param  array<mixed,T> $values
994
     * @psalm-param  TKey|null $key
995
     * @psalm-return static<TKey,T>
996
     */
997 1
    public function appendArrayValues(array $values, $key = null)
998
    {
999 1
        $this->generatorToArray();
1000
1001 1
        if ($key !== null) {
1002
            if (
1003 1
                isset($this->array[$key])
1004
                &&
1005 1
                \is_array($this->array[$key])
1006
            ) {
1007 1
                foreach ($values as $value) {
1008 1
                    $this->array[$key][] = $value;
1009
                }
1010
            } else {
1011
                foreach ($values as $value) {
1012 1
                    $this->array[$key] = $value;
1013
                }
1014
            }
1015
        } else {
1016
            foreach ($values as $value) {
1017
                $this->array[] = $value;
1018
            }
1019
        }
1020
1021 1
        return $this;
1022
    }
1023
1024
    /**
1025
     * Add a suffix to each key.
1026
     *
1027
     * @param mixed $prefix
1028
     *
1029
     * @return static
1030
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1031
     *
1032
     * @psalm-return static<TKey,T>
1033
     * @psalm-mutation-free
1034
     */
1035 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1036
    {
1037
        // init
1038 10
        $result = [];
1039
1040 10
        foreach ($this->getGenerator() as $key => $item) {
1041 9
            if ($item instanceof self) {
1042
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1043 9
            } elseif (\is_array($item)) {
1044
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1045
                    ->appendToEachKey($prefix)
1046
                    ->toArray();
1047
            } else {
1048 9
                $result[$prefix . $key] = $item;
1049
            }
1050
        }
1051
1052 10
        return self::create($result, $this->iteratorClass, false);
1053
    }
1054
1055
    /**
1056
     * Add a prefix to each value.
1057
     *
1058
     * @param mixed $prefix
1059
     *
1060
     * @return static
1061
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1062
     *
1063
     * @psalm-return static<TKey,T>
1064
     * @psalm-mutation-free
1065
     */
1066 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1067
    {
1068
        // init
1069 10
        $result = [];
1070
1071 10
        foreach ($this->getGenerator() as $key => $item) {
1072 9
            if ($item instanceof self) {
1073
                $result[$key] = $item->appendToEachValue($prefix);
1074 9
            } elseif (\is_array($item)) {
1075
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1076 9
            } elseif (\is_object($item) === true) {
1077 1
                $result[$key] = $item;
1078
            } else {
1079 9
                $result[$key] = $prefix . $item;
1080
            }
1081
        }
1082
1083 10
        return self::create($result, $this->iteratorClass, false);
1084
    }
1085
1086
    /**
1087
     * Sort an array in reverse order and maintain index association.
1088
     *
1089
     * @return $this
1090
     *               <p>(Mutable) Return this Arrayy object.</p>
1091
     *
1092
     * @psalm-return static<TKey,T>
1093
     */
1094 4
    public function arsort(): self
1095
    {
1096 4
        $this->generatorToArray();
1097
1098 4
        \arsort($this->array);
1099
1100 4
        return $this;
1101
    }
1102
1103
    /**
1104
     * Sort an array in reverse order and maintain index association.
1105
     *
1106
     * @return $this
1107
     *               <p>(Immutable) Return this Arrayy object.</p>
1108
     *
1109
     * @psalm-return static<TKey,T>
1110
     * @psalm-mutation-free
1111
     */
1112 10
    public function arsortImmutable(): self
1113
    {
1114 10
        $that = clone $this;
1115
1116 10
        $that->generatorToArray();
1117
1118 10
        \arsort($that->array);
1119
1120 10
        return $that;
1121
    }
1122
1123
    /**
1124
     * Iterate over the current array and execute a callback for each loop.
1125
     *
1126
     * EXAMPLE: <code>
1127
     * $result = A::create();
1128
     * $closure = function ($value, $key) use ($result) {
1129
     *     $result[$key] = ':' . $value . ':';
1130
     * };
1131
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1132
     * </code>
1133
     *
1134
     * @param \Closure $closure
1135
     *
1136
     * @return static
1137
     *                <p>(Immutable)</p>
1138
     *
1139
     * @psalm-return static<TKey,T>
1140
     * @psalm-mutation-free
1141
     */
1142 3
    public function at(\Closure $closure): self
1143
    {
1144 3
        $that = clone $this;
1145
1146 3
        foreach ($that->getGenerator() as $key => $value) {
1147 3
            $closure($value, $key);
1148
        }
1149
1150 3
        return static::create(
1151 3
            $that->toArray(),
1152 3
            $this->iteratorClass,
1153 3
            false
1154
        );
1155
    }
1156
1157
    /**
1158
     * Returns the average value of the current array.
1159
     *
1160
     * EXAMPLE: <code>
1161
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1162
     * </code>
1163
     *
1164
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1165
     *
1166
     * @return float|int
1167
     *                   <p>The average value.</p>
1168
     * @psalm-mutation-free
1169
     */
1170 10
    public function average($decimals = 0)
1171
    {
1172 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1173
1174 10
        if (!$count) {
1175 2
            return 0;
1176
        }
1177
1178 8
        if ((int) $decimals !== $decimals) {
1179 3
            $decimals = 0;
1180
        }
1181
1182 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1183
    }
1184
1185
    /**
1186
     * Changes all keys in an array.
1187
     *
1188
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1189
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1190
     *
1191
     * @return static
1192
     *                <p>(Immutable)</p>
1193
     *
1194
     * @psalm-return static<TKey,T>
1195
     * @psalm-mutation-free
1196
     */
1197 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1198
    {
1199
        if (
1200 1
            $case !== \CASE_LOWER
1201
            &&
1202 1
            $case !== \CASE_UPPER
1203
        ) {
1204
            $case = \CASE_LOWER;
1205
        }
1206
1207 1
        $return = [];
1208 1
        foreach ($this->getGenerator() as $key => $value) {
1209 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1210
1211 1
            if ($case === \CASE_LOWER) {
1212 1
                $key = \mb_strtolower((string) $key);
1213
            } else {
1214 1
                $key = \mb_strtoupper((string) $key);
1215
            }
1216
1217 1
            $return[$key] = $value;
1218
        }
1219
1220 1
        return static::create(
1221 1
            $return,
1222 1
            $this->iteratorClass,
1223 1
            false
1224
        );
1225
    }
1226
1227
    /**
1228
     * Change the path separator of the array wrapper.
1229
     *
1230
     * By default, the separator is: "."
1231
     *
1232
     * @param string $separator <p>Separator to set.</p>
1233
     *
1234
     * @return $this
1235
     *               <p>(Mutable) Return this Arrayy object.</p>
1236
     *
1237
     * @psalm-return static<TKey,T>
1238
     */
1239 11
    public function changeSeparator($separator): self
1240
    {
1241 11
        $this->pathSeparator = $separator;
1242
1243 11
        return $this;
1244
    }
1245
1246
    /**
1247
     * Create a chunked version of the current array.
1248
     *
1249
     * EXAMPLE: <code>
1250
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1251
     * </code>
1252
     *
1253
     * @param int  $size         <p>Size of each chunk.</p>
1254
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1255
     *
1256
     * @return static
1257
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1258
     *
1259
     * @psalm-return static<TKey,T>
1260
     * @psalm-mutation-free
1261
     */
1262 5
    public function chunk($size, $preserveKeys = false): self
1263
    {
1264 5
        if ($preserveKeys) {
1265
            $generator = function () use ($size) {
1266
                $values = [];
1267
                $tmpCounter = 0;
1268
                foreach ($this->getGenerator() as $key => $value) {
1269
                    ++$tmpCounter;
1270
1271
                    $values[$key] = $value;
1272
                    if ($tmpCounter === $size) {
1273
                        yield $values;
1274
1275
                        $values = [];
1276
                        $tmpCounter = 0;
1277
                    }
1278
                }
1279
1280
                if ($values !== []) {
1281
                    yield $values;
1282
                }
1283
            };
1284 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1285 5
            $generator = function () use ($size) {
1286 5
                $values = [];
1287 5
                $tmpCounter = 0;
1288 5
                foreach ($this->getGenerator() as $key => $value) {
1289 5
                    ++$tmpCounter;
1290
1291 5
                    $values[] = $value;
1292 5
                    if ($tmpCounter === $size) {
1293 5
                        yield $values;
1294
1295 5
                        $values = [];
1296 5
                        $tmpCounter = 0;
1297
                    }
1298
                }
1299
1300 5
                if ($values !== []) {
1301 4
                    yield $values;
1302
                }
1303 5
            };
1304
        }
1305
1306 5
        return static::create(
1307 5
            $generator,
1308 5
            $this->iteratorClass,
1309 5
            false
1310
        );
1311
    }
1312
1313
    /**
1314
     * Clean all falsy values from the current array.
1315
     *
1316
     * EXAMPLE: <code>
1317
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1318
     * </code>
1319
     *
1320
     * @return static
1321
     *                <p>(Immutable)</p>
1322
     *
1323
     * @psalm-return static<TKey,T>
1324
     * @psalm-mutation-free
1325
     */
1326 8
    public function clean(): self
1327
    {
1328 8
        return $this->filter(
1329 8
            static function ($value) {
1330 7
                return (bool) $value;
1331 8
            }
1332
        );
1333
    }
1334
1335
    /**
1336
     * WARNING!!! -> Clear the current full array or a $key of it.
1337
     *
1338
     * EXAMPLE: <code>
1339
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1340
     * </code>
1341
     *
1342
     * @param int|int[]|string|string[]|null $key
1343
     *
1344
     * @return $this
1345
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1346
     *
1347
     * @psalm-return static<TKey,T>
1348
     */
1349 10
    public function clear($key = null): self
1350
    {
1351 10
        if ($key !== null) {
1352 3
            if (\is_array($key)) {
1353 1
                foreach ($key as $keyTmp) {
1354 1
                    $this->offsetUnset($keyTmp);
1355
                }
1356
            } else {
1357 2
                $this->offsetUnset($key);
1358
            }
1359
1360 3
            return $this;
1361
        }
1362
1363 7
        $this->array = [];
1364 7
        $this->generator = null;
1365
1366 7
        return $this;
1367
    }
1368
1369
    /**
1370
     * Check if an item is in the current array.
1371
     *
1372
     * EXAMPLE: <code>
1373
     * a([1, true])->contains(true); // true
1374
     * </code>
1375
     *
1376
     * @param float|int|string $value
1377
     * @param bool             $recursive
1378
     * @param bool             $strict
1379
     *
1380
     * @return bool
1381
     * @psalm-mutation-free
1382
     */
1383 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1384
    {
1385 23
        if ($recursive === true) {
1386 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1387
        }
1388
1389
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1390 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1391 11
            if ($strict) {
1392 11
                if ($value === $valueFromArray) {
1393 11
                    return true;
1394
                }
1395
            } else {
1396
                /** @noinspection NestedPositiveIfStatementsInspection */
1397
                if ($value == $valueFromArray) {
1398 7
                    return true;
1399
                }
1400
            }
1401
        }
1402
1403 7
        return false;
1404
    }
1405
1406
    /**
1407
     * Check if an (case-insensitive) string is in the current array.
1408
     *
1409
     * EXAMPLE: <code>
1410
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1411
     * </code>
1412
     *
1413
     * @param mixed $value
1414
     * @param bool  $recursive
1415
     *
1416
     * @return bool
1417
     * @psalm-mutation-free
1418
     *
1419
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1420
     */
1421 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1422
    {
1423 26
        if ($value === null) {
1424 2
            return false;
1425
        }
1426
1427 24
        if ($recursive === true) {
1428
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1429 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1430 22
                if (\is_array($valueTmp)) {
1431 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1432 5
                    if ($return === true) {
1433 5
                        return $return;
1434
                    }
1435 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1436 22
                    return true;
1437
                }
1438
            }
1439
1440 8
            return false;
1441
        }
1442
1443
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1444 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1445 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1446 11
                return true;
1447
            }
1448
        }
1449
1450 4
        return false;
1451
    }
1452
1453
    /**
1454
     * Check if the given key/index exists in the array.
1455
     *
1456
     * EXAMPLE: <code>
1457
     * a([1 => true])->containsKey(1); // true
1458
     * </code>
1459
     *
1460
     * @param int|string $key <p>key/index to search for</p>
1461
     *
1462
     * @return bool
1463
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1464
     *
1465
     * @psalm-mutation-free
1466
     */
1467 4
    public function containsKey($key): bool
1468
    {
1469 4
        return $this->offsetExists($key);
1470
    }
1471
1472
    /**
1473
     * Check if all given needles are present in the array as key/index.
1474
     *
1475
     * EXAMPLE: <code>
1476
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1477
     * </code>
1478
     *
1479
     * @param array $needles   <p>The keys you are searching for.</p>
1480
     * @param bool  $recursive
1481
     *
1482
     * @return bool
1483
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1484
     *
1485
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1486
     * @psalm-mutation-free
1487
     */
1488 2
    public function containsKeys(array $needles, $recursive = false): bool
1489
    {
1490 2
        if ($recursive === true) {
1491
            return
1492 2
                \count(
1493 2
                    \array_intersect(
1494 2
                        $needles,
1495 2
                        $this->keys(true)->toArray()
1496
                    ),
1497 2
                    \COUNT_RECURSIVE
1498
                )
1499
                ===
1500 2
                \count(
1501 2
                    $needles,
1502 2
                    \COUNT_RECURSIVE
1503
                );
1504
        }
1505
1506 1
        return \count(
1507 1
            \array_intersect($needles, $this->keys()->toArray()),
1508 1
            \COUNT_NORMAL
1509
        )
1510
                ===
1511 1
                \count(
1512 1
                    $needles,
1513 1
                    \COUNT_NORMAL
1514
                );
1515
    }
1516
1517
    /**
1518
     * Check if all given needles are present in the array as key/index.
1519
     *
1520
     * @param array $needles <p>The keys you are searching for.</p>
1521
     *
1522
     * @return bool
1523
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1524
     *
1525
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1526
     * @psalm-mutation-free
1527
     */
1528 1
    public function containsKeysRecursive(array $needles): bool
1529
    {
1530 1
        return $this->containsKeys($needles, true);
1531
    }
1532
1533
    /**
1534
     * alias: for "Arrayy->contains()"
1535
     *
1536
     * @param float|int|string $value
1537
     *
1538
     * @return bool
1539
     *
1540
     * @see Arrayy::contains()
1541
     * @psalm-mutation-free
1542
     */
1543 9
    public function containsValue($value): bool
1544
    {
1545 9
        return $this->contains($value);
1546
    }
1547
1548
    /**
1549
     * alias: for "Arrayy->contains($value, true)"
1550
     *
1551
     * @param float|int|string $value
1552
     *
1553
     * @return bool
1554
     *
1555
     * @see Arrayy::contains()
1556
     * @psalm-mutation-free
1557
     */
1558 18
    public function containsValueRecursive($value): bool
1559
    {
1560 18
        return $this->contains($value, true);
1561
    }
1562
1563
    /**
1564
     * Check if all given needles are present in the array.
1565
     *
1566
     * EXAMPLE: <code>
1567
     * a([1, true])->containsValues(array(1, true)); // true
1568
     * </code>
1569
     *
1570
     * @param array $needles
1571
     *
1572
     * @return bool
1573
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1574
     *
1575
     * @psalm-param array<mixed>|array<T> $needles
1576
     * @psalm-mutation-free
1577
     */
1578 1
    public function containsValues(array $needles): bool
1579
    {
1580 1
        return \count(
1581 1
            \array_intersect(
1582 1
                $needles,
1583 1
                $this->toArray()
1584
            ),
1585 1
            \COUNT_NORMAL
1586
        )
1587
               ===
1588 1
               \count(
1589 1
                   $needles,
1590 1
                   \COUNT_NORMAL
1591
               );
1592
    }
1593
1594
    /**
1595
     * Counts all the values of an array
1596
     *
1597
     * @see          http://php.net/manual/en/function.array-count-values.php
1598
     *
1599
     * @return static
1600
     *                <p>
1601
     *                (Immutable)
1602
     *                An associative Arrayy-object of values from input as
1603
     *                keys and their count as value.
1604
     *                </p>
1605
     *
1606
     * @psalm-return static<TKey,T>
1607
     * @psalm-mutation-free
1608
     */
1609 7
    public function countValues(): self
1610
    {
1611 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1612
    }
1613
1614
    /**
1615
     * Creates an Arrayy object.
1616
     *
1617
     * @param mixed  $data
1618
     * @param string $iteratorClass
1619
     * @param bool   $checkPropertiesInConstructor
1620
     *
1621
     * @return static
1622
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1623
     *
1624
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1625
     *
1626
     * @psalm-mutation-free
1627
     */
1628 724
    public static function create(
1629
        $data = [],
1630
        string $iteratorClass = ArrayyIterator::class,
1631
        bool $checkPropertiesInConstructor = true
1632
    ) {
1633 724
        return new static(
1634 724
            $data,
1635 724
            $iteratorClass,
1636 724
            $checkPropertiesInConstructor
1637
        );
1638
    }
1639
1640
    /**
1641
     * Flatten an array with the given character as a key delimiter.
1642
     *
1643
     * EXAMPLE: <code>
1644
     * $callable = function ($a, $b) {
1645
     *     if ($a == $b) {
1646
     *         return 0;
1647
     *     }
1648
     *     return ($a > $b) ? 1 : -1;
1649
     * };
1650
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1651
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1652
     * </code>
1653
     *
1654
     * @param string     $delimiter
1655
     * @param string     $prepend
1656
     * @param array|null $items
1657
     *
1658
     * @return array
1659
     */
1660 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1661
    {
1662
        // init
1663 2
        $flatten = [];
1664
1665 2
        if ($items === null) {
1666 2
            $items = $this->array;
1667
        }
1668
1669 2
        foreach ($items as $key => $value) {
1670 2
            if (\is_array($value) && $value !== []) {
1671 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1672
            } else {
1673 2
                $flatten[] = [$prepend . $key => $value];
1674
            }
1675
        }
1676
1677 2
        if (\count($flatten) === 0) {
1678
            return [];
1679
        }
1680
1681 2
        return \array_merge_recursive([], ...$flatten);
1682
    }
1683
1684
    /**
1685
     * WARNING: Creates an Arrayy object by reference.
1686
     *
1687
     * @param array $array
1688
     *
1689
     * @return $this
1690
     *               <p>(Mutable) Return this Arrayy object.</p>
1691
     *
1692
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1693
     */
1694 26
    public function createByReference(array &$array = []): self
1695
    {
1696 26
        $array = $this->fallbackForArray($array);
1697
1698 26
        $this->array = &$array;
1699
1700 26
        return $this;
1701
    }
1702
1703
    /**
1704
     * Create an new instance from a callable function which will return an Generator.
1705
     *
1706
     * @param callable $generatorFunction
1707
     *
1708
     * @return static
1709
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1710
     *
1711
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1712
     *
1713
     * @psalm-mutation-free
1714
     */
1715 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1716
    {
1717 7
        return self::create($generatorFunction);
1718
    }
1719
1720
    /**
1721
     * Create an new instance filled with a copy of values from a "Generator"-object.
1722
     *
1723
     * @param \Generator $generator
1724
     *
1725
     * @return static
1726
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1727
     *
1728
     * @psalm-param \Generator<array-key,mixed> $generator
1729
     *
1730
     * @psalm-mutation-free
1731
     */
1732 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1733
    {
1734 4
        return self::create(\iterator_to_array($generator, true));
1735
    }
1736
1737
    /**
1738
     * Create an new Arrayy object via JSON.
1739
     *
1740
     * @param string $json
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1744
     *
1745
     * @psalm-mutation-free
1746
     */
1747 5
    public static function createFromJson(string $json): self
1748
    {
1749 5
        return static::create(\json_decode($json, true));
1750
    }
1751
1752
    /**
1753
     * Create an new Arrayy object via JSON.
1754
     *
1755
     * @param array $array
1756
     *
1757
     * @return static
1758
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1759
     *
1760
     * @psalm-mutation-free
1761
     */
1762 1
    public static function createFromArray(array $array): self
1763
    {
1764 1
        return static::create($array);
1765
    }
1766
1767
    /**
1768
     * Create an new instance filled with values from an object that is iterable.
1769
     *
1770
     * @param \Traversable $object <p>iterable object</p>
1771
     *
1772
     * @return static
1773
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1774
     *
1775
     * @psalm-param \Traversable<array-key,mixed> $object
1776
     *
1777
     * @psalm-mutation-free
1778
     */
1779 4
    public static function createFromObject(\Traversable $object): self
1780
    {
1781
        // init
1782 4
        $arrayy = new static();
1783
1784 4
        if ($object instanceof self) {
1785 4
            $objectArray = $object->getGenerator();
1786
        } else {
1787
            $objectArray = $object;
1788
        }
1789
1790 4
        foreach ($objectArray as $key => $value) {
1791
            /**
1792
             * @psalm-suppress ImpureMethodCall - object is already re-created
1793
             */
1794 3
            $arrayy->internalSet($key, $value);
1795
        }
1796
1797 4
        return $arrayy;
1798
    }
1799
1800
    /**
1801
     * Create an new instance filled with values from an object.
1802
     *
1803
     * @param object $object
1804
     *
1805
     * @return static
1806
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1807
     *
1808
     * @psalm-mutation-free
1809
     */
1810 5
    public static function createFromObjectVars($object): self
1811
    {
1812 5
        return self::create(self::objectToArray($object));
1813
    }
1814
1815
    /**
1816
     * Create an new Arrayy object via string.
1817
     *
1818
     * @param string      $str       <p>The input string.</p>
1819
     * @param string|null $delimiter <p>The boundary string.</p>
1820
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1821
     *                               used.</p>
1822
     *
1823
     * @return static
1824
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1825
     *
1826
     * @psalm-mutation-free
1827
     */
1828 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1829
    {
1830 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1831 1
            \preg_match_all($regEx, $str, $array);
1832
1833 1
            if (!empty($array)) {
1834 1
                $array = $array[0];
1835
            }
1836
        } else {
1837
            /** @noinspection NestedPositiveIfStatementsInspection */
1838 9
            if ($delimiter !== null) {
1839 7
                $array = \explode($delimiter, $str);
1840
            } else {
1841 2
                $array = [$str];
1842
            }
1843
        }
1844
1845
        // trim all string in the array
1846
        /**
1847
         * @psalm-suppress MissingClosureParamType
1848
         */
1849 10
        \array_walk(
1850 10
            $array,
1851 10
            static function (&$val) {
1852 10
                if ((string) $val === $val) {
1853 10
                    $val = \trim($val);
1854
                }
1855 10
            }
1856
        );
1857
1858 10
        return static::create($array);
1859
    }
1860
1861
    /**
1862
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1863
     *
1864
     * @param \Traversable $traversable
1865
     *
1866
     * @return static
1867
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1868
     *
1869
     * @psalm-param \Traversable<array-key,mixed> $traversable
1870
     *
1871
     * @psalm-mutation-free
1872
     */
1873 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1874
    {
1875 1
        return self::create(\iterator_to_array($traversable, true));
1876
    }
1877
1878
    /**
1879
     * Create an new instance containing a range of elements.
1880
     *
1881
     * @param float|int|string $low  <p>First value of the sequence.</p>
1882
     * @param float|int|string $high <p>The sequence is ended upon reaching the end value.</p>
1883
     * @param float|int        $step <p>Used as the increment between elements in the sequence.</p>
1884
     *
1885
     * @return static
1886
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1887
     *
1888
     * @psalm-mutation-free
1889
     */
1890 2
    public static function createWithRange($low, $high, $step = 1): self
1891
    {
1892 2
        return static::create(\range($low, $high, $step));
1893
    }
1894
1895
    /**
1896
     * Gets the element of the array at the current internal iterator position.
1897
     *
1898
     * @return false|mixed
1899
     */
1900
    public function current()
1901
    {
1902
        return \current($this->array);
1903
    }
1904
1905
    /**
1906
     * Custom sort by index via "uksort".
1907
     *
1908
     * EXAMPLE: <code>
1909
     * $callable = function ($a, $b) {
1910
     *     if ($a == $b) {
1911
     *         return 0;
1912
     *     }
1913
     *     return ($a > $b) ? 1 : -1;
1914
     * };
1915
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1916
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1917
     * </code>
1918
     *
1919
     * @see          http://php.net/manual/en/function.uksort.php
1920
     *
1921
     * @param callable $function
1922
     *
1923
     * @throws \InvalidArgumentException
1924
     *
1925
     * @return $this
1926
     *               <p>(Mutable) Return this Arrayy object.</p>
1927
     *
1928
     * @psalm-return static<TKey,T>
1929
     */
1930 5
    public function customSortKeys(callable $function): self
1931
    {
1932 5
        $this->generatorToArray();
1933
1934 5
        \uksort($this->array, $function);
1935
1936 5
        return $this;
1937
    }
1938
1939
    /**
1940
     * Custom sort by index via "uksort".
1941
     *
1942
     * @see          http://php.net/manual/en/function.uksort.php
1943
     *
1944
     * @param callable $function
1945
     *
1946
     * @throws \InvalidArgumentException
1947
     *
1948
     * @return $this
1949
     *               <p>(Immutable) Return this Arrayy object.</p>
1950
     *
1951
     * @psalm-return static<TKey,T>
1952
     * @psalm-mutation-free
1953
     */
1954 1
    public function customSortKeysImmutable(callable $function): self
1955
    {
1956 1
        $that = clone $this;
1957
1958 1
        $that->generatorToArray();
1959
1960
        /**
1961
         * @psalm-suppress ImpureFunctionCall - object is already cloned
1962
         */
1963 1
        \uksort($that->array, $function);
1964
1965 1
        return $that;
1966
    }
1967
1968
    /**
1969
     * Custom sort by value via "usort".
1970
     *
1971
     * EXAMPLE: <code>
1972
     * $callable = function ($a, $b) {
1973
     *     if ($a == $b) {
1974
     *         return 0;
1975
     *     }
1976
     *     return ($a > $b) ? 1 : -1;
1977
     * };
1978
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1979
     * $resultArrayy = $arrayy->customSortValues($callable); // Arrayy['one' => 1, 'two' => 2, 'three' => 3]
1980
     * </code>
1981
     *
1982
     * @see          http://php.net/manual/en/function.usort.php
1983
     *
1984
     * @param callable $function
1985
     *
1986
     * @throws \InvalidArgumentException
1987
     *
1988
     * @return $this
1989
     *               <p>(Mutable) Return this Arrayy object.</p>
1990
     *
1991
     * @psalm-return static<TKey,T>
1992
     */
1993 10 View Code Duplication
    public function customSortValues($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1994
    {
1995 10
        if (\is_callable($function) === false) {
1996
            throw new \InvalidArgumentException('Passed function must be callable');
1997
        }
1998
1999 10
        $this->generatorToArray();
2000
2001 10
        \usort($this->array, $function);
2002
2003 10
        return $this;
2004
    }
2005
2006
    /**
2007
     * Custom sort by value via "usort".
2008
     *
2009
     * @see          http://php.net/manual/en/function.usort.php
2010
     *
2011
     * @param callable $function
2012
     *
2013
     * @throws \InvalidArgumentException
2014
     *
2015
     * @return $this
2016
     *               <p>(Immutable) Return this Arrayy object.</p>
2017
     *
2018
     * @psalm-return static<TKey,T>
2019
     * @psalm-mutation-free
2020
     */
2021 4
    public function customSortValuesImmutable($function): self
2022
    {
2023 4
        $that = clone $this;
2024
2025
        /**
2026
         * @psalm-suppress ImpureMethodCall - object is already cloned
2027
         */
2028 4
        $that->customSortValues($function);
2029
2030 4
        return $that;
2031
    }
2032
2033
    /**
2034
     * Delete the given key or keys.
2035
     *
2036
     * @param int|int[]|string|string[] $keyOrKeys
2037
     *
2038
     * @return void
2039
     */
2040 9
    public function delete($keyOrKeys)
2041
    {
2042 9
        $keyOrKeys = (array) $keyOrKeys;
2043
2044 9
        foreach ($keyOrKeys as $key) {
2045 9
            $this->offsetUnset($key);
2046
        }
2047 9
    }
2048
2049
    /**
2050
     * Return values that are only in the current array.
2051
     *
2052
     * EXAMPLE: <code>
2053
     * a([1 => 1, 2 => 2])->diff([1 => 1]); // Arrayy[2 => 2]
2054
     * </code>
2055
     *
2056
     * @param array ...$array
2057
     *
2058
     * @return static
2059
     *                <p>(Immutable)</p>
2060
     *
2061
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2062
     * @psalm-return static<TKey,T>
2063
     * @psalm-mutation-free
2064
     */
2065 13
    public function diff(...$array): self
2066
    {
2067 13
        return static::create(
2068 13
            \array_diff($this->toArray(), ...$array),
2069 13
            $this->iteratorClass,
2070 13
            false
2071
        );
2072
    }
2073
2074
    /**
2075
     * Return values that are only in the current array.
2076
     *
2077
     * @param array ...$array
2078
     *
2079
     * @return static
2080
     *                <p>(Immutable)</p>
2081
     *
2082
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2083
     * @psalm-return static<TKey,T>
2084
     * @psalm-mutation-free
2085
     */
2086 8
    public function diffKey(...$array): self
2087
    {
2088 8
        return static::create(
2089 8
            \array_diff_key($this->toArray(), ...$array),
2090 8
            $this->iteratorClass,
2091 8
            false
2092
        );
2093
    }
2094
2095
    /**
2096
     * Return values and Keys that are only in the current array.
2097
     *
2098
     * @param array $array
2099
     *
2100
     * @return static
2101
     *                <p>(Immutable)</p>
2102
     *
2103
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2104
     * @psalm-return static<TKey,T>
2105
     * @psalm-mutation-free
2106
     */
2107 8
    public function diffKeyAndValue(array $array = []): self
2108
    {
2109 8
        return static::create(
2110 8
            \array_diff_assoc($this->toArray(), $array),
2111 8
            $this->iteratorClass,
2112 8
            false
2113
        );
2114
    }
2115
2116
    /**
2117
     * Return values that are only in the current multi-dimensional array.
2118
     *
2119
     * EXAMPLE: <code>
2120
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2121
     * </code>
2122
     *
2123
     * @param array                 $array
2124
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2125
     *
2126
     * @return static
2127
     *                <p>(Immutable)</p>
2128
     *
2129
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2130
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2131
     * @psalm-return static<TKey,T>
2132
     * @psalm-mutation-free
2133
     */
2134 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2135
    {
2136
        // init
2137 1
        $result = [];
2138
2139
        if (
2140 1
            $helperVariableForRecursion !== null
2141
            &&
2142 1
            \is_array($helperVariableForRecursion)
2143
        ) {
2144
            $arrayForTheLoop = $helperVariableForRecursion;
2145
        } else {
2146 1
            $arrayForTheLoop = $this->getGenerator();
2147
        }
2148
2149 1
        foreach ($arrayForTheLoop as $key => $value) {
2150 1
            if ($value instanceof self) {
2151 1
                $value = $value->toArray();
2152
            }
2153
2154 1
            if (\array_key_exists($key, $array)) {
2155 1
                if ($value !== $array[$key]) {
2156 1
                    $result[$key] = $value;
2157
                }
2158
            } else {
2159 1
                $result[$key] = $value;
2160
            }
2161
        }
2162
2163 1
        return static::create(
2164 1
            $result,
2165 1
            $this->iteratorClass,
2166 1
            false
2167
        );
2168
    }
2169
2170
    /**
2171
     * Return values that are only in the new $array.
2172
     *
2173
     * EXAMPLE: <code>
2174
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2175
     * </code>
2176
     *
2177
     * @param array $array
2178
     *
2179
     * @return static
2180
     *                <p>(Immutable)</p>
2181
     *
2182
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2183
     * @psalm-return static<TKey,T>
2184
     * @psalm-mutation-free
2185
     */
2186 8
    public function diffReverse(array $array = []): self
2187
    {
2188 8
        return static::create(
2189 8
            \array_diff($array, $this->toArray()),
2190 8
            $this->iteratorClass,
2191 8
            false
2192
        );
2193
    }
2194
2195
    /**
2196
     * Divide an array into two arrays. One with keys and the other with values.
2197
     *
2198
     * EXAMPLE: <code>
2199
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2200
     * </code>
2201
     *
2202
     * @return static
2203
     *                <p>(Immutable)</p>
2204
     *
2205
     * @psalm-return static<TKey,T>
2206
     * @psalm-mutation-free
2207
     */
2208 1
    public function divide(): self
2209
    {
2210 1
        return static::create(
2211
            [
2212 1
                $this->keys(),
2213 1
                $this->values(),
2214
            ],
2215 1
            $this->iteratorClass,
2216 1
            false
2217
        );
2218
    }
2219
2220
    /**
2221
     * Iterate over the current array and modify the array's value.
2222
     *
2223
     * EXAMPLE: <code>
2224
     * $result = A::create();
2225
     * $closure = function ($value) {
2226
     *     return ':' . $value . ':';
2227
     * };
2228
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2229
     * </code>
2230
     *
2231
     * @param \Closure $closure
2232
     *
2233
     * @return static
2234
     *                <p>(Immutable)</p>
2235
     *
2236
     * @psalm-return static<TKey,T>
2237
     * @psalm-mutation-free
2238
     */
2239 5 View Code Duplication
    public function each(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2240
    {
2241
        // init
2242 5
        $array = [];
2243
2244 5
        foreach ($this->getGenerator() as $key => $value) {
2245 5
            $array[$key] = $closure($value, $key);
2246
        }
2247
2248 5
        return static::create(
2249 5
            $array,
2250 5
            $this->iteratorClass,
2251 5
            false
2252
        );
2253
    }
2254
2255
    /**
2256
     * Sets the internal iterator to the last element in the array and returns this element.
2257
     *
2258
     * @return mixed
2259
     */
2260
    public function end()
2261
    {
2262
        return \end($this->array);
2263
    }
2264
2265
    /**
2266
     * Check if a value is in the current array using a closure.
2267
     *
2268
     * EXAMPLE: <code>
2269
     * $callable = function ($value, $key) {
2270
     *     return 2 === $key and 'two' === $value;
2271
     * };
2272
     * a(['foo', 2 => 'two'])->exists($callable); // true
2273
     * </code>
2274
     *
2275
     * @param \Closure $closure
2276
     *
2277
     * @return bool
2278
     *              <p>Returns true if the given value is found, false otherwise.</p>
2279
     */
2280 4
    public function exists(\Closure $closure): bool
2281
    {
2282
        // init
2283 4
        $isExists = false;
2284
2285 4
        foreach ($this->getGenerator() as $key => $value) {
2286 3
            if ($closure($value, $key)) {
2287 1
                $isExists = true;
2288
2289 3
                break;
2290
            }
2291
        }
2292
2293 4
        return $isExists;
2294
    }
2295
2296
    /**
2297
     * Fill the array until "$num" with "$default" values.
2298
     *
2299
     * EXAMPLE: <code>
2300
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2301
     * </code>
2302
     *
2303
     * @param int   $num
2304
     * @param mixed $default
2305
     *
2306
     * @return static
2307
     *                <p>(Immutable)</p>
2308
     *
2309
     * @psalm-return static<TKey,T>
2310
     * @psalm-mutation-free
2311
     */
2312 8
    public function fillWithDefaults(int $num, $default = null): self
2313
    {
2314 8
        if ($num < 0) {
2315 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2316
        }
2317
2318 7
        $this->generatorToArray();
2319
2320 7
        $tmpArray = $this->array;
2321
2322 7
        $count = \count($tmpArray);
2323
2324 7
        while ($count < $num) {
2325 4
            $tmpArray[] = $default;
2326 4
            ++$count;
2327
        }
2328
2329 7
        return static::create(
2330 7
            $tmpArray,
2331 7
            $this->iteratorClass,
2332 7
            false
2333
        );
2334
    }
2335
2336
    /**
2337
     * Find all items in an array that pass the truth test.
2338
     *
2339
     * EXAMPLE: <code>
2340
     * $closure = function ($value) {
2341
     *     return $value % 2 !== 0;
2342
     * }
2343
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2344
     * </code>
2345
     *
2346
     * @param \Closure|null $closure [optional] <p>
2347
     *                               The callback function to use
2348
     *                               </p>
2349
     *                               <p>
2350
     *                               If no callback is supplied, all entries of
2351
     *                               input equal to false (see
2352
     *                               converting to
2353
     *                               boolean) will be removed.
2354
     *                               </p>
2355
     * @param int           $flag    [optional] <p>
2356
     *                               Flag determining what arguments are sent to <i>callback</i>:
2357
     *                               </p>
2358
     *                               <ul>
2359
     *                               <li>
2360
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2361
     *                               to <i>callback</i> instead of the value
2362
     *                               </li>
2363
     *                               <li>
2364
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2365
     *                               arguments to <i>callback</i> instead of the value
2366
     *                               </li>
2367
     *                               </ul>
2368
     *
2369
     * @return static
2370
     *                <p>(Immutable)</p>
2371
     *
2372
     * @psalm-param null|\Closure(T=,TKey=):bool|\Closure(T=):bool||\Closure(TKey=):bool $closure
2373
     * @psalm-return static<TKey,T>
2374
     * @psalm-mutation-free
2375
     */
2376 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2377
    {
2378 12
        if (!$closure) {
2379 1
            return $this->clean();
2380
        }
2381
2382 12
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2383 1
            $generator = function () use ($closure) {
2384 1
                foreach ($this->getGenerator() as $key => $value) {
2385 1
                    if ($closure($key) === true) {
2386 1
                        yield $key => $value;
2387
                    }
2388
                }
2389 1
            };
2390 12
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2391 12
            $generator = function () use ($closure) {
2392 11
                foreach ($this->getGenerator() as $key => $value) {
2393 10
                    if ($closure($value, $key) === true) {
2394 10
                        yield $key => $value;
2395
                    }
2396
                }
2397 12
            };
2398
        } else {
2399 1 View Code Duplication
            $generator = function () use ($closure) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2400 1
                foreach ($this->getGenerator() as $key => $value) {
2401 1
                    if ($closure($value) === true) {
2402 1
                        yield $key => $value;
2403
                    }
2404
                }
2405 1
            };
2406
        }
2407
2408 12
        return static::create(
2409 12
            $generator,
2410 12
            $this->iteratorClass,
2411 12
            false
2412
        );
2413
    }
2414
2415
    /**
2416
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2417
     * property within that.
2418
     *
2419
     * @param string          $property
2420
     * @param string|string[] $value
2421
     * @param string          $comparisonOp
2422
     *                                      <p>
2423
     *                                      'eq' (equals),<br />
2424
     *                                      'gt' (greater),<br />
2425
     *                                      'gte' || 'ge' (greater or equals),<br />
2426
     *                                      'lt' (less),<br />
2427
     *                                      'lte' || 'le' (less or equals),<br />
2428
     *                                      'ne' (not equals),<br />
2429
     *                                      'contains',<br />
2430
     *                                      'notContains',<br />
2431
     *                                      'newer' (via strtotime),<br />
2432
     *                                      'older' (via strtotime),<br />
2433
     *                                      </p>
2434
     *
2435
     * @return static
2436
     *                <p>(Immutable)</p>
2437
     *
2438
     * @psalm-return static<TKey,T>
2439
     * @psalm-mutation-free
2440
     *
2441
     * @psalm-suppress MissingClosureReturnType
2442
     * @psalm-suppress MissingClosureParamType
2443
     */
2444 1
    public function filterBy(
2445
        string $property,
2446
        $value,
2447
        string $comparisonOp = null
2448
    ): self {
2449 1
        if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2450 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
2451
        }
2452
2453
        $ops = [
2454 1
            'eq' => static function ($item, $prop, $value): bool {
2455 1
                return $item[$prop] === $value;
2456 1
            },
2457 1
            'gt' => static function ($item, $prop, $value): bool {
2458
                return $item[$prop] > $value;
2459 1
            },
2460 1
            'ge' => static function ($item, $prop, $value): bool {
2461
                return $item[$prop] >= $value;
2462 1
            },
2463 1
            'gte' => static function ($item, $prop, $value): bool {
2464
                return $item[$prop] >= $value;
2465 1
            },
2466 1
            'lt' => static function ($item, $prop, $value): bool {
2467 1
                return $item[$prop] < $value;
2468 1
            },
2469 1
            'le' => static function ($item, $prop, $value): bool {
2470
                return $item[$prop] <= $value;
2471 1
            },
2472 1
            'lte' => static function ($item, $prop, $value): bool {
2473
                return $item[$prop] <= $value;
2474 1
            },
2475 1
            'ne' => static function ($item, $prop, $value): bool {
2476
                return $item[$prop] !== $value;
2477 1
            },
2478 1
            'contains' => static function ($item, $prop, $value): bool {
2479 1
                return \in_array($item[$prop], (array) $value, true);
2480 1
            },
2481 1
            'notContains' => static function ($item, $prop, $value): bool {
2482
                return !\in_array($item[$prop], (array) $value, true);
2483 1
            },
2484 1
            'newer' => static function ($item, $prop, $value): bool {
2485
                return \strtotime($item[$prop]) > \strtotime($value);
2486 1
            },
2487 1
            'older' => static function ($item, $prop, $value): bool {
2488
                return \strtotime($item[$prop]) < \strtotime($value);
2489 1
            },
2490
        ];
2491
2492 1
        $result = \array_values(
2493 1
            \array_filter(
2494 1
                $this->toArray(false, true),
2495 1
                static function ($item) use (
2496 1
                    $property,
2497 1
                    $value,
2498 1
                    $ops,
2499 1
                    $comparisonOp
2500
                ) {
2501 1
                    $item = (array) $item;
2502 1
                    $itemArrayy = static::create($item);
2503 1
                    $item[$property] = $itemArrayy->get($property, []);
2504
2505 1
                    return $ops[$comparisonOp]($item, $property, $value);
2506 1
                }
2507
            )
2508
        );
2509
2510 1
        return static::create(
2511 1
            $result,
2512 1
            $this->iteratorClass,
2513 1
            false
2514
        );
2515
    }
2516
2517
    /**
2518
     * Find the first item in an array that passes the truth test, otherwise return false.
2519
     *
2520
     * EXAMPLE: <code>
2521
     * $search = 'foo';
2522
     * $closure = function ($value, $key) use ($search) {
2523
     *     return $value === $search;
2524
     * };
2525
     * a(['foo', 'bar', 'lall'])->find($closure); // 'foo'
2526
     * </code>
2527
     *
2528
     * @param \Closure $closure
2529
     *
2530
     * @return false|mixed
2531
     *                     <p>Return false if we did not find the value.</p>
2532
     */
2533 8 View Code Duplication
    public function find(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2534
    {
2535 8
        foreach ($this->getGenerator() as $key => $value) {
2536 6
            if ($closure($value, $key)) {
2537 6
                return $value;
2538
            }
2539
        }
2540
2541 3
        return false;
2542
    }
2543
2544
    /**
2545
     * find by ...
2546
     *
2547
     * EXAMPLE: <code>
2548
     * $array = [
2549
     *     0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01'],
2550
     *     1 => ['id' => 456, 'name' => 'bar', 'group' => 'primary', 'value' => 1468, 'when' => '2014-07-15'],
2551
     * ];
2552
     * a($array)->filterBy('name', 'foo'); // Arrayy[0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01']]
2553
     * </code>
2554
     *
2555
     * @param string          $property
2556
     * @param string|string[] $value
2557
     * @param string          $comparisonOp
2558
     *
2559
     * @return static
2560
     *                <p>(Immutable)</p>
2561
     *
2562
     * @psalm-return static<TKey,T>
2563
     * @psalm-mutation-free
2564
     */
2565 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2566
    {
2567 1
        return $this->filterBy($property, $value, $comparisonOp);
2568
    }
2569
2570
    /**
2571
     * Get the first value from the current array.
2572
     *
2573
     * EXAMPLE: <code>
2574
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->first(); // 'foo'
2575
     * </code>
2576
     *
2577
     * @return mixed
2578
     *               <p>Return null if there wasn't a element.</p>
2579
     */
2580 22
    public function first()
2581
    {
2582 22
        $key_first = $this->firstKey();
2583 22
        if ($key_first === null) {
2584 3
            return null;
2585
        }
2586
2587 19
        return $this->get($key_first);
2588
    }
2589
2590
    /**
2591
     * Get the first key from the current array.
2592
     *
2593
     * @return mixed
2594
     *               <p>Return null if there wasn't a element.</p>
2595
     * @psalm-mutation-free
2596
     */
2597 29
    public function firstKey()
2598
    {
2599 29
        $this->generatorToArray();
2600
2601 29
        return \array_key_first($this->array);
2602
    }
2603
2604
    /**
2605
     * Get the first value(s) from the current array.
2606
     * And will return an empty array if there was no first entry.
2607
     *
2608
     * EXAMPLE: <code>
2609
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsImmutable(2); // Arrayy[0 => 'foo', 1 => 'bar']
2610
     * </code>
2611
     *
2612
     * @param int|null $number <p>How many values you will take?</p>
2613
     *
2614
     * @return static
2615
     *                <p>(Immutable)</p>
2616
     *
2617
     * @psalm-return static<TKey,T>
2618
     * @psalm-mutation-free
2619
     */
2620 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2621
    {
2622 37
        $arrayTmp = $this->toArray();
2623
2624 37
        if ($number === null) {
2625 14
            $array = (array) \array_shift($arrayTmp);
2626
        } else {
2627 23
            $array = \array_splice($arrayTmp, 0, $number);
2628
        }
2629
2630 37
        return static::create(
2631 37
            $array,
2632 37
            $this->iteratorClass,
2633 37
            false
2634
        );
2635
    }
2636
2637
    /**
2638
     * Get the first value(s) from the current array.
2639
     * And will return an empty array if there was no first entry.
2640
     *
2641
     * @param int|null $number <p>How many values you will take?</p>
2642
     *
2643
     * @return static
2644
     *                <p>(Immutable)</p>
2645
     *
2646
     * @psalm-return static<TKey,T>
2647
     * @psalm-mutation-free
2648
     */
2649 3 View Code Duplication
    public function firstsKeys(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2650
    {
2651 3
        $arrayTmp = $this->keys()->toArray();
2652
2653 3
        if ($number === null) {
2654
            $array = (array) \array_shift($arrayTmp);
2655
        } else {
2656 3
            $array = \array_splice($arrayTmp, 0, $number);
2657
        }
2658
2659 3
        return static::create(
2660 3
            $array,
2661 3
            $this->iteratorClass,
2662 3
            false
2663
        );
2664
    }
2665
2666
    /**
2667
     * Get and remove the first value(s) from the current array.
2668
     * And will return an empty array if there was no first entry.
2669
     *
2670
     * EXAMPLE: <code>
2671
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsMutable(); // 'foo'
2672
     * </code>
2673
     *
2674
     * @param int|null $number <p>How many values you will take?</p>
2675
     *
2676
     * @return $this
2677
     *               <p>(Mutable)</p>
2678
     *
2679
     * @psalm-return static<TKey,T>
2680
     */
2681 34
    public function firstsMutable(int $number = null): self
2682
    {
2683 34
        $this->generatorToArray();
2684
2685 34
        if ($number === null) {
2686 19
            $this->array = (array) \array_shift($this->array);
2687
        } else {
2688 15
            $this->array = \array_splice($this->array, 0, $number);
2689
        }
2690
2691 34
        return $this;
2692
    }
2693
2694
    /**
2695
     * Exchanges all keys with their associated values in an array.
2696
     *
2697
     * EXAMPLE: <code>
2698
     * a([0 => 'foo', 1 => 'bar'])->flip(); // Arrayy['foo' => 0, 'bar' => 1]
2699
     * </code>
2700
     *
2701
     * @return static
2702
     *                <p>(Immutable)</p>
2703
     *
2704
     * @psalm-return static<array-key,TKey>
2705
     * @psalm-mutation-free
2706
     */
2707
    public function flip(): self
2708
    {
2709 1
        $generator = function (): \Generator {
2710 1
            foreach ($this->getGenerator() as $key => $value) {
2711 1
                yield (string) $value => $key;
2712
            }
2713 1
        };
2714
2715 1
        return static::create(
2716 1
            $generator,
2717 1
            $this->iteratorClass,
2718 1
            false
2719
        );
2720
    }
2721
2722
    /**
2723
     * Get a value from an array (optional using dot-notation).
2724
     *
2725
     * EXAMPLE: <code>
2726
     * $arrayy = a(['user' => ['lastname' => 'Moelleken']]);
2727
     * $arrayy->get('user.lastname'); // 'Moelleken'
2728
     * // ---
2729
     * $arrayy = new A();
2730
     * $arrayy['user'] = ['lastname' => 'Moelleken'];
2731
     * $arrayy['user.firstname'] = 'Lars';
2732
     * $arrayy['user']['lastname']; // Moelleken
2733
     * $arrayy['user.lastname']; // Moelleken
2734
     * $arrayy['user.firstname']; // Lars
2735
     * </code>
2736
     *
2737
     * @param mixed $key            <p>The key to look for.</p>
2738
     * @param mixed $fallback       <p>Value to fallback to.</p>
2739
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2740
     *                              class.</p>
2741
     * @param bool  $useByReference
2742
     *
2743
     * @return mixed|static
2744
     *
2745
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2746
     * @psalm-mutation-free
2747
     */
2748 243
    public function get(
2749
        $key,
2750
        $fallback = null,
2751
        array $array = null,
2752
        bool $useByReference = false
2753
    ) {
2754 243
        if ($array !== null) {
2755 4
            if ($useByReference) {
2756
                $usedArray = &$array;
2757
            } else {
2758 4
                $usedArray = $array;
2759
            }
2760
        } else {
2761 240
            $this->generatorToArray();
2762
2763 240
            if ($useByReference) {
2764 128
                $usedArray = &$this->array;
2765
            } else {
2766 129
                $usedArray = $this->array;
2767
            }
2768
        }
2769
2770 243
        if ($key === null) {
2771 1
            return static::create(
2772 1
                [],
2773 1
                $this->iteratorClass,
2774 1
                false
2775 1
            )->createByReference($usedArray);
2776
        }
2777
2778
        // php cast "bool"-index into "int"-index
2779 243
        if ((bool) $key === $key) {
2780 3
            $key = (int) $key;
2781
        }
2782
2783 243
        if (\array_key_exists($key, $usedArray) === true) {
2784 205
            if (\is_array($usedArray[$key])) {
2785 18
                return static::create(
2786 18
                    [],
2787 18
                    $this->iteratorClass,
2788 18
                    false
2789 18
                )->createByReference($usedArray[$key]);
2790
            }
2791
2792 191
            return $usedArray[$key];
2793
        }
2794
2795
        // crawl through array, get key according to object or not
2796 61
        $usePath = false;
2797
        if (
2798 61
            $this->pathSeparator
2799
            &&
2800 61
            (string) $key === $key
2801
            &&
2802 61
            \strpos($key, $this->pathSeparator) !== false
2803
        ) {
2804 31
            $segments = \explode($this->pathSeparator, (string) $key);
2805 31
            if ($segments !== false) {
2806 31
                $usePath = true;
2807 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2808
2809 31
                foreach ($segments as $segment) {
2810
                    if (
2811
                        (
2812 31
                            \is_array($usedArrayTmp)
2813
                            ||
2814 31
                            $usedArrayTmp instanceof \ArrayAccess
2815
                        )
2816
                        &&
2817 31
                        isset($usedArrayTmp[$segment])
2818
                    ) {
2819 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2820
2821 30
                        continue;
2822
                    }
2823
2824
                    if (
2825 14
                        \is_object($usedArrayTmp) === true
2826
                        &&
2827 14
                        \property_exists($usedArrayTmp, $segment)
2828
                    ) {
2829 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2830
2831 1
                        continue;
2832
                    }
2833
2834 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2835 1
                        $segmentsTmp = $segments;
2836 1
                        unset($segmentsTmp[0]);
2837 1
                        $keyTmp = \implode('.', $segmentsTmp);
2838 1
                        $returnTmp = static::create(
2839 1
                            [],
2840 1
                            $this->iteratorClass,
2841 1
                            false
2842
                        );
2843 1
                        foreach ($this->getAll() as $dataTmp) {
2844 1
                            if ($dataTmp instanceof self) {
2845
                                $returnTmp->add($dataTmp->get($keyTmp));
2846
2847
                                continue;
2848
                            }
2849
2850
                            if (
2851
                                (
2852 1
                                    \is_array($dataTmp)
2853
                                    ||
2854 1
                                    $dataTmp instanceof \ArrayAccess
2855
                                )
2856
                                &&
2857 1
                                isset($dataTmp[$keyTmp])
2858
                            ) {
2859
                                $returnTmp->add($dataTmp[$keyTmp]);
2860
2861
                                continue;
2862
                            }
2863
2864
                            if (
2865 1
                                \is_object($dataTmp) === true
2866
                                &&
2867 1
                                \property_exists($dataTmp, $keyTmp)
2868
                            ) {
2869 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2870
2871
                                /** @noinspection UnnecessaryContinueInspection */
2872 1
                                continue;
2873
                            }
2874
                        }
2875
2876 1
                        if ($returnTmp->count() > 0) {
2877 1
                            return $returnTmp;
2878
                        }
2879
                    }
2880
2881 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2882
                }
2883
            }
2884
        }
2885
2886 58
        if (isset($usedArrayTmp)) {
2887 28 View Code Duplication
            if (!$usePath && !isset($usedArrayTmp[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2888
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2889
            }
2890
2891 28
            if (\is_array($usedArrayTmp)) {
2892 6
                return static::create(
2893 6
                    [],
2894 6
                    $this->iteratorClass,
2895 6
                    false
2896 6
                )->createByReference($usedArrayTmp);
2897
            }
2898
2899 28
            return $usedArrayTmp;
2900
        }
2901
2902 30 View Code Duplication
        if (!$usePath && !isset($usedArray[$key])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2903 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2904
        }
2905
2906
        return static::create(
2907
            [],
2908
            $this->iteratorClass,
2909
            false
2910
        )->createByReference($usedArray);
2911
    }
2912
2913
    /**
2914
     * alias: for "Arrayy->toArray()"
2915
     *
2916
     * @return array
2917
     *
2918
     * @see          Arrayy::getArray()
2919
     *
2920
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2921
     */
2922 15
    public function getAll(): array
2923
    {
2924 15
        return $this->toArray();
2925
    }
2926
2927
    /**
2928
     * Get the current array from the "Arrayy"-object.
2929
     *
2930
     * alias for "toArray()"
2931
     *
2932
     * @param bool $convertAllArrayyElements <p>
2933
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2934
     *                                       </p>
2935
     * @param bool $preserveKeys             <p>
2936
     *                                       e.g.: A generator maybe return the same key more then once,
2937
     *                                       so maybe you will ignore the keys.
2938
     *                                       </p>
2939
     *
2940
     * @return array
2941
     *
2942
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2943
     * @psalm-mutation-free
2944
     *
2945
     * @see Arrayy::toArray()
2946
     */
2947 501
    public function getArray(
2948
        bool $convertAllArrayyElements = false,
2949
        bool $preserveKeys = true
2950
    ): array {
2951 501
        return $this->toArray(
2952 501
            $convertAllArrayyElements,
2953 501
            $preserveKeys
2954
        );
2955
    }
2956
2957
    /**
2958
     * @param string $json
2959
     *
2960
     * @return $this
2961
     */
2962 3
    public static function createFromJsonMapper(string $json)
2963
    {
2964
        // init
2965 3
        $class = static::create();
2966
2967 3
        $jsonObject = \json_decode($json, false);
2968
2969 3
        $mapper = new \Arrayy\Mapper\Json();
2970 View Code Duplication
        $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($class) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2971
            if ($class->checkPropertiesMismatchInConstructor) {
2972
                throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...' . \get_class($object).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2973
            }
2974
        };
2975
2976 3
        return $mapper->map($jsonObject, $class);
2977
    }
2978
2979
    /**
2980
     * @return array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
0 ignored issues
show
Documentation introduced by
The doc-type array<int|string,TypeChe...nterface>|TypeInterface could not be parsed: Expected "|" or "end of type", but got "<" at position 57. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
2981
     *
2982
     * @internal
2983
     */
2984 6
    public function getPhpDocPropertiesFromClass()
2985
    {
2986 6
        if ($this->properties === []) {
2987 1
            $this->properties = $this->getPropertiesFromPhpDoc();
2988
        }
2989
2990 6
        return $this->properties;
2991
    }
2992
2993
    /**
2994
     * Get the current array from the "Arrayy"-object as list.
2995
     *
2996
     * alias for "toList()"
2997
     *
2998
     * @param bool $convertAllArrayyElements <p>
2999
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3000
     *                                       </p>
3001
     *
3002
     * @return array
3003
     *
3004
     * @psalm-return array<int,mixed>|array<int,T>
3005
     * @psalm-mutation-free
3006
     *
3007
     * @see Arrayy::toList()
3008
     */
3009 1
    public function getList(bool $convertAllArrayyElements = false): array
3010
    {
3011 1
        return $this->toList($convertAllArrayyElements);
3012
    }
3013
3014
    /**
3015
     * Returns the values from a single column of the input array, identified by
3016
     * the $columnKey, can be used to extract data-columns from multi-arrays.
3017
     *
3018
     * EXAMPLE: <code>
3019
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
3020
     * </code>
3021
     *
3022
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
3023
     *       array by the values from the $indexKey column in the input array.
3024
     *
3025
     * @param int|string|null $columnKey
3026
     * @param int|string|null $indexKey
3027
     *
3028
     * @return static
3029
     *                <p>(Immutable)</p>
3030
     *
3031
     * @psalm-return static<TKey,T>
3032
     * @psalm-mutation-free
3033
     */
3034 1
    public function getColumn($columnKey = null, $indexKey = null): self
3035
    {
3036 1
        if ($columnKey === null && $indexKey === null) {
3037 1
            $generator = function () {
3038 1
                foreach ($this->getGenerator() as $key => $value) {
3039 1
                    yield $value;
3040
                }
3041 1
            };
3042
        } else {
3043 1
            $generator = function () use ($columnKey, $indexKey) {
3044 1
                foreach ($this->getGenerator() as $key => $value) {
3045
                    // reset
3046 1
                    $newKey = null;
3047 1
                    $newValue = null;
3048 1
                    $newValueFound = false;
3049
3050 1
                    if ($indexKey !== null) {
3051 1
                        foreach ($value as $keyInner => $valueInner) {
3052 1
                            if ($indexKey === $keyInner) {
3053 1
                                $newKey = $valueInner;
3054
                            }
3055
3056 1
                            if ($columnKey === $keyInner) {
3057 1
                                $newValue = $valueInner;
3058 1
                                $newValueFound = true;
3059
                            }
3060
                        }
3061
                    } else {
3062 1
                        foreach ($value as $keyInner => $valueInner) {
3063 1
                            if ($columnKey === $keyInner) {
3064 1
                                $newValue = $valueInner;
3065 1
                                $newValueFound = true;
3066
                            }
3067
                        }
3068
                    }
3069
3070 1
                    if ($newValueFound === false) {
3071 1
                        if ($newKey !== null) {
3072 1
                            yield $newKey => $value;
3073
                        } else {
3074 1
                            yield $value;
3075
                        }
3076
                    } else {
3077
                        /** @noinspection NestedPositiveIfStatementsInspection */
3078 1
                        if ($newKey !== null) {
3079 1
                            yield $newKey => $newValue;
3080
                        } else {
3081 1
                            yield $newValue;
3082
                        }
3083
                    }
3084
                }
3085 1
            };
3086
        }
3087
3088 1
        return static::create(
3089 1
            $generator,
3090 1
            $this->iteratorClass,
3091 1
            false
3092
        );
3093
    }
3094
3095
    /**
3096
     * Get the current array from the "Arrayy"-object as generator by reference.
3097
     *
3098
     * @return \Generator
3099
     *
3100
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3101
     */
3102 75
    public function &getGeneratorByReference(): \Generator
3103
    {
3104 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3105
            // -> false-positive -> see "&" from method
3106
            /** @noinspection YieldFromCanBeUsedInspection */
3107 17
            foreach ($this->generator as $key => $value) {
3108 17
                yield $key => $value;
3109
            }
3110
3111 5
            return;
3112
        }
3113
3114
        // -> false-positive -> see "&$value"
3115
        /** @noinspection YieldFromCanBeUsedInspection */
3116
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3117 59
        foreach ($this->array as $key => &$value) {
3118 54
            yield $key => $value;
3119
        }
3120 35
    }
3121
3122
    /**
3123
     * Get the current array from the "Arrayy"-object as generator.
3124
     *
3125
     * @return \Generator
3126
     *
3127
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3128
     * @psalm-mutation-free
3129
     */
3130 1000
    public function getGenerator(): \Generator
3131
    {
3132 1000
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3133 47
            yield from $this->generator;
3134
3135 47
            return;
3136
        }
3137
3138 998
        yield from $this->array;
3139 968
    }
3140
3141
    /**
3142
     * alias: for "Arrayy->keys()"
3143
     *
3144
     * @return static
3145
     *                <p>(Immutable)</p>
3146
     *
3147
     * @see          Arrayy::keys()
3148
     *
3149
     * @psalm-return static<array-key,TKey>
3150
     * @psalm-mutation-free
3151
     */
3152 2
    public function getKeys()
3153
    {
3154 2
        return $this->keys();
3155
    }
3156
3157
    /**
3158
     * Get the current array from the "Arrayy"-object as object.
3159
     *
3160
     * @return \stdClass
3161
     */
3162 4
    public function getObject(): \stdClass
3163
    {
3164 4
        return self::arrayToObject($this->toArray());
3165
    }
3166
3167
    /**
3168
     * alias: for "Arrayy->randomImmutable()"
3169
     *
3170
     * @return static
3171
     *                <p>(Immutable)</p>
3172
     *
3173
     * @see          Arrayy::randomImmutable()
3174
     *
3175
     * @psalm-return static<int|array-key,T>
3176
     */
3177 4
    public function getRandom(): self
3178
    {
3179 4
        return $this->randomImmutable();
3180
    }
3181
3182
    /**
3183
     * alias: for "Arrayy->randomKey()"
3184
     *
3185
     * @return mixed
3186
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3187
     *
3188
     * @see Arrayy::randomKey()
3189
     */
3190 3
    public function getRandomKey()
3191
    {
3192 3
        return $this->randomKey();
3193
    }
3194
3195
    /**
3196
     * alias: for "Arrayy->randomKeys()"
3197
     *
3198
     * @param int $number
3199
     *
3200
     * @return static
3201
     *                <p>(Immutable)</p>
3202
     *
3203
     * @see          Arrayy::randomKeys()
3204
     *
3205
     * @psalm-return static<TKey,T>
3206
     */
3207 8
    public function getRandomKeys(int $number): self
3208
    {
3209 8
        return $this->randomKeys($number);
3210
    }
3211
3212
    /**
3213
     * alias: for "Arrayy->randomValue()"
3214
     *
3215
     * @return mixed
3216
     *               <p>Get a random value or null if there wasn't a value.</p>
3217
     *
3218
     * @see Arrayy::randomValue()
3219
     */
3220 3
    public function getRandomValue()
3221
    {
3222 3
        return $this->randomValue();
3223
    }
3224
3225
    /**
3226
     * alias: for "Arrayy->randomValues()"
3227
     *
3228
     * @param int $number
3229
     *
3230
     * @return static
3231
     *                <p>(Immutable)</p>
3232
     *
3233
     * @see          Arrayy::randomValues()
3234
     *
3235
     * @psalm-return static<TKey,T>
3236
     */
3237 6
    public function getRandomValues(int $number): self
3238
    {
3239 6
        return $this->randomValues($number);
3240
    }
3241
3242
    /**
3243
     * Gets all values.
3244
     *
3245
     * @return static
3246
     *                <p>The values of all elements in this array, in the order they
3247
     *                appear in the array.</p>
3248
     *
3249
     * @psalm-return static<TKey,T>
3250
     */
3251 4
    public function getValues()
3252
    {
3253 4
        $this->generatorToArray(false);
3254
3255 4
        return static::create(
3256 4
            \array_values($this->array),
3257 4
            $this->iteratorClass,
3258 4
            false
3259
        );
3260
    }
3261
3262
    /**
3263
     * Gets all values via Generator.
3264
     *
3265
     * @return \Generator
3266
     *                    <p>The values of all elements in this array, in the order they
3267
     *                    appear in the array as Generator.</p>
3268
     *
3269
     * @psalm-return \Generator<TKey,T>
3270
     */
3271 4
    public function getValuesYield(): \Generator
3272
    {
3273 4
        yield from $this->getGenerator();
3274 4
    }
3275
3276
    /**
3277
     * Group values from a array according to the results of a closure.
3278
     *
3279
     * @param callable|string $grouper  <p>A callable function name.</p>
3280
     * @param bool            $saveKeys
3281
     *
3282
     * @return static
3283
     *                <p>(Immutable)</p>
3284
     *
3285
     * @psalm-return static<TKey,T>
3286
     * @psalm-mutation-free
3287
     */
3288 4
    public function group($grouper, bool $saveKeys = false): self
3289
    {
3290
        // init
3291 4
        $result = [];
3292
3293
        // Iterate over values, group by property/results from closure.
3294 4
        foreach ($this->getGenerator() as $key => $value) {
3295 4
            if (\is_callable($grouper) === true) {
3296 3
                $groupKey = $grouper($value, $key);
3297
            } else {
3298 1
                $groupKey = $this->get($grouper);
3299
            }
3300
3301 4
            $newValue = $this->get($groupKey, null, $result);
3302
3303 4
            if ($groupKey instanceof self) {
3304
                $groupKey = $groupKey->toArray();
3305
            }
3306
3307 4
            if ($newValue instanceof self) {
3308 4
                $newValue = $newValue->toArray();
3309
            }
3310
3311
            // Add to results.
3312 4
            if ($groupKey !== null) {
3313 3
                if ($saveKeys) {
3314 2
                    $result[$groupKey] = $newValue;
3315 2
                    $result[$groupKey][$key] = $value;
3316
                } else {
3317 1
                    $result[$groupKey] = $newValue;
3318 4
                    $result[$groupKey][] = $value;
3319
                }
3320
            }
3321
        }
3322
3323 4
        return static::create(
3324 4
            $result,
3325 4
            $this->iteratorClass,
3326 4
            false
3327
        );
3328
    }
3329
3330
    /**
3331
     * Check if an array has a given key.
3332
     *
3333
     * @param mixed $key
3334
     *
3335
     * @return bool
3336
     */
3337 30
    public function has($key): bool
3338
    {
3339 30
        static $UN_FOUND = null;
3340
3341 30
        if ($UN_FOUND === null) {
3342
            // Generate unique string to use as marker.
3343 1
            $UN_FOUND = \uniqid('arrayy', true);
3344
        }
3345
3346 30
        if (\is_array($key)) {
3347 1
            if ($key === []) {
3348
                return false;
3349
            }
3350
3351 1
            foreach ($key as $keyTmp) {
3352 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3353 1
                if ($found === false) {
3354 1
                    return false;
3355
                }
3356
            }
3357
3358 1
            return true;
3359
        }
3360
3361 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3362
    }
3363
3364
    /**
3365
     * Check if an array has a given value.
3366
     *
3367
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3368
     *
3369
     * @param mixed $value
3370
     *
3371
     * @return bool
3372
     */
3373 1
    public function hasValue($value): bool
3374
    {
3375 1
        return $this->contains($value);
3376
    }
3377
3378
    /**
3379
     * Implodes the values of this array.
3380
     *
3381
     * EXAMPLE: <code>
3382
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3383
     * </code>
3384
     *
3385
     * @param string $glue
3386
     * @param string $prefix
3387
     *
3388
     * @return string
3389
     * @psalm-mutation-free
3390
     */
3391 28
    public function implode(string $glue = '', string $prefix = ''): string
3392
    {
3393 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3394
    }
3395
3396
    /**
3397
     * Implodes the keys of this array.
3398
     *
3399
     * @param string $glue
3400
     *
3401
     * @return string
3402
     * @psalm-mutation-free
3403
     */
3404 8
    public function implodeKeys(string $glue = ''): string
3405
    {
3406 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3407
    }
3408
3409
    /**
3410
     * Given a list and an iterate-function that returns
3411
     * a key for each element in the list (or a property name),
3412
     * returns an object with an index of each item.
3413
     *
3414
     * @param mixed $key
3415
     *
3416
     * @return static
3417
     *                <p>(Immutable)</p>
3418
     *
3419
     * @psalm-return static<TKey,T>
3420
     * @psalm-mutation-free
3421
     */
3422 4 View Code Duplication
    public function indexBy($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3423
    {
3424
        // init
3425 4
        $results = [];
3426
3427 4
        foreach ($this->getGenerator() as $a) {
3428 4
            if (\array_key_exists($key, $a) === true) {
3429 4
                $results[$a[$key]] = $a;
3430
            }
3431
        }
3432
3433 4
        return static::create(
3434 4
            $results,
3435 4
            $this->iteratorClass,
3436 4
            false
3437
        );
3438
    }
3439
3440
    /**
3441
     * alias: for "Arrayy->searchIndex()"
3442
     *
3443
     * @param mixed $value <p>The value to search for.</p>
3444
     *
3445
     * @return false|mixed
3446
     *
3447
     * @see Arrayy::searchIndex()
3448
     */
3449 4
    public function indexOf($value)
3450
    {
3451 4
        return $this->searchIndex($value);
3452
    }
3453
3454
    /**
3455
     * Get everything but the last..$to items.
3456
     *
3457
     * EXAMPLE: <code>
3458
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3459
     * </code>
3460
     *
3461
     * @param int $to
3462
     *
3463
     * @return static
3464
     *                <p>(Immutable)</p>
3465
     *
3466
     * @psalm-return static<TKey,T>
3467
     * @psalm-mutation-free
3468
     */
3469 12
    public function initial(int $to = 1): self
3470
    {
3471 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3472
    }
3473
3474
    /**
3475
     * Return an array with all elements found in input array.
3476
     *
3477
     * EXAMPLE: <code>
3478
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3479
     * </code>
3480
     *
3481
     * @param array $search
3482
     * @param bool  $keepKeys
3483
     *
3484
     * @return static
3485
     *                <p>(Immutable)</p>
3486
     *
3487
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3488
     * @psalm-return static<TKey,T>
3489
     * @psalm-mutation-free
3490
     */
3491 4
    public function intersection(array $search, bool $keepKeys = false): self
3492
    {
3493 4
        if ($keepKeys) {
3494
            /**
3495
             * @psalm-suppress MissingClosureReturnType
3496
             * @psalm-suppress MissingClosureParamType
3497
             */
3498 1
            return static::create(
3499 1
                \array_uintersect(
3500 1
                    $this->toArray(),
3501 1
                    $search,
3502 1
                    static function ($a, $b) {
3503 1
                        return $a === $b ? 0 : -1;
3504 1
                    }
3505
                ),
3506 1
                $this->iteratorClass,
3507 1
                false
3508
            );
3509
        }
3510
3511 3
        return static::create(
3512 3
            \array_values(\array_intersect($this->toArray(), $search)),
3513 3
            $this->iteratorClass,
3514 3
            false
3515
        );
3516
    }
3517
3518
    /**
3519
     * Return an array with all elements found in input array.
3520
     *
3521
     * @param array ...$array
3522
     *
3523
     * @return static
3524
     *                <p>(Immutable)</p>
3525
     *
3526
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3527
     * @psalm-return static<TKey,T>
3528
     * @psalm-mutation-free
3529
     */
3530 1
    public function intersectionMulti(...$array): self
3531
    {
3532 1
        return static::create(
3533 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3534 1
            $this->iteratorClass,
3535 1
            false
3536
        );
3537
    }
3538
3539
    /**
3540
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3541
     *
3542
     * EXAMPLE: <code>
3543
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3544
     * </code>
3545
     *
3546
     * @param array $search
3547
     *
3548
     * @return bool
3549
     *
3550
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3551
     */
3552 1
    public function intersects(array $search): bool
3553
    {
3554 1
        return $this->intersection($search)->count() > 0;
3555
    }
3556
3557
    /**
3558
     * Invoke a function on all of an array's values.
3559
     *
3560
     * @param callable $callable
3561
     * @param mixed    $arguments
3562
     *
3563
     * @return static
3564
     *                <p>(Immutable)</p>
3565
     *
3566
     * @psalm-param  callable(T=,mixed):mixed $callable
3567
     * @psalm-return static<TKey,T>
3568
     * @psalm-mutation-free
3569
     */
3570 1
    public function invoke($callable, $arguments = []): self
3571
    {
3572
        // If one argument given for each iteration, create an array for it.
3573 1
        if (!\is_array($arguments)) {
3574 1
            $arguments = \array_fill(
3575 1
                0,
3576 1
                $this->count(),
3577 1
                $arguments
3578
            );
3579
        }
3580
3581
        // If the callable has arguments, pass them.
3582 1
        if ($arguments) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arguments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
3583 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3584
        } else {
3585 1
            $array = $this->map($callable);
3586
        }
3587
3588 1
        return static::create(
3589 1
            $array,
3590 1
            $this->iteratorClass,
3591 1
            false
3592
        );
3593
    }
3594
3595
    /**
3596
     * Check whether array is associative or not.
3597
     *
3598
     * EXAMPLE: <code>
3599
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3600
     * </code>
3601
     *
3602
     * @param bool $recursive
3603
     *
3604
     * @return bool
3605
     *              <p>Returns true if associative, false otherwise.</p>
3606
     */
3607 15 View Code Duplication
    public function isAssoc(bool $recursive = false): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3608
    {
3609 15
        if ($this->isEmpty()) {
3610 3
            return false;
3611
        }
3612
3613
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3614 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3615 13
            if ((string) $key !== $key) {
3616 13
                return false;
3617
            }
3618
        }
3619
3620 3
        return true;
3621
    }
3622
3623
    /**
3624
     * Check if a given key or keys are empty.
3625
     *
3626
     * @param int|int[]|string|string[]|null $keys
3627
     *
3628
     * @return bool
3629
     *              <p>Returns true if empty, false otherwise.</p>
3630
     * @psalm-mutation-free
3631
     */
3632 45
    public function isEmpty($keys = null): bool
3633
    {
3634 45
        if ($this->generator) {
3635
            return $this->toArray() === [];
3636
        }
3637
3638 45
        if ($keys === null) {
3639 43
            return $this->array === [];
3640
        }
3641
3642 2
        foreach ((array) $keys as $key) {
3643 2
            if (!empty($this->get($key))) {
3644 2
                return false;
3645
            }
3646
        }
3647
3648 2
        return true;
3649
    }
3650
3651
    /**
3652
     * Check if the current array is equal to the given "$array" or not.
3653
     *
3654
     * EXAMPLE: <code>
3655
     * a(['💩'])->isEqual(['💩']); // true
3656
     * </code>
3657
     *
3658
     * @param array $array
3659
     *
3660
     * @return bool
3661
     *
3662
     * @psalm-param array<mixed,mixed> $array
3663
     */
3664 1
    public function isEqual(array $array): bool
3665
    {
3666 1
        return $this->toArray() === $array;
3667
    }
3668
3669
    /**
3670
     * Check if the current array is a multi-array.
3671
     *
3672
     * EXAMPLE: <code>
3673
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3674
     * </code>
3675
     *
3676
     * @return bool
3677
     */
3678 22
    public function isMultiArray(): bool
3679
    {
3680 22
        foreach ($this->getGenerator() as $key => $value) {
3681 20
            if (\is_array($value)) {
3682 20
                return true;
3683
            }
3684
        }
3685
3686 18
        return false;
3687
    }
3688
3689
    /**
3690
     * Check whether array is numeric or not.
3691
     *
3692
     * @return bool
3693
     *              <p>Returns true if numeric, false otherwise.</p>
3694
     */
3695 5 View Code Duplication
    public function isNumeric(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3696
    {
3697 5
        if ($this->isEmpty()) {
3698 2
            return false;
3699
        }
3700
3701
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3702 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3703 4
            if ((int) $key !== $key) {
3704 4
                return false;
3705
            }
3706
        }
3707
3708 2
        return true;
3709
    }
3710
3711
    /**
3712
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3713
     *
3714
     * EXAMPLE: <code>
3715
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3716
     * </code>
3717
     *
3718
     * INFO: If the array is empty we count it as non-sequential.
3719
     *
3720
     * @param bool $recursive
3721
     *
3722
     * @return bool
3723
     * @psalm-mutation-free
3724
     */
3725 10
    public function isSequential(bool $recursive = false): bool
3726
    {
3727 10
        $i = 0;
3728 10
        foreach ($this->getGenerator() as $key => $value) {
3729
            if (
3730 9
                $recursive
3731
                &&
3732 9
                (\is_array($value) || $value instanceof self)
3733
                &&
3734 9
                self::create($value)->isSequential() === false
3735
            ) {
3736 1
                return false;
3737
            }
3738
3739 9
            if ($key !== $i) {
3740 3
                return false;
3741
            }
3742
3743 8
            ++$i;
3744
        }
3745
3746
        /** @noinspection IfReturnReturnSimplificationInspection */
3747 9
        if ($i === 0) {
3748 3
            return false;
3749
        }
3750
3751 8
        return true;
3752
    }
3753
3754
    /**
3755
     * @return array
3756
     *
3757
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3758
     */
3759 2
    public function jsonSerialize(): array
3760
    {
3761 2
        return $this->toArray();
3762
    }
3763
3764
    /**
3765
     * Gets the key/index of the element at the current internal iterator position.
3766
     *
3767
     * @return int|string|null
3768
     */
3769
    public function key()
3770
    {
3771
        return \key($this->array);
3772
    }
3773
3774
    /**
3775
     * Checks if the given key exists in the provided array.
3776
     *
3777
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3778
     *       then you need to use "Arrayy->offsetExists()".
3779
     *
3780
     * @param int|string $key the key to look for
3781
     *
3782
     * @return bool
3783
     * @psalm-mutation-free
3784
     */
3785 164
    public function keyExists($key): bool
3786
    {
3787 164
        return \array_key_exists($key, $this->array);
3788
    }
3789
3790
    /**
3791
     * Get all keys from the current array.
3792
     *
3793
     * EXAMPLE: <code>
3794
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3795
     * </code>
3796
     *
3797
     * @param bool       $recursive     [optional] <p>
3798
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3799
     *                                  </p>
3800
     * @param mixed|null $search_values [optional] <p>
3801
     *                                  If specified, then only keys containing these values are returned.
3802
     *                                  </p>
3803
     * @param bool       $strict        [optional] <p>
3804
     *                                  Determines if strict comparison (===) should be used during the search.
3805
     *                                  </p>
3806
     *
3807
     * @return static
3808
     *                <p>(Immutable) An array of all the keys in input.</p>
3809
     *
3810
     * @psalm-return static<array-key,TKey>
3811
     * @psalm-mutation-free
3812
     */
3813 29
    public function keys(
3814
        bool $recursive = false,
3815
        $search_values = null,
3816
        bool $strict = true
3817
    ): self {
3818
3819
        // recursive
3820
3821 29
        if ($recursive === true) {
3822 4
            $array = $this->array_keys_recursive(
3823 4
                null,
3824 4
                $search_values,
3825 4
                $strict
3826
            );
3827
3828 4
            return static::create(
3829 4
                $array,
3830 4
                $this->iteratorClass,
3831 4
                false
3832
            );
3833
        }
3834
3835
        // non recursive
3836
3837 28
        if ($search_values === null) {
3838 28
            $arrayFunction = function (): \Generator {
3839 28
                foreach ($this->getGenerator() as $key => $value) {
3840 26
                    yield $key;
3841
                }
3842 28
            };
3843
        } else {
3844 1
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3845 1
                $is_array_tmp = \is_array($search_values);
3846
3847
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3848 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3849 View Code Duplication
                    if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3850
                        (
3851 1
                            $is_array_tmp === false
3852
                            &&
3853 1
                            $strict === true
3854
                            &&
3855 1
                            $search_values === $value
3856
                        )
3857
                        ||
3858
                        (
3859 1
                            $is_array_tmp === false
3860
                            &&
3861 1
                            $strict === false
3862
                            &&
3863 1
                            $search_values == $value
3864
                        )
3865
                        ||
3866
                        (
3867 1
                            $is_array_tmp === true
3868
                            &&
3869 1
                            \in_array($value, $search_values, $strict)
3870
                        )
3871
                    ) {
3872 1
                        yield $key;
3873
                    }
3874
                }
3875 1
            };
3876
        }
3877
3878 28
        return static::create(
3879 28
            $arrayFunction,
3880 28
            $this->iteratorClass,
3881 28
            false
3882
        );
3883
    }
3884
3885
    /**
3886
     * Sort an array by key in reverse order.
3887
     *
3888
     * @param int $sort_flags [optional] <p>
3889
     *                        You may modify the behavior of the sort using the optional
3890
     *                        parameter sort_flags, for details
3891
     *                        see sort.
3892
     *                        </p>
3893
     *
3894
     * @return $this
3895
     *               <p>(Mutable) Return this Arrayy object.</p>
3896
     *
3897
     * @psalm-return static<TKey,T>
3898
     */
3899 4
    public function krsort(int $sort_flags = 0): self
3900
    {
3901 4
        $this->generatorToArray();
3902
3903 4
        \krsort($this->array, $sort_flags);
3904
3905 4
        return $this;
3906
    }
3907
3908
    /**
3909
     * Sort an array by key in reverse order.
3910
     *
3911
     * @param int $sort_flags [optional] <p>
3912
     *                        You may modify the behavior of the sort using the optional
3913
     *                        parameter sort_flags, for details
3914
     *                        see sort.
3915
     *                        </p>
3916
     *
3917
     * @return $this
3918
     *               <p>(Immutable) Return this Arrayy object.</p>
3919
     *
3920
     * @psalm-return static<TKey,T>
3921
     * @psalm-mutation-free
3922
     */
3923 4
    public function krsortImmutable(int $sort_flags = 0): self
3924
    {
3925 4
        $that = clone $this;
3926
3927
        /**
3928
         * @psalm-suppress ImpureMethodCall - object is already cloned
3929
         */
3930 4
        $that->krsort($sort_flags);
3931
3932 4
        return $that;
3933
    }
3934
3935
    /**
3936
     * Get the last value from the current array.
3937
     *
3938
     * EXAMPLE: <code>
3939
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
3940
     * </code>
3941
     *
3942
     * @return mixed|null
3943
     *                    <p>Return null if there wasn't a element.</p>
3944
     * @psalm-mutation-free
3945
     */
3946 17
    public function last()
3947
    {
3948 17
        $key_last = $this->lastKey();
3949 17
        if ($key_last === null) {
3950 2
            return null;
3951
        }
3952
3953 15
        return $this->get($key_last);
3954
    }
3955
3956
    /**
3957
     * Get the last key from the current array.
3958
     *
3959
     * @return mixed|null
3960
     *                    <p>Return null if there wasn't a element.</p>
3961
     * @psalm-mutation-free
3962
     */
3963 21
    public function lastKey()
3964
    {
3965 21
        $this->generatorToArray();
3966
3967 21
        return \array_key_last($this->array);
3968
    }
3969
3970
    /**
3971
     * Get the last value(s) from the current array.
3972
     *
3973
     * EXAMPLE: <code>
3974
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
3975
     * </code>
3976
     *
3977
     * @param int|null $number
3978
     *
3979
     * @return static
3980
     *                <p>(Immutable)</p>
3981
     *
3982
     * @psalm-return static<TKey,T>
3983
     * @psalm-mutation-free
3984
     */
3985 13
    public function lastsImmutable(int $number = null): self
3986
    {
3987 13
        if ($this->isEmpty()) {
3988 1
            return static::create(
3989 1
                [],
3990 1
                $this->iteratorClass,
3991 1
                false
3992
            );
3993
        }
3994
3995 12
        if ($number === null) {
3996 8
            $poppedValue = $this->last();
3997
3998 8
            if ($poppedValue === null) {
3999 1
                $poppedValue = [$poppedValue];
4000
            } else {
4001 7
                $poppedValue = (array) $poppedValue;
4002
            }
4003
4004 8
            $arrayy = static::create(
4005 8
                $poppedValue,
4006 8
                $this->iteratorClass,
4007 8
                false
4008
            );
4009
        } else {
4010 4
            $arrayy = $this->rest(-$number);
4011
        }
4012
4013 12
        return $arrayy;
4014
    }
4015
4016
    /**
4017
     * Get the last value(s) from the current array.
4018
     *
4019
     * EXAMPLE: <code>
4020
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4021
     * </code>
4022
     *
4023
     * @param int|null $number
4024
     *
4025
     * @return $this
4026
     *               <p>(Mutable)</p>
4027
     *
4028
     * @psalm-return static<TKey,T>
4029
     */
4030 13
    public function lastsMutable(int $number = null): self
4031
    {
4032 13
        if ($this->isEmpty()) {
4033 1
            return $this;
4034
        }
4035
4036 12
        $this->array = $this->lastsImmutable($number)->toArray();
4037 12
        $this->generator = null;
4038
4039 12
        return $this;
4040
    }
4041
4042
    /**
4043
     * Count the values from the current array.
4044
     *
4045
     * alias: for "Arrayy->count()"
4046
     *
4047
     * @param int $mode
4048
     *
4049
     * @return int
4050
     *
4051
     * @see Arrayy::count()
4052
     */
4053 20
    public function length(int $mode = \COUNT_NORMAL): int
4054
    {
4055 20
        return $this->count($mode);
4056
    }
4057
4058
    /**
4059
     * Apply the given function to the every element of the array,
4060
     * collecting the results.
4061
     *
4062
     * @param callable $callable
4063
     * @param bool     $useKeyAsSecondParameter
4064
     * @param mixed    ...$arguments
4065
     *
4066
     * @return static
4067
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4068
     *
4069
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
4070
     * @psalm-return static<TKey,T>
4071
     * @psalm-mutation-free
4072
     */
4073 5
    public function map(
4074
        callable $callable,
4075
        bool $useKeyAsSecondParameter = false,
4076
        ...$arguments
4077
    ) {
4078
        /**
4079
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4080
         */
4081 5
        $useArguments = \func_num_args() > 2;
4082
4083 5
        return static::create(
4084 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4085 5
                foreach ($this->getGenerator() as $key => $value) {
4086 4
                    if ($useArguments) {
4087 3
                        if ($useKeyAsSecondParameter) {
4088
                            yield $key => $callable($value, $key, ...$arguments);
4089
                        } else {
4090 3
                            yield $key => $callable($value, ...$arguments);
4091
                        }
4092
                    } else {
4093
                        /** @noinspection NestedPositiveIfStatementsInspection */
4094 4
                        if ($useKeyAsSecondParameter) {
4095
                            yield $key => $callable($value, $key);
4096
                        } else {
4097 4
                            yield $key => $callable($value);
4098
                        }
4099
                    }
4100
                }
4101 5
            },
4102 5
            $this->iteratorClass,
4103 5
            false
4104
        );
4105
    }
4106
4107
    /**
4108
     * Check if all items in current array match a truth test.
4109
     *
4110
     * EXAMPLE: <code>
4111
     * $closure = function ($value, $key) {
4112
     *     return ($value % 2 === 0);
4113
     * };
4114
     * a([2, 4, 8])->matches($closure); // true
4115
     * </code>
4116
     *
4117
     * @param \Closure $closure
4118
     *
4119
     * @return bool
4120
     */
4121 15 View Code Duplication
    public function matches(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4122
    {
4123 15
        if ($this->count() === 0) {
4124 2
            return false;
4125
        }
4126
4127 13
        foreach ($this->getGenerator() as $key => $value) {
4128 13
            $value = $closure($value, $key);
4129
4130 13
            if ($value === false) {
4131 13
                return false;
4132
            }
4133
        }
4134
4135 7
        return true;
4136
    }
4137
4138
    /**
4139
     * Check if any item in the current array matches a truth test.
4140
     *
4141
     * EXAMPLE: <code>
4142
     * $closure = function ($value, $key) {
4143
     *     return ($value % 2 === 0);
4144
     * };
4145
     * a([1, 4, 7])->matches($closure); // true
4146
     * </code>
4147
     *
4148
     * @param \Closure $closure
4149
     *
4150
     * @return bool
4151
     */
4152 14 View Code Duplication
    public function matchesAny(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4153
    {
4154 14
        if ($this->count() === 0) {
4155 2
            return false;
4156
        }
4157
4158 12
        foreach ($this->getGenerator() as $key => $value) {
4159 12
            $value = $closure($value, $key);
4160
4161 12
            if ($value === true) {
4162 12
                return true;
4163
            }
4164
        }
4165
4166 4
        return false;
4167
    }
4168
4169
    /**
4170
     * Get the max value from an array.
4171
     *
4172
     * EXAMPLE: <code>
4173
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4174
     * </code>
4175
     *
4176
     * @return false|mixed
4177
     *                     <p>Will return false if there are no values.</p>
4178
     */
4179 10 View Code Duplication
    public function max()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4180
    {
4181 10
        if ($this->count() === 0) {
4182 1
            return false;
4183
        }
4184
4185 9
        $max = false;
4186
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4187 9
        foreach ($this->getGeneratorByReference() as &$value) {
4188
            if (
4189 9
                $max === false
4190
                ||
4191 9
                $value > $max
4192
            ) {
4193 9
                $max = $value;
4194
            }
4195
        }
4196
4197 9
        return $max;
4198
    }
4199
4200
    /**
4201
     * Merge the new $array into the current array.
4202
     *
4203
     * - keep key,value from the current array, also if the index is in the new $array
4204
     *
4205
     * EXAMPLE: <code>
4206
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4207
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4208
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4209
     * // ---
4210
     * $array1 = [0 => 'one', 1 => 'foo'];
4211
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4212
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4213
     * </code>
4214
     *
4215
     * @param array $array
4216
     * @param bool  $recursive
4217
     *
4218
     * @return static
4219
     *                <p>(Immutable)</p>
4220
     *
4221
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4222
     * @psalm-return static<int|TKey,T>
4223
     * @psalm-mutation-free
4224
     */
4225 33 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4226
    {
4227 33
        if ($recursive === true) {
4228 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4229 9
            $result = \array_replace_recursive($this->toArray(), $array);
4230
        } else {
4231 24
            $result = \array_replace($this->toArray(), $array);
4232
        }
4233
4234 33
        return static::create(
4235 33
            $result,
4236 33
            $this->iteratorClass,
4237 33
            false
4238
        );
4239
    }
4240
4241
    /**
4242
     * Merge the new $array into the current array.
4243
     *
4244
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4245
     * - create new indexes
4246
     *
4247
     * EXAMPLE: <code>
4248
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4249
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4250
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4251
     * // ---
4252
     * $array1 = [0 => 'one', 1 => 'foo'];
4253
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4254
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4255
     * </code>
4256
     *
4257
     * @param array $array
4258
     * @param bool  $recursive
4259
     *
4260
     * @return static
4261
     *                <p>(Immutable)</p>
4262
     *
4263
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4264
     * @psalm-return static<TKey,T>
4265
     * @psalm-mutation-free
4266
     */
4267 20 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4268
    {
4269 20
        if ($recursive === true) {
4270 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4271 5
            $result = \array_merge_recursive($this->toArray(), $array);
4272
        } else {
4273 15
            $result = \array_merge($this->toArray(), $array);
4274
        }
4275
4276 20
        return static::create(
4277 20
            $result,
4278 20
            $this->iteratorClass,
4279 20
            false
4280
        );
4281
    }
4282
4283
    /**
4284
     * Merge the the current array into the $array.
4285
     *
4286
     * - use key,value from the new $array, also if the index is in the current array
4287
     *
4288
     * EXAMPLE: <code>
4289
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4290
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4291
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4292
     * // ---
4293
     * $array1 = [0 => 'one', 1 => 'foo'];
4294
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4295
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4296
     * </code>
4297
     *
4298
     * @param array $array
4299
     * @param bool  $recursive
4300
     *
4301
     * @return static
4302
     *                <p>(Immutable)</p>
4303
     *
4304
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4305
     * @psalm-return static<TKey,T>
4306
     * @psalm-mutation-free
4307
     */
4308 17 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4309
    {
4310 17
        if ($recursive === true) {
4311 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4312 4
            $result = \array_replace_recursive($array, $this->toArray());
4313
        } else {
4314 13
            $result = \array_replace($array, $this->toArray());
4315
        }
4316
4317 17
        return static::create(
4318 17
            $result,
4319 17
            $this->iteratorClass,
4320 17
            false
4321
        );
4322
    }
4323
4324
    /**
4325
     * Merge the current array into the new $array.
4326
     *
4327
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4328
     * - create new indexes
4329
     *
4330
     * EXAMPLE: <code>
4331
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4332
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4333
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4334
     * // ---
4335
     * $array1 = [0 => 'one', 1 => 'foo'];
4336
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4337
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4338
     * </code>
4339
     *
4340
     * @param array $array
4341
     * @param bool  $recursive
4342
     *
4343
     * @return static
4344
     *                <p>(Immutable)</p>
4345
     *
4346
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4347
     * @psalm-return static<TKey,T>
4348
     * @psalm-mutation-free
4349
     */
4350 21 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4351
    {
4352 21
        if ($recursive === true) {
4353 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4354 7
            $result = \array_merge_recursive($array, $this->toArray());
4355
        } else {
4356 14
            $result = \array_merge($array, $this->toArray());
4357
        }
4358
4359 21
        return static::create(
4360 21
            $result,
4361 21
            $this->iteratorClass,
4362 21
            false
4363
        );
4364
    }
4365
4366
    /**
4367
     * @return ArrayyMeta|static
4368
     */
4369 18
    public static function meta()
4370
    {
4371 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4372
    }
4373
4374
    /**
4375
     * Get the min value from an array.
4376
     *
4377
     * EXAMPLE: <code>
4378
     * a([-9, -8, -7, 1.32])->min(); // -9
4379
     * </code>
4380
     *
4381
     * @return false|mixed
4382
     *                     <p>Will return false if there are no values.</p>
4383
     */
4384 10 View Code Duplication
    public function min()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4385
    {
4386 10
        if ($this->count() === 0) {
4387 1
            return false;
4388
        }
4389
4390 9
        $min = false;
4391
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4392 9
        foreach ($this->getGeneratorByReference() as &$value) {
4393
            if (
4394 9
                $min === false
4395
                ||
4396 9
                $value < $min
4397
            ) {
4398 9
                $min = $value;
4399
            }
4400
        }
4401
4402 9
        return $min;
4403
    }
4404
4405
    /**
4406
     * Get the most used value from the array.
4407
     *
4408
     * @return mixed|null
4409
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4410
     * @psalm-mutation-free
4411
     */
4412 3
    public function mostUsedValue()
4413
    {
4414 3
        return $this->countValues()->arsortImmutable()->firstKey();
4415
    }
4416
4417
    /**
4418
     * Get the most used value from the array.
4419
     *
4420
     * @param int|null $number <p>How many values you will take?</p>
4421
     *
4422
     * @return static
4423
     *                <p>(Immutable)</p>
4424
     *
4425
     * @psalm-return static<TKey,T>
4426
     * @psalm-mutation-free
4427
     */
4428 3
    public function mostUsedValues(int $number = null): self
4429
    {
4430 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4431
    }
4432
4433
    /**
4434
     * Move an array element to a new index.
4435
     *
4436
     * EXAMPLE: <code>
4437
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4438
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4439
     * </code>
4440
     *
4441
     * @param int|string $from
4442
     * @param int        $to
4443
     *
4444
     * @return static
4445
     *                <p>(Immutable)</p>
4446
     *
4447
     * @psalm-return static<TKey,T>
4448
     * @psalm-mutation-free
4449
     */
4450 1
    public function moveElement($from, $to): self
4451
    {
4452 1
        $array = $this->toArray();
4453
4454 1
        if ((int) $from === $from) {
4455 1
            $tmp = \array_splice($array, $from, 1);
4456 1
            \array_splice($array, (int) $to, 0, $tmp);
4457 1
            $output = $array;
4458 1
        } elseif ((string) $from === $from) {
4459 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4460 1
            $itemToMove = $array[$from];
4461 1
            if ($indexToMove !== false) {
4462 1
                \array_splice($array, $indexToMove, 1);
4463
            }
4464 1
            $i = 0;
4465 1
            $output = [];
4466 1
            foreach ($array as $key => $item) {
4467 1
                if ($i === $to) {
4468 1
                    $output[$from] = $itemToMove;
4469
                }
4470 1
                $output[$key] = $item;
4471 1
                ++$i;
4472
            }
4473
        } else {
4474
            $output = [];
4475
        }
4476
4477 1
        return static::create(
4478 1
            $output,
4479 1
            $this->iteratorClass,
4480 1
            false
4481
        );
4482
    }
4483
4484
    /**
4485
     * Move an array element to the first place.
4486
     *
4487
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4488
     *       loss the keys of an indexed array.
4489
     *
4490
     * @param int|string $key
4491
     *
4492
     * @return static
4493
     *                <p>(Immutable)</p>
4494
     *
4495
     * @psalm-return static<TKey,T>
4496
     * @psalm-mutation-free
4497
     */
4498 1 View Code Duplication
    public function moveElementToFirstPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4499
    {
4500 1
        $array = $this->toArray();
4501
4502 1
        if ($this->offsetExists($key)) {
4503 1
            $tmpValue = $this->get($key);
4504 1
            unset($array[$key]);
4505 1
            $array = [$key => $tmpValue] + $array;
4506
        }
4507
4508 1
        return static::create(
4509 1
            $array,
4510 1
            $this->iteratorClass,
4511 1
            false
4512
        );
4513
    }
4514
4515
    /**
4516
     * Move an array element to the last place.
4517
     *
4518
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4519
     *       loss the keys of an indexed array.
4520
     *
4521
     * @param int|string $key
4522
     *
4523
     * @return static
4524
     *                <p>(Immutable)</p>
4525
     *
4526
     * @psalm-return static<TKey,T>
4527
     * @psalm-mutation-free
4528
     */
4529 1 View Code Duplication
    public function moveElementToLastPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4530
    {
4531 1
        $array = $this->toArray();
4532
4533 1
        if ($this->offsetExists($key)) {
4534 1
            $tmpValue = $this->get($key);
4535 1
            unset($array[$key]);
4536 1
            $array += [$key => $tmpValue];
4537
        }
4538
4539 1
        return static::create(
4540 1
            $array,
4541 1
            $this->iteratorClass,
4542 1
            false
4543
        );
4544
    }
4545
4546
    /**
4547
     * Moves the internal iterator position to the next element and returns this element.
4548
     *
4549
     * @return false|mixed
4550
     *                     <p>(Mutable) Will return false if there are no values.</p>
4551
     */
4552
    public function next()
4553
    {
4554
        return \next($this->array);
4555
    }
4556
4557
    /**
4558
     * Get the next nth keys and values from the array.
4559
     *
4560
     * @param int $step
4561
     * @param int $offset
4562
     *
4563
     * @return static
4564
     *                <p>(Immutable)</p>
4565
     *
4566
     * @psalm-return static<TKey,T>
4567
     * @psalm-mutation-free
4568
     */
4569
    public function nth(int $step, int $offset = 0): self
4570
    {
4571 1
        $arrayFunction = function () use ($step, $offset): \Generator {
4572 1
            $position = 0;
4573 1
            foreach ($this->getGenerator() as $key => $value) {
4574 1
                if ($position++ % $step !== $offset) {
4575 1
                    continue;
4576
                }
4577
4578 1
                yield $key => $value;
4579
            }
4580 1
        };
4581
4582 1
        return static::create(
4583 1
            $arrayFunction,
4584 1
            $this->iteratorClass,
4585 1
            false
4586
        );
4587
    }
4588
4589
    /**
4590
     * Get a subset of the items from the given array.
4591
     *
4592
     * @param int[]|string[] $keys
4593
     *
4594
     * @return static
4595
     *                <p>(Immutable)</p>
4596
     *
4597
     * @psalm-param array-key[] $keys
4598
     * @psalm-return static<TKey,T>
4599
     * @psalm-mutation-free
4600
     */
4601 1 View Code Duplication
    public function only(array $keys): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4602
    {
4603 1
        $keys = \array_flip($keys);
4604
4605 1
        $generator = function () use ($keys): \Generator {
4606 1
            foreach ($this->getGenerator() as $key => $value) {
4607 1
                if (isset($keys[$key])) {
4608 1
                    yield $key => $value;
4609
                }
4610
            }
4611 1
        };
4612
4613 1
        return static::create(
4614 1
            $generator,
4615 1
            $this->iteratorClass,
4616 1
            false
4617
        );
4618
    }
4619
4620
    /**
4621
     * Pad array to the specified size with a given value.
4622
     *
4623
     * @param int   $size  <p>Size of the result array.</p>
4624
     * @param mixed $value <p>Empty value by default.</p>
4625
     *
4626
     * @return static
4627
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4628
     *
4629
     * @psalm-return static<TKey,T>
4630
     * @psalm-mutation-free
4631
     */
4632 5
    public function pad(int $size, $value): self
4633
    {
4634 5
        return static::create(
4635 5
            \array_pad($this->toArray(), $size, $value),
4636 5
            $this->iteratorClass,
4637 5
            false
4638
        );
4639
    }
4640
4641
    /**
4642
     * Partitions this array in two array according to a predicate.
4643
     * Keys are preserved in the resulting array.
4644
     *
4645
     * @param \Closure $closure
4646
     *                          <p>The predicate on which to partition.</p>
4647
     *
4648
     * @return array<int, static>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
4649
     *                    <p>An array with two elements. The first element contains the array
4650
     *                    of elements where the predicate returned TRUE, the second element
4651
     *                    contains the array of elements where the predicate returned FALSE.</p>
4652
     *
4653
     * @psalm-return array<int, static<TKey,T>>
4654
     */
4655 1
    public function partition(\Closure $closure): array
4656
    {
4657
        // init
4658 1
        $matches = [];
4659 1
        $noMatches = [];
4660
4661 1
        foreach ($this->getGenerator() as $key => $value) {
4662 1
            if ($closure($value, $key)) {
4663 1
                $matches[$key] = $value;
4664
            } else {
4665 1
                $noMatches[$key] = $value;
4666
            }
4667
        }
4668
4669 1
        return [self::create($matches), self::create($noMatches)];
4670
    }
4671
4672
    /**
4673
     * Pop a specified value off the end of the current array.
4674
     *
4675
     * @return mixed|null
4676
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4677
     */
4678 5
    public function pop()
4679
    {
4680 5
        $this->generatorToArray();
4681
4682 5
        return \array_pop($this->array);
4683
    }
4684
4685
    /**
4686
     * Prepend a (key) + value to the current array.
4687
     *
4688
     * EXAMPLE: <code>
4689
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4690
     * </code>
4691
     *
4692
     * @param mixed $value
4693
     * @param mixed $key
4694
     *
4695
     * @return $this
4696
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4697
     *
4698
     * @psalm-return static<TKey,T>
4699
     */
4700 11
    public function prepend($value, $key = null)
4701
    {
4702 11
        $this->generatorToArray();
4703
4704 11
        if ($this->properties !== []) {
4705 3
            $this->checkType($key, $value);
4706
        }
4707
4708 9
        if ($key === null) {
4709 8
            \array_unshift($this->array, $value);
4710
        } else {
4711 2
            $this->array = [$key => $value] + $this->array;
4712
        }
4713
4714 9
        return $this;
4715
    }
4716
4717
    /**
4718
     * Prepend a (key) + value to the current array.
4719
     *
4720
     * EXAMPLE: <code>
4721
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4722
     * </code>
4723
     *
4724
     * @param mixed $value
4725
     * @param mixed $key
4726
     *
4727
     * @return $this
4728
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4729
     *
4730
     * @psalm-return static<TKey,T>
4731
     * @psalm-mutation-free
4732
     */
4733 View Code Duplication
    public function prependImmutable($value, $key = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4734
    {
4735 1
        $generator = function () use ($key, $value): \Generator {
4736 1
            if ($this->properties !== []) {
4737
                $this->checkType($key, $value);
4738
            }
4739
4740 1
            if ($key !== null) {
4741
                yield $key => $value;
4742
            } else {
4743 1
                yield $value;
4744
            }
4745
4746
            /** @noinspection YieldFromCanBeUsedInspection - FP */
4747 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
4748 1
                yield $keyOld => $itemOld;
4749
            }
4750 1
        };
4751
4752 1
        return static::create(
4753 1
            $generator,
4754 1
            $this->iteratorClass,
4755 1
            false
4756
        );
4757
    }
4758
4759
    /**
4760
     * Add a suffix to each key.
4761
     *
4762
     * @param mixed $suffix
4763
     *
4764
     * @return static
4765
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
4766
     *
4767
     * @psalm-return static<TKey,T>
4768
     * @psalm-mutation-free
4769
     */
4770 10 View Code Duplication
    public function prependToEachKey($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4771
    {
4772
        // init
4773 10
        $result = [];
4774
4775 10
        foreach ($this->getGenerator() as $key => $item) {
4776 9
            if ($item instanceof self) {
4777
                $result[$key] = $item->prependToEachKey($suffix);
4778 9
            } elseif (\is_array($item)) {
4779
                $result[$key] = self::create(
4780
                    $item,
4781
                    $this->iteratorClass,
4782
                    false
4783
                )->prependToEachKey($suffix)
4784
                    ->toArray();
4785
            } else {
4786 9
                $result[$key . $suffix] = $item;
4787
            }
4788
        }
4789
4790 10
        return self::create(
4791 10
            $result,
4792 10
            $this->iteratorClass,
4793 10
            false
4794
        );
4795
    }
4796
4797
    /**
4798
     * Add a suffix to each value.
4799
     *
4800
     * @param mixed $suffix
4801
     *
4802
     * @return static
4803
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
4804
     *
4805
     * @psalm-return static<TKey,T>
4806
     * @psalm-mutation-free
4807
     */
4808 10 View Code Duplication
    public function prependToEachValue($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4809
    {
4810
        // init
4811 10
        $result = [];
4812
4813 10
        foreach ($this->getGenerator() as $key => $item) {
4814 9
            if ($item instanceof self) {
4815
                $result[$key] = $item->prependToEachValue($suffix);
4816 9
            } elseif (\is_array($item)) {
4817
                $result[$key] = self::create(
4818
                    $item,
4819
                    $this->iteratorClass,
4820
                    false
4821
                )->prependToEachValue($suffix)
4822
                    ->toArray();
4823 9
            } elseif (\is_object($item) === true) {
4824 1
                $result[$key] = $item;
4825
            } else {
4826 9
                $result[$key] = $item . $suffix;
4827
            }
4828
        }
4829
4830 10
        return self::create(
4831 10
            $result,
4832 10
            $this->iteratorClass,
4833 10
            false
4834
        );
4835
    }
4836
4837
    /**
4838
     * Return the value of a given key and
4839
     * delete the key.
4840
     *
4841
     * @param int|int[]|string|string[]|null $keyOrKeys
4842
     * @param mixed                          $fallback
4843
     *
4844
     * @return mixed
4845
     */
4846 5
    public function pull($keyOrKeys = null, $fallback = null)
4847
    {
4848 5
        if ($keyOrKeys === null) {
4849 1
            $array = $this->toArray();
4850 1
            $this->clear();
4851
4852 1
            return $array;
4853
        }
4854
4855 4
        if (\is_array($keyOrKeys)) {
4856 1
            $valueOrValues = [];
4857 1
            foreach ($keyOrKeys as $key) {
4858 1
                $valueOrValues[] = $this->get($key, $fallback);
4859 1
                $this->offsetUnset($key);
4860
            }
4861
        } else {
4862 4
            $valueOrValues = $this->get($keyOrKeys, $fallback);
4863 4
            $this->offsetUnset($keyOrKeys);
4864
        }
4865
4866 4
        return $valueOrValues;
4867
    }
4868
4869
    /**
4870
     * Push one or more values onto the end of array at once.
4871
     *
4872
     * @param array ...$args
4873
     *
4874
     * @return $this
4875
     *               <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
4876
     *
4877
     * @noinspection ReturnTypeCanBeDeclaredInspection
4878
     *
4879
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
4880
     * @psalm-return static<TKey,T>
4881
     */
4882 7
    public function push(...$args)
4883
    {
4884 7
        $this->generatorToArray();
4885
4886
        if (
4887 7
            $this->checkPropertyTypes
4888
            &&
4889 7
            $this->properties !== []
4890
        ) {
4891 1
            foreach ($args as $key => $value) {
4892 1
                $this->checkType($key, $value);
4893
            }
4894
        }
4895
4896 7
        \array_push($this->array, ...$args);
4897
4898 7
        return $this;
4899
    }
4900
4901
    /**
4902
     * Get a random value from the current array.
4903
     *
4904
     * EXAMPLE: <code>
4905
     * a([1, 2, 3, 4])->randomImmutable(2); // e.g.: Arrayy[1, 4]
4906
     * </code>
4907
     *
4908
     * @param int|null $number <p>How many values you will take?</p>
4909
     *
4910
     * @return static
4911
     *                <p>(Immutable)</p>
4912
     *
4913
     * @psalm-return static<int|array-key,T>
4914
     */
4915 19
    public function randomImmutable(int $number = null): self
4916
    {
4917 19
        $this->generatorToArray();
4918
4919 19
        if ($this->count() === 0) {
4920 1
            return static::create(
4921 1
                [],
4922 1
                $this->iteratorClass,
4923 1
                false
4924
            );
4925
        }
4926
4927 18 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4928
            /** @noinspection NonSecureArrayRandUsageInspection */
4929 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
4930
4931 13
            return static::create(
4932 13
                $arrayRandValue,
4933 13
                $this->iteratorClass,
4934 13
                false
4935
            );
4936
        }
4937
4938 6
        $arrayTmp = $this->array;
4939
        /** @noinspection NonSecureShuffleUsageInspection */
4940 6
        \shuffle($arrayTmp);
4941
4942 6
        return static::create(
4943 6
            $arrayTmp,
4944 6
            $this->iteratorClass,
4945 6
            false
4946 6
        )->firstsImmutable($number);
4947
    }
4948
4949
    /**
4950
     * Pick a random key/index from the keys of this array.
4951
     *
4952
     * EXAMPLE: <code>
4953
     * $arrayy = A::create([1 => 'one', 2 => 'two']);
4954
     * $arrayy->randomKey(); // e.g. 2
4955
     * </code>
4956
     *
4957
     * @throws \RangeException If array is empty
4958
     *
4959
     * @return mixed
4960
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
4961
     */
4962 4
    public function randomKey()
4963
    {
4964 4
        $result = $this->randomKeys(1);
4965
4966 4
        if (!isset($result[0])) {
4967
            $result[0] = null;
4968
        }
4969
4970 4
        return $result[0];
4971
    }
4972
4973
    /**
4974
     * Pick a given number of random keys/indexes out of this array.
4975
     *
4976
     * EXAMPLE: <code>
4977
     * a([1 => 'one', 2 => 'two'])->randomKeys(); // e.g. Arrayy[1, 2]
4978
     * </code>
4979
     *
4980
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
4981
     *
4982
     * @throws \RangeException If array is empty
4983
     *
4984
     * @return static
4985
     *                <p>(Immutable)</p>
4986
     *
4987
     * @psalm-return static<TKey,T>
4988
     */
4989 13
    public function randomKeys(int $number): self
4990
    {
4991 13
        $this->generatorToArray();
4992
4993 13
        $count = $this->count();
4994
4995
        if (
4996 13
            $number === 0
4997
            ||
4998 13
            $number > $count
4999
        ) {
5000 2
            throw new \RangeException(
5001 2
                \sprintf(
5002 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
5003 2
                    $number,
5004 2
                    $count
5005
                )
5006
            );
5007
        }
5008
5009 11
        $result = (array) \array_rand($this->array, $number);
5010
5011 11
        return static::create(
5012 11
            $result,
5013 11
            $this->iteratorClass,
5014 11
            false
5015
        );
5016
    }
5017
5018
    /**
5019
     * Get a random value from the current array.
5020
     *
5021
     * EXAMPLE: <code>
5022
     * a([1, 2, 3, 4])->randomMutable(2); // e.g.: Arrayy[1, 4]
5023
     * </code>
5024
     *
5025
     * @param int|null $number <p>How many values you will take?</p>
5026
     *
5027
     * @return $this
5028
     *               <p>(Mutable) Return this Arrayy object.</p>
5029
     *
5030
     * @psalm-return static<TKey,T>
5031
     */
5032 17
    public function randomMutable(int $number = null): self
5033
    {
5034 17
        $this->generatorToArray();
5035
5036 17
        if ($this->count() === 0) {
5037
            return static::create(
5038
                [],
5039
                $this->iteratorClass,
5040
                false
5041
            );
5042
        }
5043
5044 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5045
            /** @noinspection NonSecureArrayRandUsageInspection */
5046 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
5047 7
            $this->array = $arrayRandValue;
5048
5049 7
            return $this;
5050
        }
5051
5052
        /** @noinspection NonSecureShuffleUsageInspection */
5053 11
        \shuffle($this->array);
5054
5055 11
        return $this->firstsMutable($number);
5056
    }
5057
5058
    /**
5059
     * Pick a random value from the values of this array.
5060
     *
5061
     * EXAMPLE: <code>
5062
     * a([1 => 'one', 2 => 'two'])->randomValue(); // e.g. 'one'
5063
     * </code>
5064
     *
5065
     * @return mixed
5066
     *               <p>Get a random value or null if there wasn't a value.</p>
5067
     */
5068 4
    public function randomValue()
5069
    {
5070 4
        $result = $this->randomImmutable();
5071
5072 4
        if (!isset($result[0])) {
5073
            $result[0] = null;
5074
        }
5075
5076 4
        return $result[0];
5077
    }
5078
5079
    /**
5080
     * Pick a given number of random values out of this array.
5081
     *
5082
     * EXAMPLE: <code>
5083
     * a([1 => 'one', 2 => 'two'])->randomValues(); // e.g. Arrayy['one', 'two']
5084
     * </code>
5085
     *
5086
     * @param int $number
5087
     *
5088
     * @return static
5089
     *                <p>(Mutable)</p>
5090
     *
5091
     * @psalm-return static<TKey,T>
5092
     */
5093 7
    public function randomValues(int $number): self
5094
    {
5095 7
        return $this->randomMutable($number);
5096
    }
5097
5098
    /**
5099
     * Get a random value from an array, with the ability to skew the results.
5100
     *
5101
     * EXAMPLE: <code>
5102
     * a([0 => 3, 1 => 4])->randomWeighted([1 => 4]); // e.g.: Arrayy[4] (has a 66% chance of returning 4)
5103
     * </code>
5104
     *
5105
     * @param array    $array
5106
     * @param int|null $number <p>How many values you will take?</p>
5107
     *
5108
     * @return static<int,mixed>
0 ignored issues
show
Documentation introduced by
The doc-type static<int,mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
5109
     *                           <p>(Immutable)</p>
5110
     *
5111
     * @psalm-param  array<mixed,mixed> $array
5112
     * @psalm-return static<int|array-key,T>
5113
     */
5114 9
    public function randomWeighted(array $array, int $number = null): self
5115
    {
5116
        // init
5117 9
        $options = [];
5118
5119 9
        foreach ($array as $option => $weight) {
5120 9
            if ($this->searchIndex($option) !== false) {
5121 9
                for ($i = 0; $i < $weight; ++$i) {
5122 1
                    $options[] = $option;
5123
                }
5124
            }
5125
        }
5126
5127 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
5128
    }
5129
5130
    /**
5131
     * Reduce the current array via callable e.g. anonymous-function.
5132
     *
5133
     * EXAMPLE: <code>
5134
     * function myReducer($resultArray, $value) {
5135
     *     if ($value == 'foo') {
5136
     *         $resultArray[] = $value;
5137
     *     }
5138
     *     return $resultArray;
5139
     * };
5140
     * a(['foo', 'bar'])->reduce('myReducer'); // Arrayy['foo']
5141
     * </cdde>
5142
     *
5143
     * @param callable $callable
5144
     * @param mixed    $init
5145
     *
5146
     * @return static
5147
     *                <p>(Immutable)</p>
5148
     *
5149
     * @psalm-return static<TKey,T>
5150
     * @psalm-mutation-free
5151
     */
5152 18
    public function reduce($callable, $init = []): self
5153
    {
5154 18
        if ($this->generator) {
5155 1
            $result = $init;
5156
5157 1
            foreach ($this->getGenerator() as $value) {
5158 1
                $result = $callable($result, $value);
5159
            }
5160
5161 1
            return static::create(
5162 1
                $result,
5163 1
                $this->iteratorClass,
5164 1
                false
5165
            );
5166
        }
5167
5168 18
        $result = \array_reduce($this->array, $callable, $init);
5169
5170 18
        if ($result === null) {
5171
            $this->array = [];
5172
        } else {
5173 18
            $this->array = (array) $result;
5174
        }
5175
5176 18
        return static::create(
5177 18
            $this->array,
5178 18
            $this->iteratorClass,
5179 18
            false
5180
        );
5181
    }
5182
5183
    /**
5184
     * @param bool $unique
5185
     *
5186
     * @return static
5187
     *                <p>(Immutable)</p>
5188
     *
5189
     * @psalm-return static<TKey,T>
5190
     * @psalm-mutation-free
5191
     */
5192 14
    public function reduce_dimension(bool $unique = true): self
5193
    {
5194
        // init
5195 14
        $result = [];
5196
5197 14
        foreach ($this->getGenerator() as $val) {
5198 12
            if (\is_array($val)) {
5199 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5200
            } else {
5201 12
                $result[] = [$val];
5202
            }
5203
        }
5204
5205 14
        $result = $result === [] ? [] : \array_merge(...$result);
5206
5207 14
        $resultArrayy = new static($result);
5208
5209
        /**
5210
         * @psalm-suppress ImpureMethodCall - object is already re-created
5211
         * @psalm-suppress InvalidReturnStatement - why?
5212
         */
5213 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5214
    }
5215
5216
    /**
5217
     * Create a numerically re-indexed Arrayy object.
5218
     *
5219
     * EXAMPLE: <code>
5220
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5221
     * </code>
5222
     *
5223
     * @return $this
5224
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5225
     *
5226
     * @psalm-return static<TKey,T>
5227
     */
5228 9
    public function reindex(): self
5229
    {
5230 9
        $this->generatorToArray(false);
5231
5232 9
        $this->array = \array_values($this->array);
5233
5234 9
        return $this;
5235
    }
5236
5237
    /**
5238
     * Return all items that fail the truth test.
5239
     *
5240
     * EXAMPLE: <code>
5241
     * $closure = function ($value) {
5242
     *     return $value % 2 !== 0;
5243
     * }
5244
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5245
     * </code>
5246
     *
5247
     * @param \Closure $closure
5248
     *
5249
     * @return static
5250
     *                <p>(Immutable)</p>
5251
     *
5252
     * @psalm-return static<TKey,T>
5253
     * @psalm-mutation-free
5254
     */
5255 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...
5256
    {
5257
        // init
5258 1
        $filtered = [];
5259
5260 1
        foreach ($this->getGenerator() as $key => $value) {
5261 1
            if (!$closure($value, $key)) {
5262 1
                $filtered[$key] = $value;
5263
            }
5264
        }
5265
5266 1
        return static::create(
5267 1
            $filtered,
5268 1
            $this->iteratorClass,
5269 1
            false
5270
        );
5271
    }
5272
5273
    /**
5274
     * Remove a value from the current array (optional using dot-notation).
5275
     *
5276
     * EXAMPLE: <code>
5277
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5278
     * </code>
5279
     *
5280
     * @param mixed $key
5281
     *
5282
     * @return static
5283
     *                <p>(Mutable)</p>
5284
     *
5285
     * @psalm-param  TKey $key
5286
     * @psalm-return static<TKey,T>
5287
     */
5288 22
    public function remove($key)
5289
    {
5290
        // recursive call
5291 22
        if (\is_array($key)) {
5292 1
            foreach ($key as $k) {
5293 1
                $this->internalRemove($k);
5294
            }
5295
5296 1
            return static::create(
5297 1
                $this->toArray(),
5298 1
                $this->iteratorClass,
5299 1
                false
5300
            );
5301
        }
5302
5303 21
        $this->internalRemove($key);
5304
5305 21
        return static::create(
5306 21
            $this->toArray(),
5307 21
            $this->iteratorClass,
5308 21
            false
5309
        );
5310
    }
5311
5312
    /**
5313
     * alias: for "Arrayy->removeValue()"
5314
     *
5315
     * @param mixed $element
5316
     *
5317
     * @return static
5318
     *                <p>(Immutable)</p>
5319
     *
5320
     * @psalm-param  T $element
5321
     * @psalm-return static<TKey,T>
5322
     * @psalm-mutation-free
5323
     */
5324 8
    public function removeElement($element)
5325
    {
5326 8
        return $this->removeValue($element);
5327
    }
5328
5329
    /**
5330
     * Remove the first value from the current array.
5331
     *
5332
     * EXAMPLE: <code>
5333
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5334
     * </code>
5335
     *
5336
     * @return static
5337
     *                <p>(Immutable)</p>
5338
     *
5339
     * @psalm-return static<TKey,T>
5340
     * @psalm-mutation-free
5341
     */
5342 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...
5343
    {
5344 7
        $tmpArray = $this->toArray();
5345
5346 7
        \array_shift($tmpArray);
5347
5348 7
        return static::create(
5349 7
            $tmpArray,
5350 7
            $this->iteratorClass,
5351 7
            false
5352
        );
5353
    }
5354
5355
    /**
5356
     * Remove the last value from the current array.
5357
     *
5358
     * EXAMPLE: <code>
5359
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5360
     * </code>
5361
     *
5362
     * @return static
5363
     *                <p>(Immutable)</p>
5364
     *
5365
     * @psalm-return static<TKey,T>
5366
     * @psalm-mutation-free
5367
     */
5368 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...
5369
    {
5370 7
        $tmpArray = $this->toArray();
5371
5372 7
        \array_pop($tmpArray);
5373
5374 7
        return static::create(
5375 7
            $tmpArray,
5376 7
            $this->iteratorClass,
5377 7
            false
5378
        );
5379
    }
5380
5381
    /**
5382
     * Removes a particular value from an array (numeric or associative).
5383
     *
5384
     * EXAMPLE: <code>
5385
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5386
     * </code>
5387
     *
5388
     * @param mixed $value
5389
     *
5390
     * @return static
5391
     *                <p>(Immutable)</p>
5392
     *
5393
     * @psalm-param  T $value
5394
     * @psalm-return static<TKey,T>
5395
     * @psalm-mutation-free
5396
     */
5397 8
    public function removeValue($value): self
5398
    {
5399 8
        $this->generatorToArray();
5400
5401
        // init
5402 8
        $isSequentialArray = $this->isSequential();
5403
5404 8
        foreach ($this->array as $key => $item) {
5405 7
            if ($item === $value) {
5406 7
                unset($this->array[$key]);
5407
            }
5408
        }
5409
5410 8
        if ($isSequentialArray) {
5411 6
            $this->array = \array_values($this->array);
5412
        }
5413
5414 8
        return static::create(
5415 8
            $this->array,
5416 8
            $this->iteratorClass,
5417 8
            false
5418
        );
5419
    }
5420
5421
    /**
5422
     * Generate array of repeated arrays.
5423
     *
5424
     * @param int $times <p>How many times has to be repeated.</p>
5425
     *
5426
     * @return static
5427
     *                <p>(Immutable)</p>
5428
     *
5429
     * @psalm-return static<TKey,T>
5430
     * @psalm-mutation-free
5431
     */
5432 1
    public function repeat($times): self
5433
    {
5434 1
        if ($times === 0) {
5435 1
            return static::create([], $this->iteratorClass);
5436
        }
5437
5438 1
        return static::create(
5439 1
            \array_fill(0, (int) $times, $this->toArray()),
5440 1
            $this->iteratorClass,
5441 1
            false
5442
        );
5443
    }
5444
5445
    /**
5446
     * Replace a key with a new key/value pair.
5447
     *
5448
     * EXAMPLE: <code>
5449
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5450
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5451
     * </code>
5452
     *
5453
     * @param mixed $oldKey
5454
     * @param mixed $newKey
5455
     * @param mixed $newValue
5456
     *
5457
     * @return static
5458
     *                <p>(Immutable)</p>
5459
     *
5460
     * @psalm-return static<TKey,T>
5461
     * @psalm-mutation-free
5462
     */
5463 5
    public function replace($oldKey, $newKey, $newValue): self
5464
    {
5465 5
        $that = clone $this;
5466
5467
        /**
5468
         * @psalm-suppress ImpureMethodCall - object is already cloned
5469
         */
5470 5
        return $that->remove($oldKey)
5471 5
            ->set($newKey, $newValue);
5472
    }
5473
5474
    /**
5475
     * Create an array using the current array as values and the other array as keys.
5476
     *
5477
     * EXAMPLE: <code>
5478
     * $firstArray = [
5479
     *     1 => 'one',
5480
     *     2 => 'two',
5481
     *     3 => 'three',
5482
     * ];
5483
     * $secondArray = [
5484
     *     'one' => 1,
5485
     *     1     => 'one',
5486
     *     2     => 2,
5487
     * ];
5488
     * $arrayy = a($firstArray);
5489
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5490
     * </code>
5491
     *
5492
     * @param array $keys <p>An array of keys.</p>
5493
     *
5494
     * @return static
5495
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5496
     *
5497
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5498
     * @psalm-return static<TKey,T>
5499
     * @psalm-mutation-free
5500
     */
5501 2
    public function replaceAllKeys(array $keys): self
5502
    {
5503 2
        return static::create(
5504 2
            \array_combine($keys, $this->toArray()),
5505 2
            $this->iteratorClass,
5506 2
            false
5507
        );
5508
    }
5509
5510
    /**
5511
     * Create an array using the current array as keys and the other array as values.
5512
     *
5513
     * EXAMPLE: <code>
5514
     * $firstArray = [
5515
     *     1 => 'one',
5516
     *     2 => 'two',
5517
     *     3 => 'three',
5518
     * ];
5519
     * $secondArray = [
5520
     *     'one' => 1,
5521
     *     1     => 'one',
5522
     *     2     => 2,
5523
     * ];
5524
     * $arrayy = a($firstArray);
5525
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5526
     * </code>
5527
     *
5528
     * @param array $array <p>An array of values.</p>
5529
     *
5530
     * @return static
5531
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5532
     *
5533
     * @psalm-param  array<mixed,T> $array
5534
     * @psalm-return static<TKey,T>
5535
     * @psalm-mutation-free
5536
     */
5537 2
    public function replaceAllValues(array $array): self
5538
    {
5539 2
        return static::create(
5540 2
            \array_combine($this->array, $array),
5541 2
            $this->iteratorClass,
5542 2
            false
5543
        );
5544
    }
5545
5546
    /**
5547
     * Replace the keys in an array with another set.
5548
     *
5549
     * EXAMPLE: <code>
5550
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5551
     * </code>
5552
     *
5553
     * @param array $keys <p>An array of keys matching the array's size</p>
5554
     *
5555
     * @return static
5556
     *                <p>(Immutable)</p>
5557
     *
5558
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5559
     * @psalm-return static<TKey,T>
5560
     * @psalm-mutation-free
5561
     */
5562 1
    public function replaceKeys(array $keys): self
5563
    {
5564 1
        $values = \array_values($this->toArray());
5565 1
        $result = \array_combine($keys, $values);
5566
5567 1
        return static::create(
5568 1
            $result,
5569 1
            $this->iteratorClass,
5570 1
            false
5571
        );
5572
    }
5573
5574
    /**
5575
     * Replace the first matched value in an array.
5576
     *
5577
     * EXAMPLE: <code>
5578
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5579
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5580
     * </code>
5581
     *
5582
     * @param mixed $search      <p>The value to replace.</p>
5583
     * @param mixed $replacement <p>The value to replace.</p>
5584
     *
5585
     * @return static
5586
     *                <p>(Immutable)</p>
5587
     *
5588
     * @psalm-return static<TKey,T>
5589
     * @psalm-mutation-free
5590
     */
5591 3 View Code Duplication
    public function replaceOneValue($search, $replacement = ''): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5592
    {
5593 3
        $array = $this->toArray();
5594 3
        $key = \array_search($search, $array, true);
5595
5596 3
        if ($key !== false) {
5597 3
            $array[$key] = $replacement;
5598
        }
5599
5600 3
        return static::create(
5601 3
            $array,
5602 3
            $this->iteratorClass,
5603 3
            false
5604
        );
5605
    }
5606
5607
    /**
5608
     * Replace values in the current array.
5609
     *
5610
     * EXAMPLE: <code>
5611
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5612
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5613
     * </code>
5614
     *
5615
     * @param mixed $search      <p>The value to replace.</p>
5616
     * @param mixed $replacement <p>What to replace it with.</p>
5617
     *
5618
     * @return static
5619
     *                <p>(Immutable)</p>
5620
     *
5621
     * @psalm-return static<TKey,T>
5622
     * @psalm-mutation-free
5623
     */
5624 1
    public function replaceValues($search, $replacement = ''): self
5625
    {
5626
        /**
5627
         * @psalm-suppress MissingClosureReturnType
5628
         * @psalm-suppress MissingClosureParamType
5629
         */
5630 1
        return $this->each(
5631 1
            static function ($value) use ($search, $replacement) {
5632 1
                return \str_replace($search, $replacement, $value);
5633 1
            }
5634
        );
5635
    }
5636
5637
    /**
5638
     * Get the last elements from index $from until the end of this array.
5639
     *
5640
     * EXAMPLE: <code>
5641
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5642
     * </code>
5643
     *
5644
     * @param int $from
5645
     *
5646
     * @return static
5647
     *                <p>(Immutable)</p>
5648
     *
5649
     * @psalm-return static<TKey,T>
5650
     * @psalm-mutation-free
5651
     */
5652 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...
5653
    {
5654 15
        $tmpArray = $this->toArray();
5655
5656 15
        return static::create(
5657 15
            \array_splice($tmpArray, $from),
5658 15
            $this->iteratorClass,
5659 15
            false
5660
        );
5661
    }
5662
5663
    /**
5664
     * Return the array in the reverse order.
5665
     *
5666
     * EXAMPLE: <code>
5667
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5668
     * </code>
5669
     *
5670
     * @return $this
5671
     *               <p>(Mutable) Return this Arrayy object.</p>
5672
     *
5673
     * @psalm-return static<TKey,T>
5674
     */
5675 9
    public function reverse(): self
5676
    {
5677 9
        $this->generatorToArray();
5678
5679 9
        $this->array = \array_reverse($this->array);
5680
5681 9
        return $this;
5682
    }
5683
5684
    /**
5685
     * Sort an array in reverse order.
5686
     *
5687
     * @param int $sort_flags [optional] <p>
5688
     *                        You may modify the behavior of the sort using the optional
5689
     *                        parameter sort_flags, for details
5690
     *                        see sort.
5691
     *                        </p>
5692
     *
5693
     * @return $this
5694
     *               <p>(Mutable) Return this Arrayy object.</p>
5695
     *
5696
     * @psalm-return static<TKey,T>
5697
     */
5698 4
    public function rsort(int $sort_flags = 0): self
5699
    {
5700 4
        $this->generatorToArray();
5701
5702 4
        \rsort($this->array, $sort_flags);
5703
5704 4
        return $this;
5705
    }
5706
5707
    /**
5708
     * Sort an array in reverse order.
5709
     *
5710
     * @param int $sort_flags [optional] <p>
5711
     *                        You may modify the behavior of the sort using the optional
5712
     *                        parameter sort_flags, for details
5713
     *                        see sort.
5714
     *                        </p>
5715
     *
5716
     * @return $this
5717
     *               <p>(Immutable) Return this Arrayy object.</p>
5718
     *
5719
     * @psalm-return static<TKey,T>
5720
     * @psalm-mutation-free
5721
     */
5722 4
    public function rsortImmutable(int $sort_flags = 0): self
5723
    {
5724 4
        $that = clone $this;
5725
5726
        /**
5727
         * @psalm-suppress ImpureMethodCall - object is already cloned
5728
         */
5729 4
        $that->rsort($sort_flags);
5730
5731 4
        return $that;
5732
    }
5733
5734
    /**
5735
     * Search for the first index of the current array via $value.
5736
     *
5737
     * EXAMPLE: <code>
5738
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5739
     * </code>
5740
     *
5741
     * @param mixed $value
5742
     *
5743
     * @return false|float|int|string
5744
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5745
     * @psalm-mutation-free
5746
     */
5747 21
    public function searchIndex($value)
5748
    {
5749 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5750 20
            if ($value === $valueFromArray) {
5751 20
                return $keyFromArray;
5752
            }
5753
        }
5754
5755 11
        return false;
5756
    }
5757
5758
    /**
5759
     * Search for the value of the current array via $index.
5760
     *
5761
     * EXAMPLE: <code>
5762
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5763
     * </code>
5764
     *
5765
     * @param mixed $index
5766
     *
5767
     * @return static
5768
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5769
     *
5770
     * @psalm-return static<TKey,T>
5771
     * @psalm-mutation-free
5772
     */
5773 9
    public function searchValue($index): self
5774
    {
5775 9
        $this->generatorToArray();
5776
5777
        // init
5778 9
        $return = [];
5779
5780 9
        if ($this->array === []) {
5781
            return static::create(
5782
                [],
5783
                $this->iteratorClass,
5784
                false
5785
            );
5786
        }
5787
5788
        // php cast "bool"-index into "int"-index
5789 9
        if ((bool) $index === $index) {
5790 1
            $index = (int) $index;
5791
        }
5792
5793 9
        if ($this->offsetExists($index)) {
5794 7
            $return = [$this->array[$index]];
5795
        }
5796
5797 9
        return static::create(
5798 9
            $return,
5799 9
            $this->iteratorClass,
5800 9
            false
5801
        );
5802
    }
5803
5804
    /**
5805
     * Set a value for the current array (optional using dot-notation).
5806
     *
5807
     * EXAMPLE: <code>
5808
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5809
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5810
     * </code>
5811
     *
5812
     * @param string $key   <p>The key to set.</p>
5813
     * @param mixed  $value <p>Its value.</p>
5814
     *
5815
     * @return $this
5816
     *               <p>(Mutable) Return this Arrayy object.</p>
5817
     *
5818
     * @psalm-param  TKey $key
5819
     * @psalm-param  T $value
5820
     * @psalm-return static<TKey,T>
5821
     */
5822 28
    public function set($key, $value): self
5823
    {
5824 28
        $this->internalSet($key, $value);
5825
5826 27
        return $this;
5827
    }
5828
5829
    /**
5830
     * Get a value from a array and set it if it was not.
5831
     *
5832
     * WARNING: this method only set the value, if the $key is not already set
5833
     *
5834
     * EXAMPLE: <code>
5835
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5836
     * $arrayy->setAndGet(1, 4); // 1
5837
     * $arrayy->setAndGet(0, 4); // 4
5838
     * </code>
5839
     *
5840
     * @param mixed $key      <p>The key</p>
5841
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5842
     *
5843
     * @return mixed
5844
     *               <p>(Mutable)</p>
5845
     */
5846 11
    public function setAndGet($key, $fallback = null)
5847
    {
5848 11
        $this->generatorToArray();
5849
5850
        // If the key doesn't exist, set it.
5851 11
        if (!$this->has($key)) {
5852 4
            $this->array = $this->set($key, $fallback)->toArray();
5853
        }
5854
5855 11
        return $this->get($key);
5856
    }
5857
5858
    /**
5859
     * Shifts a specified value off the beginning of array.
5860
     *
5861
     * @return mixed
5862
     *               <p>(Mutable) A shifted element from the current array.</p>
5863
     */
5864 5
    public function shift()
5865
    {
5866 5
        $this->generatorToArray();
5867
5868 5
        return \array_shift($this->array);
5869
    }
5870
5871
    /**
5872
     * Shuffle the current array.
5873
     *
5874
     * EXAMPLE: <code>
5875
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5876
     * </code>
5877
     *
5878
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5879
     * @param array $array  [optional]
5880
     *
5881
     * @return static
5882
     *                <p>(Immutable)</p>
5883
     *
5884
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5885
     * @psalm-return static<TKey,T>
5886
     *
5887
     * @noinspection BadExceptionsProcessingInspection
5888
     * @noinspection RandomApiMigrationInspection
5889
     * @noinspection NonSecureShuffleUsageInspection
5890
     */
5891 2
    public function shuffle(bool $secure = false, array $array = null): self
5892
    {
5893 2
        if ($array === null) {
5894 2
            $array = $this->toArray(false);
5895
        }
5896
5897 2
        if ($secure !== true) {
5898 2
            \shuffle($array);
5899
        } else {
5900 1
            $size = \count($array, \COUNT_NORMAL);
5901 1
            $keys = \array_keys($array);
5902 1
            for ($i = $size - 1; $i > 0; --$i) {
5903
                try {
5904 1
                    $r = \random_int(0, $i);
5905
                } catch (\Exception $e) {
5906
                    $r = \mt_rand(0, $i);
5907
                }
5908 1
                if ($r !== $i) {
5909 1
                    $temp = $array[$keys[$r]];
5910 1
                    $array[$keys[$r]] = $array[$keys[$i]];
5911 1
                    $array[$keys[$i]] = $temp;
5912
                }
5913
            }
5914
        }
5915
5916 2
        foreach ($array as $key => $value) {
5917
            // check if recursive is needed
5918 2
            if (\is_array($value)) {
5919 2
                $array[$key] = $this->shuffle($secure, $value);
5920
            }
5921
        }
5922
5923 2
        return static::create(
5924 2
            $array,
5925 2
            $this->iteratorClass,
5926 2
            false
5927
        );
5928
    }
5929
5930
    /**
5931
     * Count the values from the current array.
5932
     *
5933
     * alias: for "Arrayy->count()"
5934
     *
5935
     * @param int $mode
5936
     *
5937
     * @return int
5938
     */
5939 20
    public function size(int $mode = \COUNT_NORMAL): int
5940
    {
5941 20
        return $this->count($mode);
5942
    }
5943
5944
    /**
5945
     * Checks whether array has exactly $size items.
5946
     *
5947
     * @param int $size
5948
     *
5949
     * @return bool
5950
     */
5951 1
    public function sizeIs(int $size): bool
5952
    {
5953
        // init
5954 1
        $itemsTempCount = 0;
5955
5956
        /** @noinspection PhpUnusedLocalVariableInspection */
5957
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5958 1
        foreach ($this->getGeneratorByReference() as &$value) {
5959 1
            ++$itemsTempCount;
5960 1
            if ($itemsTempCount > $size) {
5961 1
                return false;
5962
            }
5963
        }
5964
5965 1
        return $itemsTempCount === $size;
5966
    }
5967
5968
    /**
5969
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5970
     * smaller than $fromSize.
5971
     *
5972
     * @param int $fromSize
5973
     * @param int $toSize
5974
     *
5975
     * @return bool
5976
     */
5977 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5978
    {
5979 1
        if ($fromSize > $toSize) {
5980 1
            $tmp = $toSize;
5981 1
            $toSize = $fromSize;
5982 1
            $fromSize = $tmp;
5983
        }
5984
5985
        // init
5986 1
        $itemsTempCount = 0;
5987
5988 1
        foreach ($this->getGenerator() as $key => $value) {
5989 1
            ++$itemsTempCount;
5990 1
            if ($itemsTempCount > $toSize) {
5991 1
                return false;
5992
            }
5993
        }
5994
5995 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5996
    }
5997
5998
    /**
5999
     * Checks whether array has more than $size items.
6000
     *
6001
     * @param int $size
6002
     *
6003
     * @return bool
6004
     */
6005 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...
6006
    {
6007
        // init
6008 1
        $itemsTempCount = 0;
6009
6010 1
        foreach ($this->getGenerator() as $key => $value) {
6011 1
            ++$itemsTempCount;
6012 1
            if ($itemsTempCount > $size) {
6013 1
                return true;
6014
            }
6015
        }
6016
6017 1
        return $itemsTempCount > $size;
6018
    }
6019
6020
    /**
6021
     * Checks whether array has less than $size items.
6022
     *
6023
     * @param int $size
6024
     *
6025
     * @return bool
6026
     */
6027 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...
6028
    {
6029
        // init
6030 1
        $itemsTempCount = 0;
6031
6032 1
        foreach ($this->getGenerator() as $key => $value) {
6033 1
            ++$itemsTempCount;
6034 1
            if ($itemsTempCount > $size) {
6035 1
                return false;
6036
            }
6037
        }
6038
6039 1
        return $itemsTempCount < $size;
6040
    }
6041
6042
    /**
6043
     * Counts all elements in an array, or something in an object.
6044
     *
6045
     * <p>
6046
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6047
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6048
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6049
     * implemented and used in PHP.
6050
     * </p>
6051
     *
6052
     * @return int
6053
     *             <p>
6054
     *             The number of elements in var, which is
6055
     *             typically an array, since anything else will have one
6056
     *             element.
6057
     *             </p>
6058
     *             <p>
6059
     *             If var is not an array or an object with
6060
     *             implemented Countable interface,
6061
     *             1 will be returned.
6062
     *             There is one exception, if var is &null;,
6063
     *             0 will be returned.
6064
     *             </p>
6065
     *             <p>
6066
     *             Caution: count may return 0 for a variable that isn't set,
6067
     *             but it may also return 0 for a variable that has been initialized with an
6068
     *             empty array. Use isset to test if a variable is set.
6069
     *             </p>
6070
     */
6071 10
    public function sizeRecursive(): int
6072
    {
6073 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6074
    }
6075
6076
    /**
6077
     * Extract a slice of the array.
6078
     *
6079
     * @param int      $offset       <p>Slice begin index.</p>
6080
     * @param int|null $length       <p>Length of the slice.</p>
6081
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6082
     *
6083
     * @return static
6084
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6085
     *
6086
     * @psalm-return static<TKey,T>
6087
     * @psalm-mutation-free
6088
     */
6089 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6090
    {
6091 5
        return static::create(
6092 5
            \array_slice(
6093 5
                $this->toArray(),
6094 5
                $offset,
6095 5
                $length,
6096 5
                $preserveKeys
6097
            ),
6098 5
            $this->iteratorClass,
6099 5
            false
6100
        );
6101
    }
6102
6103
    /**
6104
     * Sort the current array and optional you can keep the keys.
6105
     *
6106
     * EXAMPLE: <code>
6107
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6108
     * </code>
6109
     *
6110
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6111
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6112
     *                              <strong>SORT_NATURAL</strong></p>
6113
     * @param bool       $keepKeys
6114
     *
6115
     * @return static
6116
     *                <p>(Mutable) Return this Arrayy object.</p>
6117
     *
6118
     * @psalm-return static<TKey,T>
6119
     */
6120 20
    public function sort(
6121
        $direction = \SORT_ASC,
6122
        int $strategy = \SORT_REGULAR,
6123
        bool $keepKeys = false
6124
    ): self {
6125 20
        $this->generatorToArray();
6126
6127 20
        return $this->sorting(
6128 20
            $this->array,
6129 20
            $direction,
6130 20
            $strategy,
6131 20
            $keepKeys
6132
        );
6133
    }
6134
6135
    /**
6136
     * Sort the current array and optional you can keep the keys.
6137
     *
6138
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6139
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6140
     *                              <strong>SORT_NATURAL</strong></p>
6141
     * @param bool       $keepKeys
6142
     *
6143
     * @return static
6144
     *                <p>(Immutable) Return this Arrayy object.</p>
6145
     *
6146
     * @psalm-return static<TKey,T>
6147
     */
6148 12
    public function sortImmutable(
6149
        $direction = \SORT_ASC,
6150
        int $strategy = \SORT_REGULAR,
6151
        bool $keepKeys = false
6152
    ): self {
6153 12
        $that = clone $this;
6154
6155 12
        $that->generatorToArray();
6156
6157 12
        return $that->sorting(
6158 12
            $that->array,
6159 12
            $direction,
6160 12
            $strategy,
6161 12
            $keepKeys
6162
        );
6163
    }
6164
6165
    /**
6166
     * Sort the current array by key.
6167
     *
6168
     * EXAMPLE: <code>
6169
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6170
     * </code>
6171
     *
6172
     * @see http://php.net/manual/en/function.ksort.php
6173
     * @see http://php.net/manual/en/function.krsort.php
6174
     *
6175
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6176
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6177
     *                              <strong>SORT_NATURAL</strong></p>
6178
     *
6179
     * @return $this
6180
     *               <p>(Mutable) Return this Arrayy object.</p>
6181
     *
6182
     * @psalm-return static<TKey,T>
6183
     */
6184 18
    public function sortKeys(
6185
        $direction = \SORT_ASC,
6186
        int $strategy = \SORT_REGULAR
6187
    ): self {
6188 18
        $this->generatorToArray();
6189
6190 18
        $this->sorterKeys($this->array, $direction, $strategy);
6191
6192 18
        return $this;
6193
    }
6194
6195
    /**
6196
     * Sort the current array by key.
6197
     *
6198
     * @see          http://php.net/manual/en/function.ksort.php
6199
     * @see          http://php.net/manual/en/function.krsort.php
6200
     *
6201
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6202
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6203
     *                              <strong>SORT_NATURAL</strong></p>
6204
     *
6205
     * @return $this
6206
     *               <p>(Immutable) Return this Arrayy object.</p>
6207
     *
6208
     * @psalm-return static<TKey,T>
6209
     * @psalm-mutation-free
6210
     */
6211 8
    public function sortKeysImmutable(
6212
        $direction = \SORT_ASC,
6213
        int $strategy = \SORT_REGULAR
6214
    ): self {
6215 8
        $that = clone $this;
6216
6217
        /**
6218
         * @psalm-suppress ImpureMethodCall - object is already cloned
6219
         */
6220 8
        $that->sortKeys($direction, $strategy);
6221
6222 8
        return $that;
6223
    }
6224
6225
    /**
6226
     * Sort the current array by value.
6227
     *
6228
     * EXAMPLE: <code>
6229
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6230
     * </code>
6231
     *
6232
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6233
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6234
     *                              <strong>SORT_NATURAL</strong></p>
6235
     *
6236
     * @return static
6237
     *                <p>(Mutable)</p>
6238
     *
6239
     * @psalm-return static<TKey,T>
6240
     */
6241 1
    public function sortValueKeepIndex(
6242
        $direction = \SORT_ASC,
6243
        int $strategy = \SORT_REGULAR
6244
    ): self {
6245 1
        return $this->sort($direction, $strategy, true);
6246
    }
6247
6248
    /**
6249
     * Sort the current array by value.
6250
     *
6251
     * EXAMPLE: <code>
6252
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6253
     * </code>
6254
     *
6255
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6256
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6257
     *                              <strong>SORT_NATURAL</strong></p>
6258
     *
6259
     * @return static
6260
     *                <p>(Mutable)</p>
6261
     *
6262
     * @psalm-return static<TKey,T>
6263
     */
6264 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6265
    {
6266 1
        return $this->sort($direction, $strategy, false);
6267
    }
6268
6269
    /**
6270
     * Sort a array by value or by a closure.
6271
     *
6272
     * - If the sorter is null, the array is sorted naturally.
6273
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6274
     *
6275
     * EXAMPLE: <code>
6276
     * $testArray = range(1, 5);
6277
     * $under = a($testArray)->sorter(
6278
     *     function ($value) {
6279
     *         return $value % 2 === 0;
6280
     *     }
6281
     * );
6282
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6283
     * </code>
6284
     *
6285
     * @param callable|string|null $sorter
6286
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6287
     *                                        <strong>SORT_DESC</strong></p>
6288
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6289
     *                                        <strong>SORT_NATURAL</strong></p>
6290
     *
6291
     * @return static
6292
     *                <p>(Immutable)</p>
6293
     *
6294
     * @psalm-return static<TKey,T>
6295
     * @psalm-mutation-free
6296
     */
6297 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6298
    {
6299 1
        $array = $this->toArray();
6300 1
        $direction = $this->getDirection($direction);
6301
6302
        // Transform all values into their results.
6303 1
        if ($sorter) {
6304 1
            $arrayy = static::create(
6305 1
                $array,
6306 1
                $this->iteratorClass,
6307 1
                false
6308
            );
6309
6310
            /**
6311
             * @psalm-suppress MissingClosureReturnType
6312
             * @psalm-suppress MissingClosureParamType
6313
             */
6314 1
            $results = $arrayy->each(
6315 1
                static function ($value) use ($sorter) {
6316 1
                    if (\is_callable($sorter) === true) {
6317 1
                        return $sorter($value);
6318
                    }
6319
6320 1
                    return $sorter === $value;
6321 1
                }
6322
            );
6323
6324 1
            $results = $results->toArray();
6325
        } else {
6326 1
            $results = $array;
6327
        }
6328
6329
        // Sort by the results and replace by original values
6330 1
        \array_multisort($results, $direction, $strategy, $array);
6331
6332 1
        return static::create(
6333 1
            $array,
6334 1
            $this->iteratorClass,
6335 1
            false
6336
        );
6337
    }
6338
6339
    /**
6340
     * @param int      $offset
6341
     * @param int|null $length
6342
     * @param array    $replacement
6343
     *
6344
     * @return static
6345
     *                <p>(Immutable)</p>
6346
     *
6347
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6348
     * @psalm-return static<TKey,T>
6349
     * @psalm-mutation-free
6350
     */
6351 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6352
    {
6353 1
        $tmpArray = $this->toArray();
6354
6355 1
        \array_splice(
6356 1
            $tmpArray,
6357 1
            $offset,
6358 1
            $length ?? $this->count(),
6359 1
            $replacement
6360
        );
6361
6362 1
        return static::create(
6363 1
            $tmpArray,
6364 1
            $this->iteratorClass,
6365 1
            false
6366
        );
6367
    }
6368
6369
    /**
6370
     * Split an array in the given amount of pieces.
6371
     *
6372
     * EXAMPLE: <code>
6373
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6374
     * </code>
6375
     *
6376
     * @param int  $numberOfPieces
6377
     * @param bool $keepKeys
6378
     *
6379
     * @return static
6380
     *                <p>(Immutable)</p>
6381
     *
6382
     * @psalm-return static<TKey,T>
6383
     * @psalm-mutation-free
6384
     */
6385 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6386
    {
6387 1
        if ($keepKeys) {
6388 1
            $generator = function () use ($numberOfPieces) {
6389 1
                $carry = [];
6390 1
                $i = 1;
6391 1
                foreach ($this->getGenerator() as $key => $value) {
6392 1
                    $carry[$key] = $value;
6393
6394 1
                    if ($i % $numberOfPieces !== 0) {
6395 1
                        ++$i;
6396
6397 1
                        continue;
6398
                    }
6399
6400 1
                    yield $carry;
6401
6402 1
                    $carry = [];
6403 1
                    $i = 1;
6404
                }
6405
6406 1
                if ($carry !== []) {
6407 1
                    yield $carry;
6408
                }
6409 1
            };
6410 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...
6411 1
            $generator = function () use ($numberOfPieces) {
6412 1
                $carry = [];
6413 1
                $i = 1;
6414 1
                foreach ($this->getGenerator() as $key => $value) {
6415 1
                    $carry[] = $value;
6416
6417 1
                    if ($i % $numberOfPieces !== 0) {
6418 1
                        ++$i;
6419
6420 1
                        continue;
6421
                    }
6422
6423 1
                    yield $carry;
6424
6425 1
                    $carry = [];
6426 1
                    $i = 1;
6427
                }
6428
6429 1
                if ($carry !== []) {
6430 1
                    yield $carry;
6431
                }
6432 1
            };
6433
        }
6434
6435 1
        return static::create(
6436 1
            $generator,
6437 1
            $this->iteratorClass,
6438 1
            false
6439
        );
6440
    }
6441
6442
    /**
6443
     * Strip all empty items from the current array.
6444
     *
6445
     * EXAMPLE: <code>
6446
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6447
     * </code>
6448
     *
6449
     * @return static
6450
     *                <p>(Immutable)</p>
6451
     *
6452
     * @psalm-return static<TKey,T>
6453
     * @psalm-mutation-free
6454
     */
6455 1
    public function stripEmpty(): self
6456
    {
6457 1
        return $this->filter(
6458 1
            static function ($item) {
6459 1
                if ($item === null) {
6460 1
                    return false;
6461
                }
6462
6463 1
                return (bool) \trim((string) $item);
6464 1
            }
6465
        );
6466
    }
6467
6468
    /**
6469
     * Swap two values between positions by key.
6470
     *
6471
     * EXAMPLE: <code>
6472
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6473
     * </code>
6474
     *
6475
     * @param int|string $swapA <p>a key in the array</p>
6476
     * @param int|string $swapB <p>a key in the array</p>
6477
     *
6478
     * @return static
6479
     *                <p>(Immutable)</p>
6480
     *
6481
     * @psalm-return static<TKey,T>
6482
     * @psalm-mutation-free
6483
     */
6484 1
    public function swap($swapA, $swapB): self
6485
    {
6486 1
        $array = $this->toArray();
6487
6488 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6489
6490 1
        return static::create(
6491 1
            $array,
6492 1
            $this->iteratorClass,
6493 1
            false
6494
        );
6495
    }
6496
6497
    /**
6498
     * Get the current array from the "Arrayy"-object.
6499
     * alias for "getArray()"
6500
     *
6501
     * @param bool $convertAllArrayyElements <p>
6502
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6503
     *                                       </p>
6504
     * @param bool $preserveKeys             <p>
6505
     *                                       e.g.: A generator maybe return the same key more then once,
6506
     *                                       so maybe you will ignore the keys.
6507
     *                                       </p>
6508
     *
6509
     * @return array
6510
     *
6511
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6512
     * @psalm-mutation-free
6513
     */
6514 931
    public function toArray(
6515
        bool $convertAllArrayyElements = false,
6516
        bool $preserveKeys = true
6517
    ): array {
6518
        // init
6519 931
        $array = [];
6520
6521 931
        if ($convertAllArrayyElements) {
6522 2
            foreach ($this->getGenerator() as $key => $value) {
6523 2
                if ($value instanceof self) {
6524 1
                    $value = $value->toArray(
6525 1
                        $convertAllArrayyElements,
6526 1
                        $preserveKeys
6527
                    );
6528
                }
6529
6530 2
                if ($preserveKeys) {
6531 1
                    $array[$key] = $value;
6532
                } else {
6533 2
                    $array[] = $value;
6534
                }
6535
            }
6536
        } else {
6537 931
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6538
        }
6539
6540 931
        return $array;
6541
    }
6542
6543
    /**
6544
     * Get the current array from the "Arrayy"-object as list.
6545
     *
6546
     * @param bool $convertAllArrayyElements <p>
6547
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6548
     *                                       </p>
6549
     *
6550
     * @return array
6551
     *
6552
     * @psalm-return list<array<TKey,T>>
6553
     * @psalm-mutation-free
6554
     */
6555 1
    public function toList(bool $convertAllArrayyElements = false): array
6556
    {
6557 1
        return $this->toArray(
6558 1
            $convertAllArrayyElements,
6559 1
            false
6560
        );
6561
    }
6562
6563
    /**
6564
     * Convert the current array to JSON.
6565
     *
6566
     * EXAMPLE: <code>
6567
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6568
     * </code>
6569
     *
6570
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6571
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6572
     *
6573
     * @return string
6574
     */
6575 12
    public function toJson(int $options = 0, int $depth = 512): string
6576
    {
6577 12
        $return = \json_encode($this->toArray(), $options, $depth);
6578 12
        if ($return === false) {
6579
            return '';
6580
        }
6581
6582 12
        return $return;
6583
    }
6584
6585
    /**
6586
     * @param string[]|null $items  [optional]
6587
     * @param string[]      $helper [optional]
6588
     *
6589
     * @return static|static[]
6590
     *
6591
     * @psalm-return static<int, static<TKey,T>>
6592
     */
6593 1
    public function toPermutation(array $items = null, array $helper = []): self
6594
    {
6595
        // init
6596 1
        $return = [];
6597
6598 1
        if ($items === null) {
6599 1
            $items = $this->toArray();
6600
        }
6601
6602 1
        if (empty($items)) {
6603 1
            $return[] = $helper;
6604
        } else {
6605 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6606 1
                $new_items = $items;
6607 1
                $new_helper = $helper;
6608 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6609
                /** @noinspection PhpSillyAssignmentInspection */
6610
                /** @var string[] $new_items */
6611 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...
6612 1
                \array_unshift($new_helper, $tmp_helper);
6613
                /** @noinspection SlowArrayOperationsInLoopInspection */
6614 1
                $return = \array_merge(
6615 1
                    $return,
6616 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6617
                );
6618
            }
6619
        }
6620
6621 1
        return static::create(
6622 1
            $return,
6623 1
            $this->iteratorClass,
6624 1
            false
6625
        );
6626
    }
6627
6628
    /**
6629
     * Implodes array to a string with specified separator.
6630
     *
6631
     * @param string $separator [optional] <p>The element's separator.</p>
6632
     *
6633
     * @return string
6634
     *                <p>The string representation of array, separated by ",".</p>
6635
     */
6636 19
    public function toString(string $separator = ','): string
6637
    {
6638 19
        return $this->implode($separator);
6639
    }
6640
6641
    /**
6642
     * Return a duplicate free copy of the current array.
6643
     *
6644
     * EXAMPLE: <code>
6645
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6646
     * </code>
6647
     *
6648
     * @return $this
6649
     *               <p>(Mutable)</p>
6650
     *
6651
     * @psalm-return static<TKey,T>
6652
     */
6653 13
    public function uniqueNewIndex(): self
6654
    {
6655
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6656
6657
        /**
6658
         * @psalm-suppress MissingClosureReturnType
6659
         * @psalm-suppress MissingClosureParamType
6660
         */
6661 13
        $this->array = $this->reduce(
6662 13
            static function ($resultArray, $value) {
6663 12
                if (!\in_array($value, $resultArray, true)) {
6664 12
                    $resultArray[] = $value;
6665
                }
6666
6667 12
                return $resultArray;
6668 13
            },
6669 13
            []
6670 13
        )->toArray();
6671 13
        $this->generator = null;
6672
6673 13
        return $this;
6674
    }
6675
6676
    /**
6677
     * Return a duplicate free copy of the current array. (with the old keys)
6678
     *
6679
     * EXAMPLE: <code>
6680
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6681
     * </code>
6682
     *
6683
     * @return $this
6684
     *               <p>(Mutable)</p>
6685
     *
6686
     * @psalm-return static<TKey,T>
6687
     */
6688 11
    public function uniqueKeepIndex(): self
6689
    {
6690
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6691
6692
        // init
6693 11
        $array = $this->toArray();
6694
6695
        /**
6696
         * @psalm-suppress MissingClosureReturnType
6697
         * @psalm-suppress MissingClosureParamType
6698
         */
6699 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...
6700 11
            \array_keys($array),
6701 11
            static function ($resultArray, $key) use ($array) {
6702 10
                if (!\in_array($array[$key], $resultArray, true)) {
6703 10
                    $resultArray[$key] = $array[$key];
6704
                }
6705
6706 10
                return $resultArray;
6707 11
            },
6708 11
            []
6709
        );
6710 11
        $this->generator = null;
6711
6712 11
        return $this;
6713
    }
6714
6715
    /**
6716
     * alias: for "Arrayy->uniqueNewIndex()"
6717
     *
6718
     * @return static
6719
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6720
     *
6721
     * @see          Arrayy::unique()
6722
     *
6723
     * @psalm-return static<TKey,T>
6724
     */
6725 13
    public function unique(): self
6726
    {
6727 13
        return $this->uniqueNewIndex();
6728
    }
6729
6730
    /**
6731
     * Prepends one or more values to the beginning of array at once.
6732
     *
6733
     * @param array ...$args
6734
     *
6735
     * @return $this
6736
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6737
     *
6738
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6739
     * @psalm-return static<TKey,T>
6740
     */
6741 4
    public function unshift(...$args): self
6742
    {
6743 4
        $this->generatorToArray();
6744
6745 4
        \array_unshift($this->array, ...$args);
6746
6747 4
        return $this;
6748
    }
6749
6750
    /**
6751
     * Tests whether the given closure return something valid for all elements of this array.
6752
     *
6753
     * @param \Closure $closure the predicate
6754
     *
6755
     * @return bool
6756
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6757
     */
6758 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...
6759
    {
6760 1
        foreach ($this->getGenerator() as $key => $value) {
6761 1
            if (!$closure($value, $key)) {
6762 1
                return false;
6763
            }
6764
        }
6765
6766 1
        return true;
6767
    }
6768
6769
    /**
6770
     * Get all values from a array.
6771
     *
6772
     * EXAMPLE: <code>
6773
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6774
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6775
     * </code>
6776
     *
6777
     * @return static
6778
     *                <p>(Immutable)</p>
6779
     *
6780
     * @psalm-return static<TKey,T>
6781
     * @psalm-mutation-free
6782
     */
6783 2
    public function values(): self
6784
    {
6785 2
        return static::create(
6786 2
            function () {
6787
                /** @noinspection YieldFromCanBeUsedInspection */
6788 2
                foreach ($this->getGenerator() as $value) {
6789 2
                    yield $value;
6790
                }
6791 2
            },
6792 2
            $this->iteratorClass,
6793 2
            false
6794
        );
6795
    }
6796
6797
    /**
6798
     * Apply the given function to every element in the array, discarding the results.
6799
     *
6800
     * EXAMPLE: <code>
6801
     * $callable = function (&$value, $key) {
6802
     *     $value = $key;
6803
     * };
6804
     * $arrayy = a([1, 2, 3]);
6805
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6806
     * </code>
6807
     *
6808
     * @param callable $callable
6809
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6810
     * @param mixed    $userData  [optional] <p>
6811
     *                            If the optional $userData parameter is supplied,
6812
     *                            it will be passed as the third parameter to the $callable.
6813
     *                            </p>
6814
     *
6815
     * @return $this
6816
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6817
     *
6818
     * @psalm-return static<TKey,T>
6819
     */
6820 12
    public function walk(
6821
        $callable,
6822
        bool $recursive = false,
6823
        $userData = self::ARRAYY_HELPER_WALK
6824
    ): self {
6825 12
        $this->generatorToArray();
6826
6827 12
        if ($this->array !== []) {
6828 10
            if ($recursive === true) {
6829 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6830
                    \array_walk_recursive($this->array, $callable, $userData);
6831
                } else {
6832 5
                    \array_walk_recursive($this->array, $callable);
6833
                }
6834
            } else {
6835
                /** @noinspection NestedPositiveIfStatementsInspection */
6836 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6837
                    \array_walk($this->array, $callable, $userData);
6838
                } else {
6839 5
                    \array_walk($this->array, $callable);
6840
                }
6841
            }
6842
        }
6843
6844 12
        return $this;
6845
    }
6846
6847
    /**
6848
     * Returns a collection of matching items.
6849
     *
6850
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6851
     * @param mixed  $value                 the value to match
6852
     *
6853
     * @throws \InvalidArgumentException if property or method is not defined
6854
     *
6855
     * @return static
6856
     *
6857
     * @psalm-param  T $value
6858
     * @psalm-return static<TKey,T>
6859
     */
6860 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6861
    {
6862 1
        return $this->filter(
6863 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6864
                $accessorValue = $this->extractValue(
6865
                    $item,
6866
                    $keyOrPropertyOrMethod
6867
                );
6868
6869
                return $accessorValue === $value;
6870 1
            }
6871
        );
6872
    }
6873
6874
    /**
6875
     * Convert an array into a object.
6876
     *
6877
     * @param array $array
6878
     *
6879
     * @return \stdClass
6880
     *
6881
     * @psalm-param array<mixed,mixed> $array
6882
     */
6883 4
    final protected static function arrayToObject(array $array = []): \stdClass
6884
    {
6885
        // init
6886 4
        $object = new \stdClass();
6887
6888 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6889 1
            return $object;
6890
        }
6891
6892 3
        foreach ($array as $name => $value) {
6893 3
            if (\is_array($value)) {
6894 1
                $object->{$name} = static::arrayToObject($value);
6895
            } else {
6896 3
                $object->{$name} = $value;
6897
            }
6898
        }
6899
6900 3
        return $object;
6901
    }
6902
6903
    /**
6904
     * @param array|\Generator|null $input         <p>
6905
     *                                             An array containing keys to return.
6906
     *                                             </p>
6907
     * @param mixed|null            $search_values [optional] <p>
6908
     *                                             If specified, then only keys containing these values are returned.
6909
     *                                             </p>
6910
     * @param bool                  $strict        [optional] <p>
6911
     *                                             Determines if strict comparison (===) should be used during the
6912
     *                                             search.
6913
     *                                             </p>
6914
     *
6915
     * @return array
6916
     *               <p>an array of all the keys in input</p>
6917
     *
6918
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6919
     * @psalm-return array<TKey|mixed>
6920
     * @psalm-mutation-free
6921
     */
6922 11
    protected function array_keys_recursive(
6923
        $input = null,
6924
        $search_values = null,
6925
        bool $strict = true
6926
    ): array {
6927
        // init
6928 11
        $keys = [];
6929 11
        $keysTmp = [];
6930
6931 11
        if ($input === null) {
6932 4
            $input = $this->getGenerator();
6933
        }
6934
6935 11
        if ($search_values === null) {
6936 11
            foreach ($input as $key => $value) {
6937 11
                $keys[] = $key;
6938
6939
                // check if recursive is needed
6940 11
                if (\is_array($value)) {
6941 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6942
                }
6943
            }
6944
        } else {
6945 1
            $is_array_tmp = \is_array($search_values);
6946
6947 1
            foreach ($input as $key => $value) {
6948 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...
6949
                    (
6950 1
                        $is_array_tmp === false
6951
                        &&
6952 1
                        $strict === true
6953
                        &&
6954 1
                        $search_values === $value
6955
                    )
6956
                    ||
6957
                    (
6958 1
                        $is_array_tmp === false
6959
                        &&
6960 1
                        $strict === false
6961
                        &&
6962 1
                        $search_values == $value
6963
                    )
6964
                    ||
6965
                    (
6966 1
                        $is_array_tmp === true
6967
                        &&
6968 1
                        \in_array($value, $search_values, $strict)
6969
                    )
6970
                ) {
6971 1
                    $keys[] = $key;
6972
                }
6973
6974
                // check if recursive is needed
6975 1
                if (\is_array($value)) {
6976 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6977
                }
6978
            }
6979
        }
6980
6981 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6982
    }
6983
6984
    /**
6985
     * @param mixed      $path
6986
     * @param callable   $callable
6987
     * @param array|null $currentOffset
6988
     *
6989
     * @return void
6990
     *
6991
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6992
     * @psalm-mutation-free
6993
     */
6994 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6995
    {
6996 10
        $this->generatorToArray();
6997
6998 10
        if ($currentOffset === null) {
6999 10
            $currentOffset = &$this->array;
7000
        }
7001
7002 10
        $explodedPath = \explode($this->pathSeparator, $path);
7003 10
        if ($explodedPath === false) {
7004
            return;
7005
        }
7006
7007 10
        $nextPath = \array_shift($explodedPath);
7008
7009 10
        if (!isset($currentOffset[$nextPath])) {
7010 1
            return;
7011
        }
7012
7013 9
        if (!empty($explodedPath)) {
7014 1
            $this->callAtPath(
7015 1
                \implode($this->pathSeparator, $explodedPath),
7016 1
                $callable,
7017 1
                $currentOffset[$nextPath]
7018
            );
7019
        } else {
7020 9
            $callable($currentOffset[$nextPath]);
7021
        }
7022 9
    }
7023
7024
    /**
7025
     * Extracts the value of the given property or method from the object.
7026
     *
7027
     * @param static $object                <p>The object to extract the value from.</p>
7028
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
7029
     *                                      value should be extracted.</p>
7030
     *
7031
     * @throws \InvalidArgumentException if the method or property is not defined
7032
     *
7033
     * @return mixed
7034
     *               <p>The value extracted from the specified property or method.</p>
7035
     *
7036
     * @psalm-param self<TKey,T> $object
7037
     */
7038 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7039
    {
7040 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7041 1
            $return = $object->get($keyOrPropertyOrMethod);
7042
7043 1
            if ($return instanceof self) {
7044
                return $return->toArray();
7045
            }
7046
7047 1
            return $return;
7048
        }
7049
7050
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7051
            return $object->{$keyOrPropertyOrMethod};
7052
        }
7053
7054
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7055
            return $object->{$keyOrPropertyOrMethod}();
7056
        }
7057
7058
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7059
    }
7060
7061
    /**
7062
     * create a fallback for array
7063
     *
7064
     * 1. use the current array, if it's a array
7065
     * 2. fallback to empty array, if there is nothing
7066
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7067
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7068
     * 5. call "__toArray()" on object, if the method exists
7069
     * 6. cast a string or object with "__toString()" into an array
7070
     * 7. throw a "InvalidArgumentException"-Exception
7071
     *
7072
     * @param mixed $data
7073
     *
7074
     * @throws \InvalidArgumentException
7075
     *
7076
     * @return array
7077
     *
7078
     * @psalm-return array<mixed,mixed>|array<TKey,T>
7079
     */
7080 1206
    protected function fallbackForArray(&$data): array
7081
    {
7082 1206
        $data = $this->internalGetArray($data);
7083
7084 1206
        if ($data === null) {
7085 2
            throw new \InvalidArgumentException('Passed value should be a array');
7086
        }
7087
7088 1204
        return $data;
7089
    }
7090
7091
    /**
7092
     * @param bool $preserveKeys <p>
7093
     *                           e.g.: A generator maybe return the same key more then once,
7094
     *                           so maybe you will ignore the keys.
7095
     *                           </p>
7096
     *
7097
     * @return bool
7098
     *
7099
     * @noinspection ReturnTypeCanBeDeclaredInspection
7100
     * @psalm-mutation-free :/
7101
     */
7102 1118
    protected function generatorToArray(bool $preserveKeys = true)
7103
    {
7104 1118
        if ($this->generator) {
7105 2
            $this->array = $this->toArray(false, $preserveKeys);
7106 2
            $this->generator = null;
7107
7108 2
            return true;
7109
        }
7110
7111 1118
        return false;
7112
    }
7113
7114
    /**
7115
     * Get correct PHP constant for direction.
7116
     *
7117
     * @param int|string $direction
7118
     *
7119
     * @return int
7120
     * @psalm-mutation-free
7121
     */
7122 43
    protected function getDirection($direction): int
7123
    {
7124 43
        if ((string) $direction === $direction) {
7125 10
            $direction = \strtolower($direction);
7126
7127 10
            if ($direction === 'desc') {
7128 2
                $direction = \SORT_DESC;
7129
            } else {
7130 9
                $direction = \SORT_ASC;
7131
            }
7132
        }
7133
7134
        if (
7135 43
            $direction !== \SORT_DESC
7136
            &&
7137 43
            $direction !== \SORT_ASC
7138
        ) {
7139
            $direction = \SORT_ASC;
7140
        }
7141
7142 43
        return $direction;
7143
    }
7144
7145
    /**
7146
     * @return TypeCheckInterface[]
7147
     *
7148
     * @noinspection ReturnTypeCanBeDeclaredInspection
7149
     */
7150 24
    protected function getPropertiesFromPhpDoc()
7151
    {
7152 24
        static $PROPERTY_CACHE = [];
7153 24
        $cacheKey = 'Class::' . static::class;
7154
7155 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7156 22
            return $PROPERTY_CACHE[$cacheKey];
7157
        }
7158
7159
        // init
7160 4
        $properties = [];
7161
7162 4
        $reflector = new \ReflectionClass($this);
7163 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7164 4
        $docComment = $reflector->getDocComment();
7165 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...
7166 3
            $docblock = $factory->create($docComment);
7167
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7168 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7169 3
                $typeName = $tag->getVariableName();
7170
                /** @var string|null $typeName */
7171 3
                if ($typeName !== null) {
7172 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7173 3
                    if ($typeCheckPhpDoc !== null) {
7174 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7175
                    }
7176
                }
7177
            }
7178
        }
7179
7180
        /** @noinspection PhpAssignmentInConditionInspection */
7181 4
        while ($reflector = $reflector->getParentClass()) {
7182 4
            $docComment = $reflector->getDocComment();
7183 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...
7184 4
                $docblock = $factory->create($docComment);
7185
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7186 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7187 1
                    $typeName = $tag->getVariableName();
7188
                    /** @var string|null $typeName */
7189 1
                    if ($typeName !== null) {
7190 1
                        if (isset($properties[$typeName])) {
7191 1
                            continue;
7192
                        }
7193
7194 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7195 1
                        if ($typeCheckPhpDoc !== null) {
7196 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7197
                        }
7198
                    }
7199
                }
7200
            }
7201
        }
7202
7203 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7204
    }
7205
7206
    /**
7207
     * @param mixed $glue
7208
     * @param mixed $pieces
7209
     * @param bool  $useKeys
7210
     *
7211
     * @return string
7212
     * @psalm-mutation-free
7213
     */
7214 36
    protected function implode_recursive(
7215
        $glue = '',
7216
        $pieces = [],
7217
        bool $useKeys = false
7218
    ): string {
7219 36
        if ($pieces instanceof self) {
7220 1
            $pieces = $pieces->toArray();
7221
        }
7222
7223 36
        if (\is_array($pieces)) {
7224 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7225 36
            $pieces_count_not_zero = $pieces_count > 0;
7226
7227 36
            return \implode(
7228 36
                $glue,
7229 36
                \array_map(
7230 36
                    [$this, 'implode_recursive'],
7231 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7232 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7233
                )
7234
            );
7235
        }
7236
7237
        if (
7238 36
            \is_scalar($pieces) === true
7239
            ||
7240 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7241
        ) {
7242 32
            return (string) $pieces;
7243
        }
7244
7245 8
        return '';
7246
    }
7247
7248
    /**
7249
     * @param mixed                 $needle   <p>
7250
     *                                        The searched value.
7251
     *                                        </p>
7252
     *                                        <p>
7253
     *                                        If needle is a string, the comparison is done
7254
     *                                        in a case-sensitive manner.
7255
     *                                        </p>
7256
     * @param array|\Generator|null $haystack <p>
7257
     *                                        The array.
7258
     *                                        </p>
7259
     * @param bool                  $strict   [optional] <p>
7260
     *                                        If the third parameter strict is set to true
7261
     *                                        then the in_array function will also check the
7262
     *                                        types of the
7263
     *                                        needle in the haystack.
7264
     *                                        </p>
7265
     *
7266
     * @return bool
7267
     *              <p>true if needle is found in the array, false otherwise</p>
7268
     *
7269
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7270
     * @psalm-mutation-free
7271
     */
7272 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7273
    {
7274 18
        if ($haystack === null) {
7275
            $haystack = $this->getGenerator();
7276
        }
7277
7278 18
        foreach ($haystack as $item) {
7279 14
            if (\is_array($item)) {
7280 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7281
            } else {
7282
                /** @noinspection NestedPositiveIfStatementsInspection */
7283 14
                if ($strict === true) {
7284 14
                    $returnTmp = $item === $needle;
7285
                } else {
7286
                    $returnTmp = $item == $needle;
7287
                }
7288
            }
7289
7290 14
            if ($returnTmp === true) {
7291 14
                return true;
7292
            }
7293
        }
7294
7295 8
        return false;
7296
    }
7297
7298
    /**
7299
     * @param mixed $data
7300
     *
7301
     * @return array|null
7302
     *
7303
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7304
     */
7305 1206
    protected function internalGetArray(&$data)
7306
    {
7307 1206
        if (\is_array($data)) {
7308 1200
            return $data;
7309
        }
7310
7311 80
        if (!$data) {
7312 7
            return [];
7313
        }
7314
7315 79
        if (\is_object($data) === true) {
7316 72
            if ($data instanceof \ArrayObject) {
7317 5
                return $data->getArrayCopy();
7318
            }
7319
7320 68
            if ($data instanceof \Generator) {
7321
                return static::createFromGeneratorImmutable($data)->toArray();
7322
            }
7323
7324 68
            if ($data instanceof \Traversable) {
7325
                return static::createFromObject($data)->toArray();
7326
            }
7327
7328 68
            if ($data instanceof \JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

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

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

2. Missing use statement

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

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

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

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