Completed
Push — master ( acfa31...ff32ef )
by Lars
01:46
created

Arrayy::asortImmutable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 11
ccs 4
cts 4
cp 1
crap 1
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
/** @noinspection ReturnTypeCanBeDeclaredInspection */
4
/** @noinspection ClassReImplementsParentInterfaceInspection */
5
6
declare(strict_types=1);
7
8
namespace Arrayy;
9
10
use Arrayy\Type\TypeInterface;
11
use Arrayy\TypeCheck\TypeCheckArray;
12
use Arrayy\TypeCheck\TypeCheckInterface;
13
use Arrayy\TypeCheck\TypeCheckPhpDoc;
14
15
/**
16
 * Methods to manage arrays.
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 *
21
 * @template TKey of array-key
22
 * @template T
23
 * @template-extends \ArrayObject<TKey,T>
24
 * @template-implements \IteratorAggregate<TKey,T>
25
 * @template-implements \ArrayAccess<TKey|null,T>
26
 */
27
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
28
{
29
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
30
31
    const ARRAYY_HELPER_WALK = '!!!!Arrayy_Helper_Walk!!!!';
32
33
    /**
34
     * @var array
35
     *
36
     * @psalm-var array<mixed,mixed>|array<TKey,T>
37
     */
38
    protected $array = [];
39
40
    /**
41
     * @var \Arrayy\ArrayyRewindableGenerator|null
42
     *
43
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
44
     */
45
    protected $generator;
46
47
    /**
48
     * @var string
49
     *
50
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
51
     */
52
    protected $iteratorClass = ArrayyIterator::class;
53
54
    /**
55
     * @var string
56
     */
57
    protected $pathSeparator = '.';
58
59
    /**
60
     * @var bool
61
     */
62
    protected $checkPropertyTypes = false;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $checkForMissingPropertiesInConstructor = false;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $checkPropertiesMismatchInConstructor = false;
73
74
    /**
75
     * @var bool
76
     */
77
    protected $checkPropertiesMismatch = true;
78
79
    /**
80
     * @var array<int|string,TypeCheckInterface>|mixed|TypeCheckArray<int|string,TypeCheckInterface>|TypeInterface
81
     */
82
    protected $properties = [];
83
84
    /**
85
     * Initializes
86
     *
87
     * @param mixed  $data                         <p>
88
     *                                             Should be an array or a generator, otherwise it will try
89
     *                                             to convert it into an array.
90
     *                                             </p>
91
     * @param string $iteratorClass                optional <p>
92
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
93
     *                                             need this option.
94
     *                                             </p>
95
     * @param bool   $checkPropertiesInConstructor optional <p>
96
     *                                             You need to extend the "Arrayy"-class and you need to set
97
     *                                             the $checkPropertiesMismatchInConstructor class property
98
     *                                             to
99
     *                                             true, otherwise this option didn't not work anyway.
100
     *                                             </p>
101
     *
102
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
103
     */
104 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 723
    public static function create(
1629
        $data = [],
1630
        string $iteratorClass = ArrayyIterator::class,
1631
        bool $checkPropertiesInConstructor = true
1632
    ) {
1633 723
        return new static(
1634 723
            $data,
1635 723
            $iteratorClass,
1636 723
            $checkPropertiesInConstructor
1637
        );
1638
    }
1639
1640
    /**
1641
     * Flatten an array with the given character as a key delimiter.
1642
     *
1643
     * EXAMPLE: <code>
1644
     * $callable = function ($a, $b) {
1645
     *     if ($a == $b) {
1646
     *         return 0;
1647
     *     }
1648
     *     return ($a > $b) ? 1 : -1;
1649
     * };
1650
     * $arrayy = a(['three' => 3, 'one' => 1, 'two' => 2]);
1651
     * $resultArrayy = $arrayy->customSortKeys($callable); // Arrayy['one' => 1, 'three' => 3, 'two' => 2]
1652
     * </code>
1653
     *
1654
     * @param string     $delimiter
1655
     * @param string     $prepend
1656
     * @param array|null $items
1657
     *
1658
     * @return array
1659
     */
1660 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1661
    {
1662
        // init
1663 2
        $flatten = [];
1664
1665 2
        if ($items === null) {
1666 2
            $items = $this->array;
1667
        }
1668
1669 2
        foreach ($items as $key => $value) {
1670 2
            if (\is_array($value) && $value !== []) {
1671 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1672
            } else {
1673 2
                $flatten[] = [$prepend . $key => $value];
1674
            }
1675
        }
1676
1677 2
        if (\count($flatten) === 0) {
1678
            return [];
1679
        }
1680
1681 2
        return \array_merge_recursive([], ...$flatten);
1682
    }
1683
1684
    /**
1685
     * WARNING: Creates an Arrayy object by reference.
1686
     *
1687
     * @param array $array
1688
     *
1689
     * @return $this
1690
     *               <p>(Mutable) Return this Arrayy object.</p>
1691
     *
1692
     * @psalm-param  array<mixed,mixed>|array<array-key,mixed> $array
1693
     */
1694 26
    public function createByReference(array &$array = []): self
1695
    {
1696 26
        $array = $this->fallbackForArray($array);
1697
1698 26
        $this->array = &$array;
1699
1700 26
        return $this;
1701
    }
1702
1703
    /**
1704
     * Create an new instance from a callable function which will return an Generator.
1705
     *
1706
     * @param callable $generatorFunction
1707
     *
1708
     * @return static
1709
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1710
     *
1711
     * @psalm-param callable():\Generator<array-key,mixed> $generatorFunction
1712
     *
1713
     * @psalm-mutation-free
1714
     */
1715 7
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1716
    {
1717 7
        return self::create($generatorFunction);
1718
    }
1719
1720
    /**
1721
     * Create an new instance filled with a copy of values from a "Generator"-object.
1722
     *
1723
     * @param \Generator $generator
1724
     *
1725
     * @return static
1726
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1727
     *
1728
     * @psalm-param \Generator<array-key,mixed> $generator
1729
     *
1730
     * @psalm-mutation-free
1731
     */
1732 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1733
    {
1734 4
        return self::create(\iterator_to_array($generator, true));
1735
    }
1736
1737
    /**
1738
     * Create an new Arrayy object via JSON.
1739
     *
1740
     * @param string $json
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1744
     *
1745
     * @psalm-mutation-free
1746
     */
1747 5
    public static function createFromJson(string $json): self
1748
    {
1749 5
        return static::create(\json_decode($json, true));
1750
    }
1751
1752
    /**
1753
     * Create an new Arrayy object via JSON.
1754
     *
1755
     * @param array $array
1756
     *
1757
     * @return static
1758
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1759
     *
1760
     * @psalm-mutation-free
1761
     */
1762 1
    public static function createFromArray(array $array): self
1763
    {
1764 1
        return static::create($array);
1765
    }
1766
1767
    /**
1768
     * Create an new instance filled with values from an object that is iterable.
1769
     *
1770
     * @param \Traversable $object <p>iterable object</p>
1771
     *
1772
     * @return static
1773
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1774
     *
1775
     * @psalm-param \Traversable<array-key,mixed> $object
1776
     *
1777
     * @psalm-mutation-free
1778
     */
1779 4
    public static function createFromObject(\Traversable $object): self
1780
    {
1781
        // init
1782 4
        $arrayy = new static();
1783
1784 4
        if ($object instanceof self) {
1785 4
            $objectArray = $object->getGenerator();
1786
        } else {
1787
            $objectArray = $object;
1788
        }
1789
1790 4
        foreach ($objectArray as $key => $value) {
1791
            /**
1792
             * @psalm-suppress ImpureMethodCall - object is already re-created
1793
             */
1794 3
            $arrayy->internalSet($key, $value);
1795
        }
1796
1797 4
        return $arrayy;
1798
    }
1799
1800
    /**
1801
     * Create an new instance filled with values from an object.
1802
     *
1803
     * @param object $object
1804
     *
1805
     * @return static
1806
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1807
     *
1808
     * @psalm-mutation-free
1809
     */
1810 5
    public static function createFromObjectVars($object): self
1811
    {
1812 5
        return self::create(self::objectToArray($object));
1813
    }
1814
1815
    /**
1816
     * Create an new Arrayy object via string.
1817
     *
1818
     * @param string      $str       <p>The input string.</p>
1819
     * @param string|null $delimiter <p>The boundary string.</p>
1820
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1821
     *                               used.</p>
1822
     *
1823
     * @return static
1824
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1825
     *
1826
     * @psalm-mutation-free
1827
     */
1828 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1829
    {
1830 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1831 1
            \preg_match_all($regEx, $str, $array);
1832
1833 1
            if (!empty($array)) {
1834 1
                $array = $array[0];
1835
            }
1836
        } else {
1837
            /** @noinspection NestedPositiveIfStatementsInspection */
1838 9
            if ($delimiter !== null) {
1839 7
                $array = \explode($delimiter, $str);
1840
            } else {
1841 2
                $array = [$str];
1842
            }
1843
        }
1844
1845
        // trim all string in the array
1846
        /**
1847
         * @psalm-suppress MissingClosureParamType
1848
         */
1849 10
        \array_walk(
1850 10
            $array,
1851 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 and return the end result.
5132
     *
5133
     * EXAMPLE: <code>
5134
     * a([1, 2, 3, 4])->reduce(
5135
     *     function ($carry, $item) {
5136
     *         return $carry * $item;
5137
     *     },
5138
     *     1
5139
     * ); // Arrayy[24]
5140
     * </code>
5141
     *
5142
     * @param callable $callable
5143
     * @param mixed    $initial
5144
     *
5145
     * @return static
5146
     *                <p>(Immutable)</p>
5147
     *
5148
     * @psalm-return static<TKey,T>
5149
     * @psalm-mutation-free
5150
     */
5151 18 View Code Duplication
    public function reduce($callable, $initial = []): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
5152
    {
5153 18
        foreach ($this->getGenerator() as $key => $value) {
5154 17
            $initial = $callable($initial, $value, $key);
5155
        }
5156
5157 18
        return static::create(
5158 18
            $initial,
5159 18
            $this->iteratorClass,
5160 18
            false
5161
        );
5162
    }
5163
5164
    /**
5165
     * @param bool $unique
5166
     *
5167
     * @return static
5168
     *                <p>(Immutable)</p>
5169
     *
5170
     * @psalm-return static<TKey,T>
5171
     * @psalm-mutation-free
5172
     */
5173 14
    public function reduce_dimension(bool $unique = true): self
5174
    {
5175
        // init
5176 14
        $result = [];
5177
5178 14
        foreach ($this->getGenerator() as $val) {
5179 12
            if (\is_array($val)) {
5180 5
                $result[] = (new static($val))->reduce_dimension($unique)->toArray();
5181
            } else {
5182 12
                $result[] = [$val];
5183
            }
5184
        }
5185
5186 14
        $result = $result === [] ? [] : \array_merge(...$result);
5187
5188 14
        $resultArrayy = new static($result);
5189
5190
        /**
5191
         * @psalm-suppress ImpureMethodCall - object is already re-created
5192
         * @psalm-suppress InvalidReturnStatement - why?
5193
         */
5194 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
5195
    }
5196
5197
    /**
5198
     * Create a numerically re-indexed Arrayy object.
5199
     *
5200
     * EXAMPLE: <code>
5201
     * a([2 => 1, 3 => 2])->reindex(); // Arrayy[0 => 1, 1 => 2]
5202
     * </code>
5203
     *
5204
     * @return $this
5205
     *               <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
5206
     *
5207
     * @psalm-return static<TKey,T>
5208
     */
5209 9
    public function reindex(): self
5210
    {
5211 9
        $this->generatorToArray(false);
5212
5213 9
        $this->array = \array_values($this->array);
5214
5215 9
        return $this;
5216
    }
5217
5218
    /**
5219
     * Return all items that fail the truth test.
5220
     *
5221
     * EXAMPLE: <code>
5222
     * $closure = function ($value) {
5223
     *     return $value % 2 !== 0;
5224
     * }
5225
     * a([1, 2, 3, 4])->reject($closure); // Arrayy[1 => 2, 3 => 4]
5226
     * </code>
5227
     *
5228
     * @param \Closure $closure
5229
     *
5230
     * @return static
5231
     *                <p>(Immutable)</p>
5232
     *
5233
     * @psalm-return static<TKey,T>
5234
     * @psalm-mutation-free
5235
     */
5236 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...
5237
    {
5238
        // init
5239 1
        $filtered = [];
5240
5241 1
        foreach ($this->getGenerator() as $key => $value) {
5242 1
            if (!$closure($value, $key)) {
5243 1
                $filtered[$key] = $value;
5244
            }
5245
        }
5246
5247 1
        return static::create(
5248 1
            $filtered,
5249 1
            $this->iteratorClass,
5250 1
            false
5251
        );
5252
    }
5253
5254
    /**
5255
     * Remove a value from the current array (optional using dot-notation).
5256
     *
5257
     * EXAMPLE: <code>
5258
     * a([1 => 'bar', 'foo' => 'foo'])->remove(1); // Arrayy['foo' => 'foo']
5259
     * </code>
5260
     *
5261
     * @param mixed $key
5262
     *
5263
     * @return static
5264
     *                <p>(Mutable)</p>
5265
     *
5266
     * @psalm-param  TKey $key
5267
     * @psalm-return static<TKey,T>
5268
     */
5269 22
    public function remove($key)
5270
    {
5271
        // recursive call
5272 22
        if (\is_array($key)) {
5273 1
            foreach ($key as $k) {
5274 1
                $this->internalRemove($k);
5275
            }
5276
5277 1
            return static::create(
5278 1
                $this->toArray(),
5279 1
                $this->iteratorClass,
5280 1
                false
5281
            );
5282
        }
5283
5284 21
        $this->internalRemove($key);
5285
5286 21
        return static::create(
5287 21
            $this->toArray(),
5288 21
            $this->iteratorClass,
5289 21
            false
5290
        );
5291
    }
5292
5293
    /**
5294
     * alias: for "Arrayy->removeValue()"
5295
     *
5296
     * @param mixed $element
5297
     *
5298
     * @return static
5299
     *                <p>(Immutable)</p>
5300
     *
5301
     * @psalm-param  T $element
5302
     * @psalm-return static<TKey,T>
5303
     * @psalm-mutation-free
5304
     */
5305 8
    public function removeElement($element)
5306
    {
5307 8
        return $this->removeValue($element);
5308
    }
5309
5310
    /**
5311
     * Remove the first value from the current array.
5312
     *
5313
     * EXAMPLE: <code>
5314
     * a([1 => 'bar', 'foo' => 'foo'])->removeFirst(); // Arrayy['foo' => 'foo']
5315
     * </code>
5316
     *
5317
     * @return static
5318
     *                <p>(Immutable)</p>
5319
     *
5320
     * @psalm-return static<TKey,T>
5321
     * @psalm-mutation-free
5322
     */
5323 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...
5324
    {
5325 7
        $tmpArray = $this->toArray();
5326
5327 7
        \array_shift($tmpArray);
5328
5329 7
        return static::create(
5330 7
            $tmpArray,
5331 7
            $this->iteratorClass,
5332 7
            false
5333
        );
5334
    }
5335
5336
    /**
5337
     * Remove the last value from the current array.
5338
     *
5339
     * EXAMPLE: <code>
5340
     * a([1 => 'bar', 'foo' => 'foo'])->removeLast(); // Arrayy[1 => 'bar']
5341
     * </code>
5342
     *
5343
     * @return static
5344
     *                <p>(Immutable)</p>
5345
     *
5346
     * @psalm-return static<TKey,T>
5347
     * @psalm-mutation-free
5348
     */
5349 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...
5350
    {
5351 7
        $tmpArray = $this->toArray();
5352
5353 7
        \array_pop($tmpArray);
5354
5355 7
        return static::create(
5356 7
            $tmpArray,
5357 7
            $this->iteratorClass,
5358 7
            false
5359
        );
5360
    }
5361
5362
    /**
5363
     * Removes a particular value from an array (numeric or associative).
5364
     *
5365
     * EXAMPLE: <code>
5366
     * a([1 => 'bar', 'foo' => 'foo'])->removeValue('foo'); // Arrayy[1 => 'bar']
5367
     * </code>
5368
     *
5369
     * @param mixed $value
5370
     *
5371
     * @return static
5372
     *                <p>(Immutable)</p>
5373
     *
5374
     * @psalm-param  T $value
5375
     * @psalm-return static<TKey,T>
5376
     * @psalm-mutation-free
5377
     */
5378 8
    public function removeValue($value): self
5379
    {
5380 8
        $this->generatorToArray();
5381
5382
        // init
5383 8
        $isSequentialArray = $this->isSequential();
5384
5385 8
        foreach ($this->array as $key => $item) {
5386 7
            if ($item === $value) {
5387 7
                unset($this->array[$key]);
5388
            }
5389
        }
5390
5391 8
        if ($isSequentialArray) {
5392 6
            $this->array = \array_values($this->array);
5393
        }
5394
5395 8
        return static::create(
5396 8
            $this->array,
5397 8
            $this->iteratorClass,
5398 8
            false
5399
        );
5400
    }
5401
5402
    /**
5403
     * Generate array of repeated arrays.
5404
     *
5405
     * @param int $times <p>How many times has to be repeated.</p>
5406
     *
5407
     * @return static
5408
     *                <p>(Immutable)</p>
5409
     *
5410
     * @psalm-return static<TKey,T>
5411
     * @psalm-mutation-free
5412
     */
5413 1
    public function repeat($times): self
5414
    {
5415 1
        if ($times === 0) {
5416 1
            return static::create([], $this->iteratorClass);
5417
        }
5418
5419 1
        return static::create(
5420 1
            \array_fill(0, (int) $times, $this->toArray()),
5421 1
            $this->iteratorClass,
5422 1
            false
5423
        );
5424
    }
5425
5426
    /**
5427
     * Replace a key with a new key/value pair.
5428
     *
5429
     * EXAMPLE: <code>
5430
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
5431
     * $arrayy->replace(2, 'notfoo', 'notbar'); // Arrayy[1 => 'foo', 'notfoo' => 'notbar', 3 => 'bar']
5432
     * </code>
5433
     *
5434
     * @param mixed $oldKey
5435
     * @param mixed $newKey
5436
     * @param mixed $newValue
5437
     *
5438
     * @return static
5439
     *                <p>(Immutable)</p>
5440
     *
5441
     * @psalm-return static<TKey,T>
5442
     * @psalm-mutation-free
5443
     */
5444 5
    public function replace($oldKey, $newKey, $newValue): self
5445
    {
5446 5
        $that = clone $this;
5447
5448
        /**
5449
         * @psalm-suppress ImpureMethodCall - object is already cloned
5450
         */
5451 5
        return $that->remove($oldKey)
5452 5
            ->set($newKey, $newValue);
5453
    }
5454
5455
    /**
5456
     * Create an array using the current array as values and the other array as keys.
5457
     *
5458
     * EXAMPLE: <code>
5459
     * $firstArray = [
5460
     *     1 => 'one',
5461
     *     2 => 'two',
5462
     *     3 => 'three',
5463
     * ];
5464
     * $secondArray = [
5465
     *     'one' => 1,
5466
     *     1     => 'one',
5467
     *     2     => 2,
5468
     * ];
5469
     * $arrayy = a($firstArray);
5470
     * $arrayy->replaceAllKeys($secondArray); // Arrayy[1 => "one", 'one' => "two", 2 => "three"]
5471
     * </code>
5472
     *
5473
     * @param array $keys <p>An array of keys.</p>
5474
     *
5475
     * @return static
5476
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
5477
     *
5478
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5479
     * @psalm-return static<TKey,T>
5480
     * @psalm-mutation-free
5481
     */
5482 2
    public function replaceAllKeys(array $keys): self
5483
    {
5484 2
        return static::create(
5485 2
            \array_combine($keys, $this->toArray()),
5486 2
            $this->iteratorClass,
5487 2
            false
5488
        );
5489
    }
5490
5491
    /**
5492
     * Create an array using the current array as keys and the other array as values.
5493
     *
5494
     * EXAMPLE: <code>
5495
     * $firstArray = [
5496
     *     1 => 'one',
5497
     *     2 => 'two',
5498
     *     3 => 'three',
5499
     * ];
5500
     * $secondArray = [
5501
     *     'one' => 1,
5502
     *     1     => 'one',
5503
     *     2     => 2,
5504
     * ];
5505
     * $arrayy = a($firstArray);
5506
     * $arrayy->replaceAllValues($secondArray); // Arrayy['one' => 1, 'two' => 'one', 'three' => 2]
5507
     * </code>
5508
     *
5509
     * @param array $array <p>An array of values.</p>
5510
     *
5511
     * @return static
5512
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
5513
     *
5514
     * @psalm-param  array<mixed,T> $array
5515
     * @psalm-return static<TKey,T>
5516
     * @psalm-mutation-free
5517
     */
5518 2
    public function replaceAllValues(array $array): self
5519
    {
5520 2
        return static::create(
5521 2
            \array_combine($this->array, $array),
5522 2
            $this->iteratorClass,
5523 2
            false
5524
        );
5525
    }
5526
5527
    /**
5528
     * Replace the keys in an array with another set.
5529
     *
5530
     * EXAMPLE: <code>
5531
     * a([1 => 'bar', 'foo' => 'foo'])->replaceKeys([1 => 2, 'foo' => 'replaced']); // Arrayy[2 => 'bar', 'replaced' => 'foo']
5532
     * </code>
5533
     *
5534
     * @param array $keys <p>An array of keys matching the array's size</p>
5535
     *
5536
     * @return static
5537
     *                <p>(Immutable)</p>
5538
     *
5539
     * @psalm-param  array<mixed,mixed>|array<mixed,TKey> $keys
5540
     * @psalm-return static<TKey,T>
5541
     * @psalm-mutation-free
5542
     */
5543 1
    public function replaceKeys(array $keys): self
5544
    {
5545 1
        $values = \array_values($this->toArray());
5546 1
        $result = \array_combine($keys, $values);
5547
5548 1
        return static::create(
5549 1
            $result,
5550 1
            $this->iteratorClass,
5551 1
            false
5552
        );
5553
    }
5554
5555
    /**
5556
     * Replace the first matched value in an array.
5557
     *
5558
     * EXAMPLE: <code>
5559
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5560
     * a($testArray)->replaceOneValue('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'foobar']
5561
     * </code>
5562
     *
5563
     * @param mixed $search      <p>The value to replace.</p>
5564
     * @param mixed $replacement <p>The value to replace.</p>
5565
     *
5566
     * @return static
5567
     *                <p>(Immutable)</p>
5568
     *
5569
     * @psalm-return static<TKey,T>
5570
     * @psalm-mutation-free
5571
     */
5572 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...
5573
    {
5574 3
        $array = $this->toArray();
5575 3
        $key = \array_search($search, $array, true);
5576
5577 3
        if ($key !== false) {
5578 3
            $array[$key] = $replacement;
5579
        }
5580
5581 3
        return static::create(
5582 3
            $array,
5583 3
            $this->iteratorClass,
5584 3
            false
5585
        );
5586
    }
5587
5588
    /**
5589
     * Replace values in the current array.
5590
     *
5591
     * EXAMPLE: <code>
5592
     * $testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
5593
     * a($testArray)->replaceValues('foo', 'replaced'); // Arrayy['bar', 'foo' => 'replaced', 'foobar' => 'replacedbar']
5594
     * </code>
5595
     *
5596
     * @param mixed $search      <p>The value to replace.</p>
5597
     * @param mixed $replacement <p>What to replace it with.</p>
5598
     *
5599
     * @return static
5600
     *                <p>(Immutable)</p>
5601
     *
5602
     * @psalm-return static<TKey,T>
5603
     * @psalm-mutation-free
5604
     */
5605 1
    public function replaceValues($search, $replacement = ''): self
5606
    {
5607
        /**
5608
         * @psalm-suppress MissingClosureReturnType
5609
         * @psalm-suppress MissingClosureParamType
5610
         */
5611 1
        return $this->each(
5612 1
            static function ($value) use ($search, $replacement) {
5613 1
                return \str_replace($search, $replacement, $value);
5614 1
            }
5615
        );
5616
    }
5617
5618
    /**
5619
     * Get the last elements from index $from until the end of this array.
5620
     *
5621
     * EXAMPLE: <code>
5622
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->rest(2); // Arrayy[0 => 'lall']
5623
     * </code>
5624
     *
5625
     * @param int $from
5626
     *
5627
     * @return static
5628
     *                <p>(Immutable)</p>
5629
     *
5630
     * @psalm-return static<TKey,T>
5631
     * @psalm-mutation-free
5632
     */
5633 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...
5634
    {
5635 15
        $tmpArray = $this->toArray();
5636
5637 15
        return static::create(
5638 15
            \array_splice($tmpArray, $from),
5639 15
            $this->iteratorClass,
5640 15
            false
5641
        );
5642
    }
5643
5644
    /**
5645
     * Return the array in the reverse order.
5646
     *
5647
     * EXAMPLE: <code>
5648
     * a([1, 2, 3])->reverse(); // self[3, 2, 1]
5649
     * </code>
5650
     *
5651
     * @return $this
5652
     *               <p>(Mutable) Return this Arrayy object.</p>
5653
     *
5654
     * @psalm-return static<TKey,T>
5655
     */
5656 9
    public function reverse(): self
5657
    {
5658 9
        $this->generatorToArray();
5659
5660 9
        $this->array = \array_reverse($this->array);
5661
5662 9
        return $this;
5663
    }
5664
5665
    /**
5666
     * Sort an array in reverse order.
5667
     *
5668
     * @param int $sort_flags [optional] <p>
5669
     *                        You may modify the behavior of the sort using the optional
5670
     *                        parameter sort_flags, for details
5671
     *                        see sort.
5672
     *                        </p>
5673
     *
5674
     * @return $this
5675
     *               <p>(Mutable) Return this Arrayy object.</p>
5676
     *
5677
     * @psalm-return static<TKey,T>
5678
     */
5679 4
    public function rsort(int $sort_flags = 0): self
5680
    {
5681 4
        $this->generatorToArray();
5682
5683 4
        \rsort($this->array, $sort_flags);
5684
5685 4
        return $this;
5686
    }
5687
5688
    /**
5689
     * Sort an array in reverse order.
5690
     *
5691
     * @param int $sort_flags [optional] <p>
5692
     *                        You may modify the behavior of the sort using the optional
5693
     *                        parameter sort_flags, for details
5694
     *                        see sort.
5695
     *                        </p>
5696
     *
5697
     * @return $this
5698
     *               <p>(Immutable) Return this Arrayy object.</p>
5699
     *
5700
     * @psalm-return static<TKey,T>
5701
     * @psalm-mutation-free
5702
     */
5703 4
    public function rsortImmutable(int $sort_flags = 0): self
5704
    {
5705 4
        $that = clone $this;
5706
5707
        /**
5708
         * @psalm-suppress ImpureMethodCall - object is already cloned
5709
         */
5710 4
        $that->rsort($sort_flags);
5711
5712 4
        return $that;
5713
    }
5714
5715
    /**
5716
     * Search for the first index of the current array via $value.
5717
     *
5718
     * EXAMPLE: <code>
5719
     * a(['fòô' => 'bàř', 'lall' => 'bàř'])->searchIndex('bàř'); // Arrayy[0 => 'fòô']
5720
     * </code>
5721
     *
5722
     * @param mixed $value
5723
     *
5724
     * @return false|float|int|string
5725
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
5726
     * @psalm-mutation-free
5727
     */
5728 21
    public function searchIndex($value)
5729
    {
5730 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
5731 20
            if ($value === $valueFromArray) {
5732 20
                return $keyFromArray;
5733
            }
5734
        }
5735
5736 11
        return false;
5737
    }
5738
5739
    /**
5740
     * Search for the value of the current array via $index.
5741
     *
5742
     * EXAMPLE: <code>
5743
     * a(['fòô' => 'bàř'])->searchValue('fòô'); // Arrayy[0 => 'bàř']
5744
     * </code>
5745
     *
5746
     * @param mixed $index
5747
     *
5748
     * @return static
5749
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
5750
     *
5751
     * @psalm-return static<TKey,T>
5752
     * @psalm-mutation-free
5753
     */
5754 9
    public function searchValue($index): self
5755
    {
5756 9
        $this->generatorToArray();
5757
5758
        // init
5759 9
        $return = [];
5760
5761 9
        if ($this->array === []) {
5762
            return static::create(
5763
                [],
5764
                $this->iteratorClass,
5765
                false
5766
            );
5767
        }
5768
5769
        // php cast "bool"-index into "int"-index
5770 9
        if ((bool) $index === $index) {
5771 1
            $index = (int) $index;
5772
        }
5773
5774 9
        if ($this->offsetExists($index)) {
5775 7
            $return = [$this->array[$index]];
5776
        }
5777
5778 9
        return static::create(
5779 9
            $return,
5780 9
            $this->iteratorClass,
5781 9
            false
5782
        );
5783
    }
5784
5785
    /**
5786
     * Set a value for the current array (optional using dot-notation).
5787
     *
5788
     * EXAMPLE: <code>
5789
     * $arrayy = a(['Lars' => ['lastname' => 'Moelleken']]);
5790
     * $arrayy->set('Lars.lastname', 'Müller'); // Arrayy['Lars', ['lastname' => 'Müller']]]
5791
     * </code>
5792
     *
5793
     * @param string $key   <p>The key to set.</p>
5794
     * @param mixed  $value <p>Its value.</p>
5795
     *
5796
     * @return $this
5797
     *               <p>(Mutable) Return this Arrayy object.</p>
5798
     *
5799
     * @psalm-param  TKey $key
5800
     * @psalm-param  T $value
5801
     * @psalm-return static<TKey,T>
5802
     */
5803 28
    public function set($key, $value): self
5804
    {
5805 28
        $this->internalSet($key, $value);
5806
5807 27
        return $this;
5808
    }
5809
5810
    /**
5811
     * Get a value from a array and set it if it was not.
5812
     *
5813
     * WARNING: this method only set the value, if the $key is not already set
5814
     *
5815
     * EXAMPLE: <code>
5816
     * $arrayy = a([1 => 1, 2 => 2, 3 => 3]);
5817
     * $arrayy->setAndGet(1, 4); // 1
5818
     * $arrayy->setAndGet(0, 4); // 4
5819
     * </code>
5820
     *
5821
     * @param mixed $key      <p>The key</p>
5822
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
5823
     *
5824
     * @return mixed
5825
     *               <p>(Mutable)</p>
5826
     */
5827 11
    public function setAndGet($key, $fallback = null)
5828
    {
5829 11
        $this->generatorToArray();
5830
5831
        // If the key doesn't exist, set it.
5832 11
        if (!$this->has($key)) {
5833 4
            $this->array = $this->set($key, $fallback)->toArray();
5834
        }
5835
5836 11
        return $this->get($key);
5837
    }
5838
5839
    /**
5840
     * Shifts a specified value off the beginning of array.
5841
     *
5842
     * @return mixed
5843
     *               <p>(Mutable) A shifted element from the current array.</p>
5844
     */
5845 5
    public function shift()
5846
    {
5847 5
        $this->generatorToArray();
5848
5849 5
        return \array_shift($this->array);
5850
    }
5851
5852
    /**
5853
     * Shuffle the current array.
5854
     *
5855
     * EXAMPLE: <code>
5856
     * a([1 => 'bar', 'foo' => 'foo'])->shuffle(); // e.g.: Arrayy[['foo' => 'foo', 1 => 'bar']]
5857
     * </code>
5858
     *
5859
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
5860
     * @param array $array  [optional]
5861
     *
5862
     * @return static
5863
     *                <p>(Immutable)</p>
5864
     *
5865
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
5866
     * @psalm-return static<TKey,T>
5867
     *
5868
     * @noinspection BadExceptionsProcessingInspection
5869
     * @noinspection NonSecureShuffleUsageInspection
5870
     */
5871 2
    public function shuffle(bool $secure = false, array $array = null): self
5872
    {
5873 2
        if ($array === null) {
5874 2
            $array = $this->toArray(false);
5875
        }
5876
5877 2
        if ($secure !== true) {
5878 2
            \shuffle($array);
5879
        } else {
5880 1
            $size = \count($array, \COUNT_NORMAL);
5881 1
            $keys = \array_keys($array);
5882 1
            for ($i = $size - 1; $i > 0; --$i) {
5883
                try {
5884 1
                    $r = \random_int(0, $i);
5885
                } catch (\Exception $e) {
5886
                    $r = \mt_rand(0, $i);
5887
                }
5888 1
                if ($r !== $i) {
5889
                    $temp = $array[$keys[$r]];
5890
                    $array[$keys[$r]] = $array[$keys[$i]];
5891
                    $array[$keys[$i]] = $temp;
5892
                }
5893
            }
5894
        }
5895
5896 2
        foreach ($array as $key => $value) {
5897
            // check if recursive is needed
5898 2
            if (\is_array($value)) {
5899 2
                $array[$key] = $this->shuffle($secure, $value);
5900
            }
5901
        }
5902
5903 2
        return static::create(
5904 2
            $array,
5905 2
            $this->iteratorClass,
5906 2
            false
5907
        );
5908
    }
5909
5910
    /**
5911
     * Count the values from the current array.
5912
     *
5913
     * alias: for "Arrayy->count()"
5914
     *
5915
     * @param int $mode
5916
     *
5917
     * @return int
5918
     */
5919 20
    public function size(int $mode = \COUNT_NORMAL): int
5920
    {
5921 20
        return $this->count($mode);
5922
    }
5923
5924
    /**
5925
     * Checks whether array has exactly $size items.
5926
     *
5927
     * @param int $size
5928
     *
5929
     * @return bool
5930
     */
5931 1
    public function sizeIs(int $size): bool
5932
    {
5933
        // init
5934 1
        $itemsTempCount = 0;
5935
5936
        /** @noinspection PhpUnusedLocalVariableInspection */
5937
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
5938 1
        foreach ($this->getGeneratorByReference() as &$value) {
5939 1
            ++$itemsTempCount;
5940 1
            if ($itemsTempCount > $size) {
5941 1
                return false;
5942
            }
5943
        }
5944
5945 1
        return $itemsTempCount === $size;
5946
    }
5947
5948
    /**
5949
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
5950
     * smaller than $fromSize.
5951
     *
5952
     * @param int $fromSize
5953
     * @param int $toSize
5954
     *
5955
     * @return bool
5956
     */
5957 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
5958
    {
5959 1
        if ($fromSize > $toSize) {
5960 1
            $tmp = $toSize;
5961 1
            $toSize = $fromSize;
5962 1
            $fromSize = $tmp;
5963
        }
5964
5965
        // init
5966 1
        $itemsTempCount = 0;
5967
5968 1
        foreach ($this->getGenerator() as $key => $value) {
5969 1
            ++$itemsTempCount;
5970 1
            if ($itemsTempCount > $toSize) {
5971 1
                return false;
5972
            }
5973
        }
5974
5975 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
5976
    }
5977
5978
    /**
5979
     * Checks whether array has more than $size items.
5980
     *
5981
     * @param int $size
5982
     *
5983
     * @return bool
5984
     */
5985 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...
5986
    {
5987
        // init
5988 1
        $itemsTempCount = 0;
5989
5990 1
        foreach ($this->getGenerator() as $key => $value) {
5991 1
            ++$itemsTempCount;
5992 1
            if ($itemsTempCount > $size) {
5993 1
                return true;
5994
            }
5995
        }
5996
5997 1
        return $itemsTempCount > $size;
5998
    }
5999
6000
    /**
6001
     * Checks whether array has less than $size items.
6002
     *
6003
     * @param int $size
6004
     *
6005
     * @return bool
6006
     */
6007 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...
6008
    {
6009
        // init
6010 1
        $itemsTempCount = 0;
6011
6012 1
        foreach ($this->getGenerator() as $key => $value) {
6013 1
            ++$itemsTempCount;
6014 1
            if ($itemsTempCount > $size) {
6015 1
                return false;
6016
            }
6017
        }
6018
6019 1
        return $itemsTempCount < $size;
6020
    }
6021
6022
    /**
6023
     * Counts all elements in an array, or something in an object.
6024
     *
6025
     * <p>
6026
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
6027
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
6028
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
6029
     * implemented and used in PHP.
6030
     * </p>
6031
     *
6032
     * @return int
6033
     *             <p>
6034
     *             The number of elements in var, which is
6035
     *             typically an array, since anything else will have one
6036
     *             element.
6037
     *             </p>
6038
     *             <p>
6039
     *             If var is not an array or an object with
6040
     *             implemented Countable interface,
6041
     *             1 will be returned.
6042
     *             There is one exception, if var is &null;,
6043
     *             0 will be returned.
6044
     *             </p>
6045
     *             <p>
6046
     *             Caution: count may return 0 for a variable that isn't set,
6047
     *             but it may also return 0 for a variable that has been initialized with an
6048
     *             empty array. Use isset to test if a variable is set.
6049
     *             </p>
6050
     */
6051 10
    public function sizeRecursive(): int
6052
    {
6053 10
        return \count($this->toArray(), \COUNT_RECURSIVE);
6054
    }
6055
6056
    /**
6057
     * Extract a slice of the array.
6058
     *
6059
     * @param int      $offset       <p>Slice begin index.</p>
6060
     * @param int|null $length       <p>Length of the slice.</p>
6061
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
6062
     *
6063
     * @return static
6064
     *                <p>(Immutable) A slice of the original array with length $length.</p>
6065
     *
6066
     * @psalm-return static<TKey,T>
6067
     * @psalm-mutation-free
6068
     */
6069 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
6070
    {
6071 5
        return static::create(
6072 5
            \array_slice(
6073 5
                $this->toArray(),
6074 5
                $offset,
6075 5
                $length,
6076 5
                $preserveKeys
6077
            ),
6078 5
            $this->iteratorClass,
6079 5
            false
6080
        );
6081
    }
6082
6083
    /**
6084
     * Sort the current array and optional you can keep the keys.
6085
     *
6086
     * EXAMPLE: <code>
6087
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sort(SORT_ASC, SORT_NATURAL, false); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6088
     * </code>
6089
     *
6090
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6091
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6092
     *                              <strong>SORT_NATURAL</strong></p>
6093
     * @param bool       $keepKeys
6094
     *
6095
     * @return static
6096
     *                <p>(Mutable) Return this Arrayy object.</p>
6097
     *
6098
     * @psalm-return static<TKey,T>
6099
     */
6100 20
    public function sort(
6101
        $direction = \SORT_ASC,
6102
        int $strategy = \SORT_REGULAR,
6103
        bool $keepKeys = false
6104
    ): self {
6105 20
        $this->generatorToArray();
6106
6107 20
        return $this->sorting(
6108 20
            $this->array,
6109 20
            $direction,
6110 20
            $strategy,
6111 20
            $keepKeys
6112
        );
6113
    }
6114
6115
    /**
6116
     * Sort the current array and optional you can keep the keys.
6117
     *
6118
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6119
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
6120
     *                              <strong>SORT_NATURAL</strong></p>
6121
     * @param bool       $keepKeys
6122
     *
6123
     * @return static
6124
     *                <p>(Immutable) Return this Arrayy object.</p>
6125
     *
6126
     * @psalm-return static<TKey,T>
6127
     */
6128 12
    public function sortImmutable(
6129
        $direction = \SORT_ASC,
6130
        int $strategy = \SORT_REGULAR,
6131
        bool $keepKeys = false
6132
    ): self {
6133 12
        $that = clone $this;
6134
6135 12
        $that->generatorToArray();
6136
6137 12
        return $that->sorting(
6138 12
            $that->array,
6139 12
            $direction,
6140 12
            $strategy,
6141 12
            $keepKeys
6142
        );
6143
    }
6144
6145
    /**
6146
     * Sort the current array by key.
6147
     *
6148
     * EXAMPLE: <code>
6149
     * a([1 => 2, 0 => 1])->sortKeys(\SORT_ASC); // Arrayy[0 => 1, 1 => 2]
6150
     * </code>
6151
     *
6152
     * @see http://php.net/manual/en/function.ksort.php
6153
     * @see http://php.net/manual/en/function.krsort.php
6154
     *
6155
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6156
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6157
     *                              <strong>SORT_NATURAL</strong></p>
6158
     *
6159
     * @return $this
6160
     *               <p>(Mutable) Return this Arrayy object.</p>
6161
     *
6162
     * @psalm-return static<TKey,T>
6163
     */
6164 18
    public function sortKeys(
6165
        $direction = \SORT_ASC,
6166
        int $strategy = \SORT_REGULAR
6167
    ): self {
6168 18
        $this->generatorToArray();
6169
6170 18
        $this->sorterKeys($this->array, $direction, $strategy);
6171
6172 18
        return $this;
6173
    }
6174
6175
    /**
6176
     * Sort the current array by key.
6177
     *
6178
     * @see          http://php.net/manual/en/function.ksort.php
6179
     * @see          http://php.net/manual/en/function.krsort.php
6180
     *
6181
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6182
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6183
     *                              <strong>SORT_NATURAL</strong></p>
6184
     *
6185
     * @return $this
6186
     *               <p>(Immutable) Return this Arrayy object.</p>
6187
     *
6188
     * @psalm-return static<TKey,T>
6189
     * @psalm-mutation-free
6190
     */
6191 8
    public function sortKeysImmutable(
6192
        $direction = \SORT_ASC,
6193
        int $strategy = \SORT_REGULAR
6194
    ): self {
6195 8
        $that = clone $this;
6196
6197
        /**
6198
         * @psalm-suppress ImpureMethodCall - object is already cloned
6199
         */
6200 8
        $that->sortKeys($direction, $strategy);
6201
6202 8
        return $that;
6203
    }
6204
6205
    /**
6206
     * Sort the current array by value.
6207
     *
6208
     * EXAMPLE: <code>
6209
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueKeepIndex(SORT_ASC, SORT_REGULAR); // Arrayy[0 => 'a', 3 => 'd', 2 => 'f']
6210
     * </code>
6211
     *
6212
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6213
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6214
     *                              <strong>SORT_NATURAL</strong></p>
6215
     *
6216
     * @return static
6217
     *                <p>(Mutable)</p>
6218
     *
6219
     * @psalm-return static<TKey,T>
6220
     */
6221 1
    public function sortValueKeepIndex(
6222
        $direction = \SORT_ASC,
6223
        int $strategy = \SORT_REGULAR
6224
    ): self {
6225 1
        return $this->sort($direction, $strategy, true);
6226
    }
6227
6228
    /**
6229
     * Sort the current array by value.
6230
     *
6231
     * EXAMPLE: <code>
6232
     * a(3 => 'd', 2 => 'f', 0 => 'a')->sortValueNewIndex(SORT_ASC, SORT_NATURAL); // Arrayy[0 => 'a', 1 => 'd', 2 => 'f']
6233
     * </code>
6234
     *
6235
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
6236
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6237
     *                              <strong>SORT_NATURAL</strong></p>
6238
     *
6239
     * @return static
6240
     *                <p>(Mutable)</p>
6241
     *
6242
     * @psalm-return static<TKey,T>
6243
     */
6244 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6245
    {
6246 1
        return $this->sort($direction, $strategy, false);
6247
    }
6248
6249
    /**
6250
     * Sort a array by value or by a closure.
6251
     *
6252
     * - If the sorter is null, the array is sorted naturally.
6253
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
6254
     *
6255
     * EXAMPLE: <code>
6256
     * $testArray = range(1, 5);
6257
     * $under = a($testArray)->sorter(
6258
     *     function ($value) {
6259
     *         return $value % 2 === 0;
6260
     *     }
6261
     * );
6262
     * var_dump($under); // Arrayy[1, 3, 5, 2, 4]
6263
     * </code>
6264
     *
6265
     * @param callable|string|null $sorter
6266
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
6267
     *                                        <strong>SORT_DESC</strong></p>
6268
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
6269
     *                                        <strong>SORT_NATURAL</strong></p>
6270
     *
6271
     * @return static
6272
     *                <p>(Immutable)</p>
6273
     *
6274
     * @psalm-return static<TKey,T>
6275
     * @psalm-mutation-free
6276
     */
6277 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
6278
    {
6279 1
        $array = $this->toArray();
6280 1
        $direction = $this->getDirection($direction);
6281
6282
        // Transform all values into their results.
6283 1
        if ($sorter) {
6284 1
            $arrayy = static::create(
6285 1
                $array,
6286 1
                $this->iteratorClass,
6287 1
                false
6288
            );
6289
6290
            /**
6291
             * @psalm-suppress MissingClosureReturnType
6292
             * @psalm-suppress MissingClosureParamType
6293
             */
6294 1
            $results = $arrayy->each(
6295 1
                static function ($value) use ($sorter) {
6296 1
                    if (\is_callable($sorter) === true) {
6297 1
                        return $sorter($value);
6298
                    }
6299
6300 1
                    return $sorter === $value;
6301 1
                }
6302
            );
6303
6304 1
            $results = $results->toArray();
6305
        } else {
6306 1
            $results = $array;
6307
        }
6308
6309
        // Sort by the results and replace by original values
6310 1
        \array_multisort($results, $direction, $strategy, $array);
6311
6312 1
        return static::create(
6313 1
            $array,
6314 1
            $this->iteratorClass,
6315 1
            false
6316
        );
6317
    }
6318
6319
    /**
6320
     * @param int      $offset
6321
     * @param int|null $length
6322
     * @param array    $replacement
6323
     *
6324
     * @return static
6325
     *                <p>(Immutable)</p>
6326
     *
6327
     * @psalm-param  array<mixed,mixed>|array<mixed,T> $replacement
6328
     * @psalm-return static<TKey,T>
6329
     * @psalm-mutation-free
6330
     */
6331 1
    public function splice(int $offset, int $length = null, $replacement = []): self
6332
    {
6333 1
        $tmpArray = $this->toArray();
6334
6335 1
        \array_splice(
6336 1
            $tmpArray,
6337 1
            $offset,
6338 1
            $length ?? $this->count(),
6339 1
            $replacement
6340
        );
6341
6342 1
        return static::create(
6343 1
            $tmpArray,
6344 1
            $this->iteratorClass,
6345 1
            false
6346
        );
6347
    }
6348
6349
    /**
6350
     * Split an array in the given amount of pieces.
6351
     *
6352
     * EXAMPLE: <code>
6353
     * a(['a' => 1, 'b' => 2])->split(2, true); // Arrayy[['a' => 1], ['b' => 2]]
6354
     * </code>
6355
     *
6356
     * @param int  $numberOfPieces
6357
     * @param bool $keepKeys
6358
     *
6359
     * @return static
6360
     *                <p>(Immutable)</p>
6361
     *
6362
     * @psalm-return static<TKey,T>
6363
     * @psalm-mutation-free
6364
     */
6365 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
6366
    {
6367 1
        if ($keepKeys) {
6368 1
            $generator = function () use ($numberOfPieces) {
6369 1
                $carry = [];
6370 1
                $i = 1;
6371 1
                foreach ($this->getGenerator() as $key => $value) {
6372 1
                    $carry[$key] = $value;
6373
6374 1
                    if ($i % $numberOfPieces !== 0) {
6375 1
                        ++$i;
6376
6377 1
                        continue;
6378
                    }
6379
6380 1
                    yield $carry;
6381
6382 1
                    $carry = [];
6383 1
                    $i = 1;
6384
                }
6385
6386 1
                if ($carry !== []) {
6387 1
                    yield $carry;
6388
                }
6389 1
            };
6390 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...
6391 1
            $generator = function () use ($numberOfPieces) {
6392 1
                $carry = [];
6393 1
                $i = 1;
6394 1
                foreach ($this->getGenerator() as $key => $value) {
6395 1
                    $carry[] = $value;
6396
6397 1
                    if ($i % $numberOfPieces !== 0) {
6398 1
                        ++$i;
6399
6400 1
                        continue;
6401
                    }
6402
6403 1
                    yield $carry;
6404
6405 1
                    $carry = [];
6406 1
                    $i = 1;
6407
                }
6408
6409 1
                if ($carry !== []) {
6410 1
                    yield $carry;
6411
                }
6412 1
            };
6413
        }
6414
6415 1
        return static::create(
6416 1
            $generator,
6417 1
            $this->iteratorClass,
6418 1
            false
6419
        );
6420
    }
6421
6422
    /**
6423
     * Strip all empty items from the current array.
6424
     *
6425
     * EXAMPLE: <code>
6426
     * a(['a' => 1, 'b' => ''])->stripEmpty(); // Arrayy[['a' => 1]]
6427
     * </code>
6428
     *
6429
     * @return static
6430
     *                <p>(Immutable)</p>
6431
     *
6432
     * @psalm-return static<TKey,T>
6433
     * @psalm-mutation-free
6434
     */
6435 1
    public function stripEmpty(): self
6436
    {
6437 1
        return $this->filter(
6438 1
            static function ($item) {
6439 1
                if ($item === null) {
6440 1
                    return false;
6441
                }
6442
6443 1
                return (bool) \trim((string) $item);
6444 1
            }
6445
        );
6446
    }
6447
6448
    /**
6449
     * Swap two values between positions by key.
6450
     *
6451
     * EXAMPLE: <code>
6452
     * a(['a' => 1, 'b' => ''])->swap('a', 'b'); // Arrayy[['a' => '', 'b' => 1]]
6453
     * </code>
6454
     *
6455
     * @param int|string $swapA <p>a key in the array</p>
6456
     * @param int|string $swapB <p>a key in the array</p>
6457
     *
6458
     * @return static
6459
     *                <p>(Immutable)</p>
6460
     *
6461
     * @psalm-return static<TKey,T>
6462
     * @psalm-mutation-free
6463
     */
6464 1
    public function swap($swapA, $swapB): self
6465
    {
6466 1
        $array = $this->toArray();
6467
6468 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
6469
6470 1
        return static::create(
6471 1
            $array,
6472 1
            $this->iteratorClass,
6473 1
            false
6474
        );
6475
    }
6476
6477
    /**
6478
     * Get the current array from the "Arrayy"-object.
6479
     * alias for "getArray()"
6480
     *
6481
     * @param bool $convertAllArrayyElements <p>
6482
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6483
     *                                       </p>
6484
     * @param bool $preserveKeys             <p>
6485
     *                                       e.g.: A generator maybe return the same key more then once,
6486
     *                                       so maybe you will ignore the keys.
6487
     *                                       </p>
6488
     *
6489
     * @return array
6490
     *
6491
     * @psalm-return array<mixed,mixed>|array<TKey,T>
6492
     * @psalm-mutation-free
6493
     */
6494 931
    public function toArray(
6495
        bool $convertAllArrayyElements = false,
6496
        bool $preserveKeys = true
6497
    ): array {
6498
        // init
6499 931
        $array = [];
6500
6501 931
        if ($convertAllArrayyElements) {
6502 2
            foreach ($this->getGenerator() as $key => $value) {
6503 2
                if ($value instanceof self) {
6504 1
                    $value = $value->toArray(
6505 1
                        $convertAllArrayyElements,
6506 1
                        $preserveKeys
6507
                    );
6508
                }
6509
6510 2
                if ($preserveKeys) {
6511 1
                    $array[$key] = $value;
6512
                } else {
6513 2
                    $array[] = $value;
6514
                }
6515
            }
6516
        } else {
6517 931
            $array = \iterator_to_array($this->getGenerator(), $preserveKeys);
6518
        }
6519
6520 931
        return $array;
6521
    }
6522
6523
    /**
6524
     * Get the current array from the "Arrayy"-object as list.
6525
     *
6526
     * @param bool $convertAllArrayyElements <p>
6527
     *                                       Convert all Child-"Arrayy" objects also to arrays.
6528
     *                                       </p>
6529
     *
6530
     * @return array
6531
     *
6532
     * @psalm-return list<array<TKey,T>>
6533
     * @psalm-mutation-free
6534
     */
6535 1
    public function toList(bool $convertAllArrayyElements = false): array
6536
    {
6537 1
        return $this->toArray(
6538 1
            $convertAllArrayyElements,
6539 1
            false
6540
        );
6541
    }
6542
6543
    /**
6544
     * Convert the current array to JSON.
6545
     *
6546
     * EXAMPLE: <code>
6547
     * a(['bar', ['foo']])->toJson(); // '["bar",{"1":"foo"}]'
6548
     * </code>
6549
     *
6550
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
6551
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
6552
     *
6553
     * @return string
6554
     */
6555 12
    public function toJson(int $options = 0, int $depth = 512): string
6556
    {
6557 12
        $return = \json_encode($this->toArray(), $options, $depth);
6558 12
        if ($return === false) {
6559
            return '';
6560
        }
6561
6562 12
        return $return;
6563
    }
6564
6565
    /**
6566
     * @param string[]|null $items  [optional]
6567
     * @param string[]      $helper [optional]
6568
     *
6569
     * @return static|static[]
6570
     *
6571
     * @psalm-return static<int, static<TKey,T>>
6572
     */
6573 1
    public function toPermutation(array $items = null, array $helper = []): self
6574
    {
6575
        // init
6576 1
        $return = [];
6577
6578 1
        if ($items === null) {
6579 1
            $items = $this->toArray();
6580
        }
6581
6582 1
        if (empty($items)) {
6583 1
            $return[] = $helper;
6584
        } else {
6585 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
6586 1
                $new_items = $items;
6587 1
                $new_helper = $helper;
6588 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
6589
                /** @noinspection PhpSillyAssignmentInspection */
6590
                /** @var string[] $new_items */
6591 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...
6592 1
                \array_unshift($new_helper, $tmp_helper);
6593 1
                $return = \array_merge(
6594 1
                    $return,
6595 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
6596
                );
6597
            }
6598
        }
6599
6600 1
        return static::create(
6601 1
            $return,
6602 1
            $this->iteratorClass,
6603 1
            false
6604
        );
6605
    }
6606
6607
    /**
6608
     * Implodes array to a string with specified separator.
6609
     *
6610
     * @param string $separator [optional] <p>The element's separator.</p>
6611
     *
6612
     * @return string
6613
     *                <p>The string representation of array, separated by ",".</p>
6614
     */
6615 19
    public function toString(string $separator = ','): string
6616
    {
6617 19
        return $this->implode($separator);
6618
    }
6619
6620
    /**
6621
     * Return a duplicate free copy of the current array.
6622
     *
6623
     * EXAMPLE: <code>
6624
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[1, 2]
6625
     * </code>
6626
     *
6627
     * @return $this
6628
     *               <p>(Mutable)</p>
6629
     *
6630
     * @psalm-return static<TKey,T>
6631
     */
6632 13
    public function uniqueNewIndex(): self
6633
    {
6634
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6635
6636
        /**
6637
         * @psalm-suppress MissingClosureReturnType
6638
         * @psalm-suppress MissingClosureParamType
6639
         */
6640 13
        $this->array = $this->reduce(
6641 13
            static function ($resultArray, $value) {
6642 12
                if (!\in_array($value, $resultArray, true)) {
6643 12
                    $resultArray[] = $value;
6644
                }
6645
6646 12
                return $resultArray;
6647 13
            },
6648 13
            []
6649 13
        )->toArray();
6650 13
        $this->generator = null;
6651
6652 13
        return $this;
6653
    }
6654
6655
    /**
6656
     * Return a duplicate free copy of the current array. (with the old keys)
6657
     *
6658
     * EXAMPLE: <code>
6659
     * a([2 => 1, 3 => 2, 4 => 2])->uniqueNewIndex(); // Arrayy[2 => 1, 3 => 2]
6660
     * </code>
6661
     *
6662
     * @return $this
6663
     *               <p>(Mutable)</p>
6664
     *
6665
     * @psalm-return static<TKey,T>
6666
     */
6667 11
    public function uniqueKeepIndex(): self
6668
    {
6669
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
6670
6671
        // init
6672 11
        $array = $this->toArray();
6673
6674
        /**
6675
         * @psalm-suppress MissingClosureReturnType
6676
         * @psalm-suppress MissingClosureParamType
6677
         */
6678 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...
6679 11
            \array_keys($array),
6680 11
            static function ($resultArray, $key) use ($array) {
6681 10
                if (!\in_array($array[$key], $resultArray, true)) {
6682 10
                    $resultArray[$key] = $array[$key];
6683
                }
6684
6685 10
                return $resultArray;
6686 11
            },
6687 11
            []
6688
        );
6689 11
        $this->generator = null;
6690
6691 11
        return $this;
6692
    }
6693
6694
    /**
6695
     * alias: for "Arrayy->uniqueNewIndex()"
6696
     *
6697
     * @return static
6698
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
6699
     *
6700
     * @see          Arrayy::unique()
6701
     *
6702
     * @psalm-return static<TKey,T>
6703
     */
6704 13
    public function unique(): self
6705
    {
6706 13
        return $this->uniqueNewIndex();
6707
    }
6708
6709
    /**
6710
     * Prepends one or more values to the beginning of array at once.
6711
     *
6712
     * @param array ...$args
6713
     *
6714
     * @return $this
6715
     *               <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
6716
     *
6717
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
6718
     * @psalm-return static<TKey,T>
6719
     */
6720 4
    public function unshift(...$args): self
6721
    {
6722 4
        $this->generatorToArray();
6723
6724 4
        \array_unshift($this->array, ...$args);
6725
6726 4
        return $this;
6727
    }
6728
6729
    /**
6730
     * Tests whether the given closure return something valid for all elements of this array.
6731
     *
6732
     * @param \Closure $closure the predicate
6733
     *
6734
     * @return bool
6735
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
6736
     */
6737 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...
6738
    {
6739 1
        foreach ($this->getGenerator() as $key => $value) {
6740 1
            if (!$closure($value, $key)) {
6741 1
                return false;
6742
            }
6743
        }
6744
6745 1
        return true;
6746
    }
6747
6748
    /**
6749
     * Get all values from a array.
6750
     *
6751
     * EXAMPLE: <code>
6752
     * $arrayy = a([1 => 'foo', 2 => 'foo2', 3 => 'bar']);
6753
     * $arrayyTmp->values(); // Arrayy[0 => 'foo', 1 => 'foo2', 2 => 'bar']
6754
     * </code>
6755
     *
6756
     * @return static
6757
     *                <p>(Immutable)</p>
6758
     *
6759
     * @psalm-return static<TKey,T>
6760
     * @psalm-mutation-free
6761
     */
6762 2
    public function values(): self
6763
    {
6764 2
        return static::create(
6765 2
            function () {
6766
                /** @noinspection YieldFromCanBeUsedInspection */
6767 2
                foreach ($this->getGenerator() as $value) {
6768 2
                    yield $value;
6769
                }
6770 2
            },
6771 2
            $this->iteratorClass,
6772 2
            false
6773
        );
6774
    }
6775
6776
    /**
6777
     * Apply the given function to every element in the array, discarding the results.
6778
     *
6779
     * EXAMPLE: <code>
6780
     * $callable = function (&$value, $key) {
6781
     *     $value = $key;
6782
     * };
6783
     * $arrayy = a([1, 2, 3]);
6784
     * $arrayy->walk($callable); // Arrayy[0, 1, 2]
6785
     * </code>
6786
     *
6787
     * @param callable $callable
6788
     * @param bool     $recursive [optional] <p>Whether array will be walked recursively or no</p>
6789
     * @param mixed    $userData  [optional] <p>
6790
     *                            If the optional $userData parameter is supplied,
6791
     *                            it will be passed as the third parameter to the $callable.
6792
     *                            </p>
6793
     *
6794
     * @return $this
6795
     *               <p>(Mutable) Return this Arrayy object, with modified elements.</p>
6796
     *
6797
     * @psalm-return static<TKey,T>
6798
     */
6799 12
    public function walk(
6800
        $callable,
6801
        bool $recursive = false,
6802
        $userData = self::ARRAYY_HELPER_WALK
6803
    ): self {
6804 12
        $this->generatorToArray();
6805
6806 12
        if ($this->array !== []) {
6807 10
            if ($recursive === true) {
6808 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6809
                    \array_walk_recursive($this->array, $callable, $userData);
6810
                } else {
6811 5
                    \array_walk_recursive($this->array, $callable);
6812
                }
6813
            } else {
6814
                /** @noinspection NestedPositiveIfStatementsInspection */
6815 5
                if ($userData !== self::ARRAYY_HELPER_WALK) {
6816
                    \array_walk($this->array, $callable, $userData);
6817
                } else {
6818 5
                    \array_walk($this->array, $callable);
6819
                }
6820
            }
6821
        }
6822
6823 12
        return $this;
6824
    }
6825
6826
    /**
6827
     * Returns a collection of matching items.
6828
     *
6829
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
6830
     * @param mixed  $value                 the value to match
6831
     *
6832
     * @throws \InvalidArgumentException if property or method is not defined
6833
     *
6834
     * @return static
6835
     *
6836
     * @psalm-param  T $value
6837
     * @psalm-return static<TKey,T>
6838
     */
6839 1
    public function where(string $keyOrPropertyOrMethod, $value): self
6840
    {
6841 1
        return $this->filter(
6842 1
            function ($item) use ($keyOrPropertyOrMethod, $value) {
6843
                $accessorValue = $this->extractValue(
6844
                    $item,
6845
                    $keyOrPropertyOrMethod
6846
                );
6847
6848
                return $accessorValue === $value;
6849 1
            }
6850
        );
6851
    }
6852
6853
    /**
6854
     * Convert an array into a object.
6855
     *
6856
     * @param array $array
6857
     *
6858
     * @return \stdClass
6859
     *
6860
     * @psalm-param array<mixed,mixed> $array
6861
     */
6862 4
    final protected static function arrayToObject(array $array = []): \stdClass
6863
    {
6864
        // init
6865 4
        $object = new \stdClass();
6866
6867 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
6868 1
            return $object;
6869
        }
6870
6871 3
        foreach ($array as $name => $value) {
6872 3
            if (\is_array($value)) {
6873 1
                $object->{$name} = static::arrayToObject($value);
6874
            } else {
6875 3
                $object->{$name} = $value;
6876
            }
6877
        }
6878
6879 3
        return $object;
6880
    }
6881
6882
    /**
6883
     * @param array|\Generator|null $input         <p>
6884
     *                                             An array containing keys to return.
6885
     *                                             </p>
6886
     * @param mixed|null            $search_values [optional] <p>
6887
     *                                             If specified, then only keys containing these values are returned.
6888
     *                                             </p>
6889
     * @param bool                  $strict        [optional] <p>
6890
     *                                             Determines if strict comparison (===) should be used during the
6891
     *                                             search.
6892
     *                                             </p>
6893
     *
6894
     * @return array
6895
     *               <p>an array of all the keys in input</p>
6896
     *
6897
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
6898
     * @psalm-return array<TKey|mixed>
6899
     * @psalm-mutation-free
6900
     */
6901 11
    protected function array_keys_recursive(
6902
        $input = null,
6903
        $search_values = null,
6904
        bool $strict = true
6905
    ): array {
6906
        // init
6907 11
        $keys = [];
6908 11
        $keysTmp = [];
6909
6910 11
        if ($input === null) {
6911 4
            $input = $this->getGenerator();
6912
        }
6913
6914 11
        if ($search_values === null) {
6915 11
            foreach ($input as $key => $value) {
6916 11
                $keys[] = $key;
6917
6918
                // check if recursive is needed
6919 11
                if (\is_array($value)) {
6920 11
                    $keysTmp[] = $this->array_keys_recursive($value);
6921
                }
6922
            }
6923
        } else {
6924 1
            $is_array_tmp = \is_array($search_values);
6925
6926 1
            foreach ($input as $key => $value) {
6927 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...
6928
                    (
6929 1
                        $is_array_tmp === false
6930
                        &&
6931 1
                        $strict === true
6932
                        &&
6933 1
                        $search_values === $value
6934
                    )
6935
                    ||
6936
                    (
6937 1
                        $is_array_tmp === false
6938
                        &&
6939 1
                        $strict === false
6940
                        &&
6941 1
                        $search_values == $value
6942
                    )
6943
                    ||
6944
                    (
6945 1
                        $is_array_tmp === true
6946
                        &&
6947 1
                        \in_array($value, $search_values, $strict)
6948
                    )
6949
                ) {
6950 1
                    $keys[] = $key;
6951
                }
6952
6953
                // check if recursive is needed
6954 1
                if (\is_array($value)) {
6955 1
                    $keysTmp[] = $this->array_keys_recursive($value);
6956
                }
6957
            }
6958
        }
6959
6960 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
6961
    }
6962
6963
    /**
6964
     * @param mixed      $path
6965
     * @param callable   $callable
6966
     * @param array|null $currentOffset
6967
     *
6968
     * @return void
6969
     *
6970
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
6971
     * @psalm-mutation-free
6972
     */
6973 10
    protected function callAtPath($path, $callable, &$currentOffset = null)
6974
    {
6975 10
        $this->generatorToArray();
6976
6977 10
        if ($currentOffset === null) {
6978 10
            $currentOffset = &$this->array;
6979
        }
6980
6981 10
        $explodedPath = \explode($this->pathSeparator, $path);
6982 10
        if ($explodedPath === false) {
6983
            return;
6984
        }
6985
6986 10
        $nextPath = \array_shift($explodedPath);
6987
6988 10
        if (!isset($currentOffset[$nextPath])) {
6989 1
            return;
6990
        }
6991
6992 9
        if (!empty($explodedPath)) {
6993 1
            $this->callAtPath(
6994 1
                \implode($this->pathSeparator, $explodedPath),
6995 1
                $callable,
6996 1
                $currentOffset[$nextPath]
6997
            );
6998
        } else {
6999 9
            $callable($currentOffset[$nextPath]);
7000
        }
7001 9
    }
7002
7003
    /**
7004
     * Extracts the value of the given property or method from the object.
7005
     *
7006
     * @param static $object                <p>The object to extract the value from.</p>
7007
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
7008
     *                                      value should be extracted.</p>
7009
     *
7010
     * @throws \InvalidArgumentException if the method or property is not defined
7011
     *
7012
     * @return mixed
7013
     *               <p>The value extracted from the specified property or method.</p>
7014
     *
7015
     * @psalm-param self<TKey,T> $object
7016
     */
7017 1
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
7018
    {
7019 1
        if (isset($object[$keyOrPropertyOrMethod])) {
7020 1
            $return = $object->get($keyOrPropertyOrMethod);
7021
7022 1
            if ($return instanceof self) {
7023
                return $return->toArray();
7024
            }
7025
7026 1
            return $return;
7027
        }
7028
7029
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
7030
            return $object->{$keyOrPropertyOrMethod};
7031
        }
7032
7033
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
7034
            return $object->{$keyOrPropertyOrMethod}();
7035
        }
7036
7037
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
7038
    }
7039
7040
    /**
7041
     * create a fallback for array
7042
     *
7043
     * 1. use the current array, if it's a array
7044
     * 2. fallback to empty array, if there is nothing
7045
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
7046
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
7047
     * 5. call "__toArray()" on object, if the method exists
7048
     * 6. cast a string or object with "__toString()" into an array
7049
     * 7. throw a "InvalidArgumentException"-Exception
7050
     *
7051
     * @param mixed $data
7052
     *
7053
     * @throws \InvalidArgumentException
7054
     *
7055
     * @return array
7056
     *
7057
     * @psalm-return array<mixed,mixed>|array<TKey,T>
7058
     */
7059 1206
    protected function fallbackForArray(&$data): array
7060
    {
7061 1206
        $data = $this->internalGetArray($data);
7062
7063 1206
        if ($data === null) {
7064 2
            throw new \InvalidArgumentException('Passed value should be a array');
7065
        }
7066
7067 1204
        return $data;
7068
    }
7069
7070
    /**
7071
     * @param bool $preserveKeys <p>
7072
     *                           e.g.: A generator maybe return the same key more then once,
7073
     *                           so maybe you will ignore the keys.
7074
     *                           </p>
7075
     *
7076
     * @return bool
7077
     *
7078
     * @noinspection ReturnTypeCanBeDeclaredInspection
7079
     * @psalm-mutation-free :/
7080
     */
7081 1118
    protected function generatorToArray(bool $preserveKeys = true)
7082
    {
7083 1118
        if ($this->generator) {
7084 2
            $this->array = $this->toArray(false, $preserveKeys);
7085 2
            $this->generator = null;
7086
7087 2
            return true;
7088
        }
7089
7090 1118
        return false;
7091
    }
7092
7093
    /**
7094
     * Get correct PHP constant for direction.
7095
     *
7096
     * @param int|string $direction
7097
     *
7098
     * @return int
7099
     * @psalm-mutation-free
7100
     */
7101 43
    protected function getDirection($direction): int
7102
    {
7103 43
        if ((string) $direction === $direction) {
7104 10
            $direction = \strtolower($direction);
7105
7106 10
            if ($direction === 'desc') {
7107 2
                $direction = \SORT_DESC;
7108
            } else {
7109 9
                $direction = \SORT_ASC;
7110
            }
7111
        }
7112
7113
        if (
7114 43
            $direction !== \SORT_DESC
7115
            &&
7116 43
            $direction !== \SORT_ASC
7117
        ) {
7118
            $direction = \SORT_ASC;
7119
        }
7120
7121 43
        return $direction;
7122
    }
7123
7124
    /**
7125
     * @return TypeCheckInterface[]
7126
     *
7127
     * @noinspection ReturnTypeCanBeDeclaredInspection
7128
     */
7129 24
    protected function getPropertiesFromPhpDoc()
7130
    {
7131 24
        static $PROPERTY_CACHE = [];
7132 24
        $cacheKey = 'Class::' . static::class;
7133
7134 24
        if (isset($PROPERTY_CACHE[$cacheKey])) {
7135 22
            return $PROPERTY_CACHE[$cacheKey];
7136
        }
7137
7138
        // init
7139 4
        $properties = [];
7140
7141 4
        $reflector = new \ReflectionClass($this);
7142 4
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
7143 4
        $docComment = $reflector->getDocComment();
7144 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...
7145 3
            $docblock = $factory->create($docComment);
7146
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7147 3
            foreach ($docblock->getTagsByName('property') as $tag) {
7148 3
                $typeName = $tag->getVariableName();
7149
                /** @var string|null $typeName */
7150 3
                if ($typeName !== null) {
7151 3
                    $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7152 3
                    if ($typeCheckPhpDoc !== null) {
7153 3
                        $properties[$typeName] = $typeCheckPhpDoc;
7154
                    }
7155
                }
7156
            }
7157
        }
7158
7159
        /** @noinspection PhpAssignmentInConditionInspection */
7160 4
        while ($reflector = $reflector->getParentClass()) {
7161 4
            $docComment = $reflector->getDocComment();
7162 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...
7163 4
                $docblock = $factory->create($docComment);
7164
                /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
7165 4
                foreach ($docblock->getTagsByName('property') as $tag) {
7166 1
                    $typeName = $tag->getVariableName();
7167
                    /** @var string|null $typeName */
7168 1
                    if ($typeName !== null) {
7169 1
                        if (isset($properties[$typeName])) {
7170 1
                            continue;
7171
                        }
7172
7173 1
                        $typeCheckPhpDoc = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag, $typeName);
7174 1
                        if ($typeCheckPhpDoc !== null) {
7175 1
                            $properties[$typeName] = $typeCheckPhpDoc;
7176
                        }
7177
                    }
7178
                }
7179
            }
7180
        }
7181
7182 4
        return $PROPERTY_CACHE[$cacheKey] = $properties;
7183
    }
7184
7185
    /**
7186
     * @param mixed $glue
7187
     * @param mixed $pieces
7188
     * @param bool  $useKeys
7189
     *
7190
     * @return string
7191
     * @psalm-mutation-free
7192
     */
7193 36
    protected function implode_recursive(
7194
        $glue = '',
7195
        $pieces = [],
7196
        bool $useKeys = false
7197
    ): string {
7198 36
        if ($pieces instanceof self) {
7199 1
            $pieces = $pieces->toArray();
7200
        }
7201
7202 36
        if (\is_array($pieces)) {
7203 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
7204 36
            $pieces_count_not_zero = $pieces_count > 0;
7205
7206 36
            return \implode(
7207 36
                $glue,
7208 36
                \array_map(
7209 36
                    [$this, 'implode_recursive'],
7210 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
7211 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
7212
                )
7213
            );
7214
        }
7215
7216
        if (
7217 36
            \is_scalar($pieces) === true
7218
            ||
7219 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
7220
        ) {
7221 32
            return (string) $pieces;
7222
        }
7223
7224 8
        return '';
7225
    }
7226
7227
    /**
7228
     * @param mixed                 $needle   <p>
7229
     *                                        The searched value.
7230
     *                                        </p>
7231
     *                                        <p>
7232
     *                                        If needle is a string, the comparison is done
7233
     *                                        in a case-sensitive manner.
7234
     *                                        </p>
7235
     * @param array|\Generator|null $haystack <p>
7236
     *                                        The array.
7237
     *                                        </p>
7238
     * @param bool                  $strict   [optional] <p>
7239
     *                                        If the third parameter strict is set to true
7240
     *                                        then the in_array function will also check the
7241
     *                                        types of the
7242
     *                                        needle in the haystack.
7243
     *                                        </p>
7244
     *
7245
     * @return bool
7246
     *              <p>true if needle is found in the array, false otherwise</p>
7247
     *
7248
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
7249
     * @psalm-mutation-free
7250
     */
7251 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
7252
    {
7253 18
        if ($haystack === null) {
7254
            $haystack = $this->getGenerator();
7255
        }
7256
7257 18
        foreach ($haystack as $item) {
7258 14
            if (\is_array($item)) {
7259 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
7260
            } else {
7261
                /** @noinspection NestedPositiveIfStatementsInspection */
7262 14
                if ($strict === true) {
7263 14
                    $returnTmp = $item === $needle;
7264
                } else {
7265
                    $returnTmp = $item == $needle;
7266
                }
7267
            }
7268
7269 14
            if ($returnTmp === true) {
7270 14
                return true;
7271
            }
7272
        }
7273
7274 8
        return false;
7275
    }
7276
7277
    /**
7278
     * @param mixed $data
7279
     *
7280
     * @return array|null
7281
     *
7282
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
7283
     */
7284 1206
    protected function internalGetArray(&$data)
7285
    {
7286 1206
        if (\is_array($data)) {
7287 1200
            return $data;
7288
        }
7289
7290 80
        if (!$data) {
7291 7
            return [];
7292
        }
7293
7294 79
        if (\is_object($data) === true) {
7295 72
            if ($data instanceof \ArrayObject) {
7296 5
                return $data->getArrayCopy();
7297
            }
7298
7299 68
            if ($data instanceof \Generator) {
7300
                return static::createFromGeneratorImmutable($data)->toArray();
7301
            }
7302
7303 68
            if ($data instanceof \Traversable) {
7304
                return static::createFromObject($data)->toArray();
7305
            }
7306
7307 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...
7308
                return (array) $data->jsonSerialize();
7309
            }
7310
7311 68
            if (\method_exists($data, '__toArray')) {
7312
                return (array) $data->__toArray();
7313
            }
7314
7315 68
            if (\method_exists($data, '__toString')) {
7316
                return [(string) $data];
7317
            }
7318
        }
7319
7320 75
        if (\is_callable($data)) {
7321
            /**
7322
             * @psalm-suppress InvalidPropertyAssignmentValue - why?
7323
             */
7324 66
            $this->generator = new ArrayyRewindableGenerator($data);
7325
7326 66
            return [];
7327
        }
7328
7329 11
        if (\is_scalar($data)) {
7330 9
            return [$data];
7331
        }
7332
7333 2
        return null;
7334
    }
7335
7336
    /**
7337
     * Internal mechanics of remove method.
7338
     *
7339
     * @param mixed $key
7340
     *
7341
     * @return bool
7342
     */
7343 22
    protected function internalRemove($key): bool
7344
    {
7345 22
        $this->generatorToArray();
7346
7347
        if (
7348 22
            $this->pathSeparator
7349
            &&
7350 22
            (string) $key === $key
7351
            &&
7352 22
            \strpos($key, $this->pathSeparator) !== false
7353
        ) {
7354
            $path = \explode($this->pathSeparator, (string) $key);
7355
7356
            if ($path !== false) {
7357
                // crawl though the keys
7358
                while (\count($path, \COUNT_NORMAL) > 1) {
7359
                    $key = \array_shift($path);
7360
7361
                    if (!$this->has($key)) {
7362
                        return false;
7363
                    }
7364
7365
                    $this->array = &$this->array[$key];
7366
                }
7367
7368
                $key = \array_shift($path);
7369
            }
7370
        }
7371
7372 22
        unset($this->array[$key]);
7373
7374 22
        return true;
7375
    }
7376
7377
    /**
7378
     * Internal mechanic of set method.
7379
     *
7380
     * @param int|string|null $key
7381
     * @param mixed           $value
7382
     * @param bool            $checkProperties
7383
     *
7384
     * @return bool
7385
     */
7386 1056
    protected function internalSet(
7387
        $key,
7388
        &$value,
7389
        bool $checkProperties = true
7390
    ): bool {
7391
        if (
7392 1056
            $checkProperties === true
7393
            &&
7394 1056
            $this->properties !== []
7395
        ) {
7396 112
            $this->checkType($key, $value);
7397
        }
7398
7399 1054
        if ($key === null) {
7400
            return false;
7401
        }
7402
7403 1054
        $this->generatorToArray();
7404
7405
        /** @psalm-var array<int|string,mixed> $array */
7406 1054
        $array = &$this->array;
7407
7408
        /**
7409
         * https://github.com/vimeo/psalm/issues/2536
7410
         *
7411
         * @psalm-suppress PossiblyInvalidArgument
7412
         * @psalm-suppress InvalidScalarArgument
7413
         */
7414
        if (
7415 1054
            $this->pathSeparator
7416
            &&
7417 1054
            (string) $key === $key
7418
            &&
7419 1054
            \strpos($key, $this->pathSeparator) !== false
7420
        ) {
7421 9
            $path = \explode($this->pathSeparator, (string) $key);
7422
7423 9
            if ($path !== false) {
7424
                // crawl through the keys
7425 9
                while (\count($path, \COUNT_NORMAL) > 1) {
7426 9
                    $key = \array_shift($path);
7427
7428 9
                    $array = &$array[$key];
7429
                }
7430
7431 9
                $key = \array_shift($path);
7432
            }
7433
        }
7434
7435 1054
        if ($array === null) {
7436 4
            $array = [];
7437 1051
        } elseif (!\is_array($array)) {
7438 1
            throw new \RuntimeException('Can not set value at this path "' . $key . '" because (' . \gettype($array) . ')"' . \print_r($array, true) . '" is not an array.');
7439
        }
7440
7441 1054
        $array[$key] = $value;
7442
7443 1054
        return true;
7444
    }
7445
7446
    /**
7447
     * Convert a object into an array.
7448
     *
7449
     * @param mixed|object $object
7450
     *
7451
     * @return array|mixed
7452
     *
7453
     * @psalm-mutation-free
7454
     */
7455 5
    protected static function objectToArray($object)
7456
    {
7457 5
        if (!\is_object($object)) {
7458 4
            return $object;
7459
        }
7460
7461 5
        $object = \get_object_vars($object);
7462
7463
        /**
7464
         * @psalm-suppress PossiblyInvalidArgument - the parameter is always some kind of array - false-positive from psalm?
7465
         */
7466 5
        return \array_map(['static', 'objectToArray'], $object);
7467
    }
7468
7469
    /**
7470
     * @param array $data
7471
     * @param bool  $checkPropertiesInConstructor
7472
     *
7473
     * @return void
7474
     *
7475
     * @psalm-param array<mixed,T> $data
7476
     */
7477 1204
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
7478
    {
7479 1204
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
7480
                                        &&
7481 1204
                                        $checkPropertiesInConstructor === true;
7482
7483 1204
        if ($this->properties !== []) {
7484 99
            foreach ($data as $key => &$valueInner) {
7485 99
                $this->internalSet(
7486 99
                    $key,
7487 99
                    $valueInner,
7488 99
                    $checkPropertiesInConstructor
7489
                );
7490
            }
7491
        } else {
7492
            if (
7493 1124
                $this->checkPropertyTypes === true
7494
                ||
7495 1124
                $checkPropertiesInConstructor === true
7496
            ) {
7497 23
                $this->properties = $this->getPropertiesFromPhpDoc();
7498
            }
7499
7500
            /** @var TypeCheckInterface[] $properties */
7501 1124
            $properties = $this->properties;
7502
7503
            if (
7504 1124
                $this->checkPropertiesMismatchInConstructor === true
7505
                &&
7506 1124
                \count($data) !== 0
7507
                &&
7508 1124
                \count(\array_diff_key($properties, $data)) > 0
7509
            ) {
7510 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...
7511
            }
7512
7513 1123
            foreach ($data as $key => &$valueInner) {
7514 952
                $this->internalSet(
7515 952
                    $key,
7516 952
                    $valueInner,
7517 952
                    $checkPropertiesInConstructor
7518
                );
7519
            }
7520
        }
7521 1196
    }
7522
7523
    /**
7524
     * sorting keys
7525
     *
7526
     * @param array      $elements
7527
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7528
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7529
     *                              <strong>SORT_NATURAL</strong></p>
7530
     *
7531
     * @return $this
7532
     *               <p>(Mutable) Return this Arrayy object.</p>
7533
     *
7534
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7535
     * @psalm-return static<TKey,T>
7536
     */
7537 18
    protected function sorterKeys(
7538
        array &$elements,
7539
        $direction = \SORT_ASC,
7540
        int $strategy = \SORT_REGULAR
7541
    ): self {
7542 18
        $direction = $this->getDirection($direction);
7543
7544
        switch ($direction) {
7545 18
            case 'desc':
7546 18
            case \SORT_DESC:
7547 6
                \krsort($elements, $strategy);
7548
7549 6
                break;
7550 13
            case 'asc':
7551 13
            case \SORT_ASC:
7552
            default:
7553 13
                \ksort($elements, $strategy);
7554
        }
7555
7556 18
        return $this;
7557
    }
7558
7559
    /**
7560
     * @param array      $elements  <p>Warning: used as reference</p>
7561
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
7562
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
7563
     *                              <strong>SORT_NATURAL</strong></p>
7564
     * @param bool       $keepKeys
7565
     *
7566
     * @return $this
7567
     *               <p>(Mutable) Return this Arrayy object.</p>
7568
     *
7569
     * @psalm-param  array<mixed,mixed>|array<mixed|TKey,T> $elements
7570
     * @psalm-return static<TKey,T>
7571
     */
7572 24
    protected function sorting(
7573
        array &$elements,
7574
        $direction = \SORT_ASC,
7575
        int $strategy = \SORT_REGULAR,
7576
        bool $keepKeys = false
7577
    ): self {
7578 24
        $direction = $this->getDirection($direction);
7579
7580 24
        if (!$strategy) {
7581 24
            $strategy = \SORT_REGULAR;
7582
        }
7583
7584
        switch ($direction) {
7585 24
            case 'desc':
7586 24
            case \SORT_DESC:
7587 13
                if ($keepKeys) {
7588 9
                    \arsort($elements, $strategy);
7589
                } else {
7590 4
                    \rsort($elements, $strategy);
7591
                }
7592
7593 13
                break;
7594 11
            case 'asc':
7595 11
            case \SORT_ASC:
7596
            default:
7597 11
                if ($keepKeys) {
7598 4
                    \asort($elements, $strategy);
7599
                } else {
7600 7
                    \sort($elements, $strategy);
7601
                }
7602
        }
7603
7604 24
        return $this;
7605
    }
7606
7607
    /**
7608
     * @param array $array
7609
     *
7610
     * @return array
7611
     *
7612
     * @psalm-mutation-free
7613
     */
7614 25
    private function getArrayRecursiveHelperArrayy(array $array)
7615
    {
7616 25
        if ($array === []) {
7617
            return [];
7618
        }
7619
7620 25
        \array_walk_recursive(
7621 25
            $array,
7622
            /**
7623
             * @param array|self $item
7624
             *
7625
             * @return void
7626
             */
7627 25
            static function (&$item) {
7628 25
                if ($item instanceof self) {
7629 1
                    $item = $item->getArray();
7630
                }
7631 25
            }
7632
        );
7633
7634 25
        return $array;
7635
    }
7636
7637
    /**
7638
     * @param int|string|null $key
7639
     * @param mixed           $value
7640
     *
7641
     * @return void
7642
     */
7643 112
    private function checkType($key, $value)
7644
    {
7645
        if (
7646 112
            $key !== null
7647
            &&
7648 112
            isset($this->properties[$key]) === false
7649
            &&
7650 112
            $this->checkPropertiesMismatch === true
7651
        ) {
7652
            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...
7653
        }
7654
7655 112
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
7656 97
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
7657 24
        } elseif ($key !== null && isset($this->properties[$key])) {
7658 24
            $this->properties[$key]->checkType($value);
7659
        }
7660 110
    }
7661
}
7662