Completed
Push — master ( 75f269...02e63a )
by Lars
02:19
created

Arrayy::isAssoc()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15

Duplication

Lines 14
Ratio 93.33 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 14
loc 15
ccs 7
cts 7
cp 1
crap 4
rs 9.7666
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 1212
    public function __construct(
105
        $data = [],
106
        string $iteratorClass = ArrayyIterator::class,
107
        bool $checkPropertiesInConstructor = true
108
    ) {
109 1212
        $data = $this->fallbackForArray($data);
110
111
        // used only for serialize + unserialize, all other methods are overwritten
112
        /**
113
         * @psalm-suppress InvalidArgument - why?
114
         */
115 1210
        parent::__construct([], 0, $iteratorClass);
116
117 1210
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
118
119 1202
        $this->setIteratorClass($iteratorClass);
120 1202
    }
121
122
    /**
123
     * @return void
124
     */
125 52
    public function __clone()
126
    {
127 52
        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 52
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 52
    }
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 133
    public function &__get($key)
209
    {
210 133
        $return = $this->get($key, null, null, true);
211
212 133
        if (\is_array($return) === true) {
213
            $return = static::create($return, $this->iteratorClass, false);
214
        }
215
216 133
        return $return;
217
    }
218
219
    /**
220
     * Add new values (optional using dot-notation).
221
     *
222
     * @param mixed           $value
223
     * @param int|string|null $key
224
     *
225
     * @return static
226
     *                <p>(Immutable) Return this Arrayy object, with the appended values.</p>
227
     *
228
     * @psalm-param  T $value
229
     * @psalm-return static<TKey,T>
230
     *
231
     * @psalm-mutation-free
232
     */
233 13
    public function add($value, $key = null)
234
    {
235 13
        if ($key !== null) {
236 5
            $get = $this->get($key);
237 5
            if ($get !== null) {
238 1
                $value = \array_merge_recursive(
239 1
                    !$get instanceof self ? [$get] : $get->getArray(),
240 1
                    !\is_array($value) ? [$value] : $value
241
                );
242
            }
243
244 5
            $this->internalSet($key, $value);
245
246 4
            return $this;
247
        }
248
249 8
        return $this->append($value);
250
    }
251
252
    /**
253
     * Append a (key) + value to the current array.
254
     *
255
     * EXAMPLE: <code>
256
     * a(['fòô' => 'bàř'])->append('foo'); // Arrayy['fòô' => 'bàř', 0 => 'foo']
257
     * </code>
258
     *
259
     * @param mixed $value
260
     * @param mixed $key
261
     *
262
     * @return $this
263
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
264
     *
265
     * @psalm-return static<TKey,T>
266
     */
267 20
    public function append($value, $key = null): self
268
    {
269 20
        $this->generatorToArray();
270
271 20
        if ($this->properties !== []) {
272 6
            $this->checkType($key, $value);
273
        }
274
275 19
        if ($key !== null) {
276
            if (
277 2
                isset($this->array[$key])
278
                &&
279 2
                \is_array($this->array[$key])
280
            ) {
281
                $this->array[$key][] = $value;
282
            } else {
283 2
                $this->array[$key] = $value;
284
            }
285
        } else {
286 17
            $this->array[] = $value;
287
        }
288
289 19
        return $this;
290
    }
291
292
    /**
293
     * Append a (key) + value to the current array.
294
     *
295
     * EXAMPLE: <code>
296
     * a(['fòô' => 'bàř'])->appendImmutable('foo')->getArray(); // ['fòô' => 'bàř', 0 => 'foo']
297
     * </code>
298
     *
299
     * @param mixed $value
300
     * @param mixed $key
301
     *
302
     * @return $this
303
     *               <p>(Immutable) Return this Arrayy object, with the appended values.</p>
304
     *
305
     * @psalm-return static<TKey,T>
306
     * @psalm-mutation-free
307
     */
308 1 View Code Duplication
    public function appendImmutable($value, $key = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
309
    {
310
        $generator = function () use ($key, $value): \Generator {
311 1
            if ($this->properties !== []) {
312
                $this->checkType($key, $value);
313
            }
314
315
            /** @noinspection YieldFromCanBeUsedInspection - FP */
316 1
            foreach ($this->getGenerator() as $keyOld => $itemOld) {
317 1
                yield $keyOld => $itemOld;
318
            }
319
320 1
            if ($key !== null) {
321
                yield $key => $value;
322
            } else {
323 1
                yield $value;
324
            }
325 1
        };
326
327 1
        return static::create(
328 1
            $generator,
329 1
            $this->iteratorClass,
330 1
            false
331
        );
332
    }
333
334
    /**
335
     * Sort the entries by value.
336
     *
337
     * @param int $sort_flags [optional] <p>
338
     *                        You may modify the behavior of the sort using the optional
339
     *                        parameter sort_flags, for details
340
     *                        see sort.
341
     *                        </p>
342
     *
343
     * @return $this
344
     *               <p>(Mutable) Return this Arrayy object.</p>
345
     *
346
     * @psalm-return static<TKey,T>
347
     */
348 4
    public function asort(int $sort_flags = 0): self
349
    {
350 4
        $this->generatorToArray();
351
352 4
        \asort($this->array, $sort_flags);
353
354 4
        return $this;
355
    }
356
357
    /**
358
     * Sort the entries by value.
359
     *
360
     * @param int $sort_flags [optional] <p>
361
     *                        You may modify the behavior of the sort using the optional
362
     *                        parameter sort_flags, for details
363
     *                        see sort.
364
     *                        </p>
365
     *
366
     * @return $this
367
     *               <p>(Immutable) Return this Arrayy object.</p>
368
     *
369
     * @psalm-return static<TKey,T>
370
     * @psalm-mutation-free
371
     */
372 4
    public function asortImmutable(int $sort_flags = 0): self
373
    {
374 4
        $that = clone $this;
375
376
        /**
377
         * @psalm-suppress ImpureMethodCall - object is already cloned
378
         */
379 4
        $that->asort($sort_flags);
380
381 4
        return $that;
382
    }
383
384
    /**
385
     * Counts all elements in an array, or something in an object.
386
     *
387
     * EXAMPLE: <code>
388
     * a([-9, -8, -7, 1.32])->count(); // 4
389
     * </code>
390
     *
391
     * <p>
392
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
393
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
394
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
395
     * implemented and used in PHP.
396
     * </p>
397
     *
398
     * @see http://php.net/manual/en/function.count.php
399
     *
400
     * @param int $mode [optional] If the optional mode parameter is set to
401
     *                  COUNT_RECURSIVE (or 1), count
402
     *                  will recursively count the array. This is particularly useful for
403
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
404
     *
405
     * @return int
406
     *             <p>
407
     *             The number of elements in var, which is
408
     *             typically an array, since anything else will have one
409
     *             element.
410
     *             </p>
411
     *             <p>
412
     *             If var is not an array or an object with
413
     *             implemented Countable interface,
414
     *             1 will be returned.
415
     *             There is one exception, if var is &null;,
416
     *             0 will be returned.
417
     *             </p>
418
     *             <p>
419
     *             Caution: count may return 0 for a variable that isn't set,
420
     *             but it may also return 0 for a variable that has been initialized with an
421
     *             empty array. Use isset to test if a variable is set.
422
     *             </p>
423
     * @psalm-mutation-free
424
     */
425 147
    public function count(int $mode = \COUNT_NORMAL): int
426
    {
427
        if (
428 147
            $this->generator
429
            &&
430 147
            $mode === \COUNT_NORMAL
431
        ) {
432 4
            return \iterator_count($this->generator);
433
        }
434
435 143
        return \count($this->toArray(), $mode);
436
    }
437
438
    /**
439
     * Exchange the array for another one.
440
     *
441
     * @param array|mixed|static $data
442
     *
443
     * 1. use the current array, if it's a array
444
     * 2. fallback to empty array, if there is nothing
445
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
446
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
447
     * 5. call "__toArray()" on object, if the method exists
448
     * 6. cast a string or object with "__toString()" into an array
449
     * 7. throw a "InvalidArgumentException"-Exception
450
     *
451
     * @return array
452
     *
453
     * @psalm-param  T,array<TKey,T>|self<TKey,T> $data
454
     * @psalm-return array<mixed,mixed>|array<TKey,T>
455
     */
456 1
    public function exchangeArray($data): array
457
    {
458 1
        $this->array = $this->fallbackForArray($data);
459 1
        $this->generator = null;
460
461 1
        return $this->array;
462
    }
463
464
    /**
465
     * Creates a copy of the ArrayyObject.
466
     *
467
     * @return array
468
     *
469
     * @psalm-return array<mixed,mixed>|array<TKey,T>
470
     */
471 6
    public function getArrayCopy(): array
472
    {
473 6
        $this->generatorToArray();
474
475 6
        return $this->array;
476
    }
477
478
    /**
479
     * Returns a new iterator, thus implementing the \Iterator interface.
480
     *
481
     * EXAMPLE: <code>
482
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
483
     * </code>
484
     *
485
     * @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...
486
     *                          <p>An iterator for the values in the array.</p>
487
     * @psalm-return \Iterator<array-key|TKey, mixed|T>
488
     */
489 28
    public function getIterator(): \Iterator
490
    {
491 28
        if ($this->generator instanceof ArrayyRewindableGenerator) {
492 1
            $generator = clone $this->generator;
493 1
            $this->generator = new ArrayyRewindableExtendedGenerator(
494
                static function () use ($generator): \Generator {
495 1
                    yield from $generator;
496 1
                },
497 1
                null,
498 1
                static::class
499
            );
500
501 1
            return $this->generator;
502
        }
503
504 27
        $iterator = $this->getIteratorClass();
505
506 27
        if ($iterator === ArrayyIterator::class) {
507 27
            return new $iterator($this->toArray(), 0, static::class);
508
        }
509
510
        $return = new $iterator($this->toArray());
511
        \assert($return instanceof \Iterator);
512
513
        return $return;
514
    }
515
516
    /**
517
     * Gets the iterator classname for the ArrayObject.
518
     *
519
     * @return string
520
     *
521
     * @psalm-return class-string
522
     */
523 27
    public function getIteratorClass(): string
524
    {
525 27
        return $this->iteratorClass;
526
    }
527
528
    /**
529
     * Sort the entries by key.
530
     *
531
     * @param int $sort_flags [optional] <p>
532
     *                        You may modify the behavior of the sort using the optional
533
     *                        parameter sort_flags, for details
534
     *                        see sort.
535
     *                        </p>
536
     *
537
     * @return $this
538
     *               <p>(Mutable) Return this Arrayy object.</p>
539
     *
540
     * @psalm-return static<TKey,T>
541
     */
542 4
    public function ksort(int $sort_flags = 0): self
543
    {
544 4
        $this->generatorToArray();
545
546 4
        \ksort($this->array, $sort_flags);
547
548 4
        return $this;
549
    }
550
551
    /**
552
     * Sort the entries by key.
553
     *
554
     * @param int $sort_flags [optional] <p>
555
     *                        You may modify the behavior of the sort using the optional
556
     *                        parameter sort_flags, for details
557
     *                        see sort.
558
     *                        </p>
559
     *
560
     * @return $this
561
     *               <p>(Immutable) Return this Arrayy object.</p>
562
     *
563
     * @psalm-return static<TKey,T>
564
     */
565 4
    public function ksortImmutable(int $sort_flags = 0): self
566
    {
567 4
        $that = clone $this;
568
569
        /**
570
         * @psalm-suppress ImpureMethodCall - object is already cloned
571
         */
572 4
        $that->ksort($sort_flags);
573
574 4
        return $that;
575
    }
576
577
    /**
578
     * Sort an array using a case insensitive "natural order" algorithm.
579
     *
580
     * @return $this
581
     *               <p>(Mutable) Return this Arrayy object.</p>
582
     *
583
     * @psalm-return static<TKey,T>
584
     */
585 8
    public function natcasesort(): self
586
    {
587 8
        $this->generatorToArray();
588
589 8
        \natcasesort($this->array);
590
591 8
        return $this;
592
    }
593
594
    /**
595
     * Sort an array using a case insensitive "natural order" algorithm.
596
     *
597
     * @return $this
598
     *               <p>(Immutable) Return this Arrayy object.</p>
599
     *
600
     * @psalm-return static<TKey,T>
601
     * @psalm-mutation-free
602
     */
603 4
    public function natcasesortImmutable(): self
604
    {
605 4
        $that = clone $this;
606
607
        /**
608
         * @psalm-suppress ImpureMethodCall - object is already cloned
609
         */
610 4
        $that->natcasesort();
611
612 4
        return $that;
613
    }
614
615
    /**
616
     * Sort entries using a "natural order" algorithm.
617
     *
618
     * @return $this
619
     *               <p>(Mutable) Return this Arrayy object.</p>
620
     *
621
     * @psalm-return static<TKey,T>
622
     */
623 10
    public function natsort(): self
624
    {
625 10
        $this->generatorToArray();
626
627 10
        \natsort($this->array);
628
629 10
        return $this;
630
    }
631
632
    /**
633
     * Sort entries using a "natural order" algorithm.
634
     *
635
     * @return $this
636
     *               <p>(Immutable) Return this Arrayy object.</p>
637
     *
638
     * @psalm-return static<TKey,T>
639
     * @psalm-mutation-free
640
     */
641 4
    public function natsortImmutable(): self
642
    {
643 4
        $that = clone $this;
644
645
        /**
646
         * @psalm-suppress ImpureMethodCall - object is already cloned
647
         */
648 4
        $that->natsort();
649
650 4
        return $that;
651
    }
652
653
    /**
654
     * Whether or not an offset exists.
655
     *
656
     * @param bool|int|string $offset
657
     *
658
     * @return bool
659
     *
660
     * @noinspection PhpSillyAssignmentInspection
661
     *
662
     * @psalm-mutation-free
663
     */
664 164
    public function offsetExists($offset): bool
665
    {
666
        // php cast "bool"-index into "int"-index
667 164
        if ((bool) $offset === $offset) {
668 1
            $offset = (int) $offset;
669
        }
670 164
        \assert(\is_int($offset) || \is_string($offset));
671
672 164
        $offsetExists = $this->keyExists($offset);
673 164
        if ($offsetExists === true) {
674 143
            return true;
675
        }
676
677
        /**
678
         * https://github.com/vimeo/psalm/issues/2536
679
         *
680
         * @psalm-suppress PossiblyInvalidArgument
681
         * @psalm-suppress InvalidScalarArgument
682
         */
683 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...
684 124
            $this->pathSeparator
685
            &&
686 124
            (string) $offset === $offset
687
            &&
688 124
            \strpos($offset, $this->pathSeparator) !== false
689
        ) {
690 4
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
691 4
            if ($explodedPath !== false) {
692
                /** @var string $lastOffset - helper for phpstan */
693 4
                $lastOffset = \array_pop($explodedPath);
694 4
                $containerPath = \implode($this->pathSeparator, $explodedPath);
695
696
                /**
697
                 * @psalm-suppress MissingClosureReturnType
698
                 * @psalm-suppress MissingClosureParamType
699
                 */
700 4
                $this->callAtPath(
701 4
                    $containerPath,
702
                    static function ($container) use ($lastOffset, &$offsetExists) {
703 4
                        $offsetExists = \array_key_exists($lastOffset, $container);
704 4
                    }
705
                );
706
            }
707
        }
708
709 124
        return $offsetExists;
710
    }
711
712
    /**
713
     * Returns the value at specified offset.
714
     *
715
     * @param int|string $offset
716
     *
717
     * @return mixed
718
     *               <p>Will return null if the offset did not exists.</p>
719
     */
720 133
    public function &offsetGet($offset)
721
    {
722
        // init
723 133
        $value = null;
724
725 133
        if ($this->offsetExists($offset)) {
726 131
            $value = &$this->__get($offset);
727
        }
728
729 133
        return $value;
730
    }
731
732
    /**
733
     * Assigns a value to the specified offset + check the type.
734
     *
735
     * @param int|string|null $offset
736
     * @param mixed           $value
737
     *
738
     * @return void
739
     */
740 28
    public function offsetSet($offset, $value)
741
    {
742 28
        $this->generatorToArray();
743
744 28
        if ($offset === null) {
745 7
            if ($this->properties !== []) {
746 2
                $this->checkType(null, $value);
747
            }
748
749 6
            $this->array[] = $value;
750
        } else {
751 21
            $this->internalSet(
752 21
                $offset,
753 21
                $value,
754 21
                true
755
            );
756
        }
757 27
    }
758
759
    /**
760
     * Unset an offset.
761
     *
762
     * @param int|string $offset
763
     *
764
     * @return void
765
     *              <p>(Mutable) Return nothing.</p>
766
     */
767 26
    public function offsetUnset($offset)
768
    {
769 26
        $this->generatorToArray();
770
771 26
        if ($this->array === []) {
772 6
            return;
773
        }
774
775 21
        if ($this->keyExists($offset)) {
776 14
            unset($this->array[$offset]);
777
778 14
            return;
779
        }
780
781
        /**
782
         * https://github.com/vimeo/psalm/issues/2536
783
         *
784
         * @psalm-suppress PossiblyInvalidArgument
785
         * @psalm-suppress InvalidScalarArgument
786
         */
787 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...
788 10
            $this->pathSeparator
789
            &&
790 10
            (string) $offset === $offset
791
            &&
792 10
            \strpos($offset, $this->pathSeparator) !== false
793
        ) {
794 7
            $path = \explode($this->pathSeparator, (string) $offset);
795
796 7
            if ($path !== false) {
797 7
                $pathToUnset = \array_pop($path);
798
799
                /**
800
                 * @psalm-suppress MissingClosureReturnType
801
                 * @psalm-suppress MissingClosureParamType
802
                 */
803 7
                $this->callAtPath(
804 7
                    \implode($this->pathSeparator, $path),
805
                    static function (&$offset) use ($pathToUnset) {
806 6
                        if (\is_array($offset)) {
807 5
                            unset($offset[$pathToUnset]);
808
                        } else {
809 1
                            $offset = null;
810
                        }
811 7
                    }
812
                );
813
            }
814
        }
815
816 10
        unset($this->array[$offset]);
817 10
    }
818
819
    /**
820
     * Serialize the current "Arrayy"-object.
821
     *
822
     * EXAMPLE: <code>
823
     * a([1, 4, 7])->serialize();
824
     * </code>
825
     *
826
     * @return string
827
     */
828 2
    public function serialize(): string
829
    {
830 2
        $this->generatorToArray();
831
832 2
        if (\PHP_VERSION_ID < 70400) {
833 2
            return parent::serialize();
834
        }
835
836
        return \serialize($this);
837
    }
838
839
    /**
840
     * Sets the iterator classname for the current "Arrayy"-object.
841
     *
842
     * @param string $iteratorClass
843
     *
844
     * @throws \InvalidArgumentException
845
     *
846
     * @return void
847
     *
848
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
849
     */
850 1202
    public function setIteratorClass($iteratorClass)
851
    {
852 1202
        if (\class_exists($iteratorClass)) {
853 1202
            $this->iteratorClass = $iteratorClass;
854
855 1202
            return;
856
        }
857
858
        if (\strpos($iteratorClass, '\\') === 0) {
859
            $iteratorClass = '\\' . $iteratorClass;
860
            if (\class_exists($iteratorClass)) {
861
                /**
862
                 * @psalm-suppress PropertyTypeCoercion
863
                 */
864
                $this->iteratorClass = $iteratorClass;
865
866
                return;
867
            }
868
        }
869
870
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
871
    }
872
873
    /**
874
     * Sort the entries with a user-defined comparison function and maintain key association.
875
     *
876
     * @param callable $function
877
     *
878
     * @throws \InvalidArgumentException
879
     *
880
     * @return $this
881
     *               <p>(Mutable) Return this Arrayy object.</p>
882
     *
883
     * @psalm-return static<TKey,T>
884
     */
885 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...
886
    {
887 8
        if (!\is_callable($function)) {
888
            throw new \InvalidArgumentException('Passed function must be callable');
889
        }
890
891 8
        $this->generatorToArray();
892
893 8
        \uasort($this->array, $function);
894
895 8
        return $this;
896
    }
897
898
    /**
899
     * Sort the entries with a user-defined comparison function and maintain key association.
900
     *
901
     * @param callable $function
902
     *
903
     * @throws \InvalidArgumentException
904
     *
905
     * @return $this
906
     *               <p>(Immutable) Return this Arrayy object.</p>
907
     *
908
     * @psalm-return static<TKey,T>
909
     * @psalm-mutation-free
910
     */
911 4
    public function uasortImmutable($function): self
912
    {
913 4
        $that = clone $this;
914
915
        /**
916
         * @psalm-suppress ImpureMethodCall - object is already cloned
917
         */
918 4
        $that->uasort($function);
919
920 4
        return $that;
921
    }
922
923
    /**
924
     * Sort the entries by keys using a user-defined comparison function.
925
     *
926
     * @param callable $function
927
     *
928
     * @throws \InvalidArgumentException
929
     *
930
     * @return static
931
     *                <p>(Mutable) Return this Arrayy object.</p>
932
     *
933
     * @psalm-return static<TKey,T>
934
     */
935 5
    public function uksort($function): self
936
    {
937 5
        return $this->customSortKeys($function);
938
    }
939
940
    /**
941
     * Sort the entries by keys using a user-defined comparison function.
942
     *
943
     * @param callable $function
944
     *
945
     * @throws \InvalidArgumentException
946
     *
947
     * @return static
948
     *                <p>(Immutable) Return this Arrayy object.</p>
949
     *
950
     * @psalm-return static<TKey,T>
951
     * @psalm-mutation-free
952
     */
953 1
    public function uksortImmutable($function): self
954
    {
955 1
        return $this->customSortKeysImmutable($function);
956
    }
957
958
    /**
959
     * Unserialize an string and return the instance of the "Arrayy"-class.
960
     *
961
     * EXAMPLE: <code>
962
     * $serialized = a([1, 4, 7])->serialize();
963
     * a()->unserialize($serialized);
964
     * </code>
965
     *
966
     * @param string $string
967
     *
968
     * @return $this
969
     *
970
     * @psalm-return static<TKey,T>
971
     */
972 2
    public function unserialize($string): self
973
    {
974 2
        if (\PHP_VERSION_ID < 70400) {
975 2
            parent::unserialize($string);
976
977 2
            return $this;
978
        }
979
980
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
981
    }
982
983
    /**
984
     * Append a (key) + values to the current array.
985
     *
986
     * EXAMPLE: <code>
987
     * a(['fòô' => ['bàř']])->appendArrayValues(['foo1', 'foo2'], 'fòô'); // Arrayy['fòô' => ['bàř', 'foo1', 'foo2']]
988
     * </code>
989
     *
990
     * @param array $values
991
     * @param mixed $key
992
     *
993
     * @return $this
994
     *               <p>(Mutable) Return this Arrayy object, with the appended values.</p>
995
     *
996
     * @psalm-param  array<mixed,T> $values
997
     * @psalm-param  TKey|null $key
998
     * @psalm-return static<TKey,T>
999
     */
1000 1
    public function appendArrayValues(array $values, $key = null)
1001
    {
1002 1
        $this->generatorToArray();
1003
1004 1
        if ($key !== null) {
1005
            if (
1006 1
                isset($this->array[$key])
1007
                &&
1008 1
                \is_array($this->array[$key])
1009
            ) {
1010 1
                foreach ($values as $value) {
1011 1
                    $this->array[$key][] = $value;
1012
                }
1013
            } else {
1014
                foreach ($values as $value) {
1015 1
                    $this->array[$key] = $value;
1016
                }
1017
            }
1018
        } else {
1019
            foreach ($values as $value) {
1020
                $this->array[] = $value;
1021
            }
1022
        }
1023
1024 1
        return $this;
1025
    }
1026
1027
    /**
1028
     * Add a suffix to each key.
1029
     *
1030
     * @param int|string $prefix
1031
     *
1032
     * @return static
1033
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
1034
     *
1035
     * @psalm-return static<TKey,T>
1036
     * @psalm-mutation-free
1037
     */
1038 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...
1039
    {
1040
        // init
1041 10
        $result = [];
1042
1043 10
        foreach ($this->getGenerator() as $key => $item) {
1044 9
            if ($item instanceof self) {
1045
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
1046 9
            } elseif (\is_array($item)) {
1047
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
1048
                    ->appendToEachKey($prefix)
1049
                    ->toArray();
1050
            } else {
1051 9
                $result[$prefix . $key] = $item;
1052
            }
1053
        }
1054
1055 10
        return self::create($result, $this->iteratorClass, false);
1056
    }
1057
1058
    /**
1059
     * Add a prefix to each value.
1060
     *
1061
     * @param mixed $prefix
1062
     *
1063
     * @return static
1064
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
1065
     *
1066
     * @psalm-return static<TKey,T>
1067
     * @psalm-mutation-free
1068
     */
1069 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...
1070
    {
1071
        // init
1072 10
        $result = [];
1073
1074 10
        foreach ($this->getGenerator() as $key => $item) {
1075 9
            if ($item instanceof self) {
1076
                $result[$key] = $item->appendToEachValue($prefix);
1077 9
            } elseif (\is_array($item)) {
1078
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
1079 9
            } elseif (\is_object($item) === true) {
1080 1
                $result[$key] = $item;
1081
            } else {
1082 9
                $result[$key] = $prefix . $item;
1083
            }
1084
        }
1085
1086 10
        return self::create($result, $this->iteratorClass, false);
1087
    }
1088
1089
    /**
1090
     * Sort an array in reverse order and maintain index association.
1091
     *
1092
     * @return $this
1093
     *               <p>(Mutable) Return this Arrayy object.</p>
1094
     *
1095
     * @psalm-return static<TKey,T>
1096
     */
1097 4
    public function arsort(): self
1098
    {
1099 4
        $this->generatorToArray();
1100
1101 4
        \arsort($this->array);
1102
1103 4
        return $this;
1104
    }
1105
1106
    /**
1107
     * Sort an array in reverse order and maintain index association.
1108
     *
1109
     * @return $this
1110
     *               <p>(Immutable) Return this Arrayy object.</p>
1111
     *
1112
     * @psalm-return static<TKey,T>
1113
     * @psalm-mutation-free
1114
     */
1115 10
    public function arsortImmutable(): self
1116
    {
1117 10
        $that = clone $this;
1118
1119 10
        $that->generatorToArray();
1120
1121 10
        \arsort($that->array);
1122
1123 10
        return $that;
1124
    }
1125
1126
    /**
1127
     * Iterate over the current array and execute a callback for each loop.
1128
     *
1129
     * EXAMPLE: <code>
1130
     * $result = A::create();
1131
     * $closure = function ($value, $key) use ($result) {
1132
     *     $result[$key] = ':' . $value . ':';
1133
     * };
1134
     * a(['foo', 'bar' => 'bis'])->at($closure); // Arrayy[':foo:', 'bar' => ':bis:']
1135
     * </code>
1136
     *
1137
     * @param \Closure $closure
1138
     *
1139
     * @return static
1140
     *                <p>(Immutable)</p>
1141
     *
1142
     * @psalm-return static<TKey,T>
1143
     * @psalm-mutation-free
1144
     */
1145 3
    public function at(\Closure $closure): self
1146
    {
1147 3
        $that = clone $this;
1148
1149 3
        foreach ($that->getGenerator() as $key => $value) {
1150 3
            $closure($value, $key);
1151
        }
1152
1153 3
        return static::create(
1154 3
            $that->toArray(),
1155 3
            $this->iteratorClass,
1156 3
            false
1157
        );
1158
    }
1159
1160
    /**
1161
     * Returns the average value of the current array.
1162
     *
1163
     * EXAMPLE: <code>
1164
     * a([-9, -8, -7, 1.32])->average(2); // -5.67
1165
     * </code>
1166
     *
1167
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
1168
     *
1169
     * @return float|int
1170
     *                   <p>The average value.</p>
1171
     * @psalm-mutation-free
1172
     */
1173 10
    public function average($decimals = 0)
1174
    {
1175 10
        $count = \count($this->toArray(), \COUNT_NORMAL);
1176
1177 10
        if (!$count) {
1178 2
            return 0;
1179
        }
1180
1181 8
        if ((int) $decimals !== $decimals) {
1182 3
            $decimals = 0;
1183
        }
1184
1185 8
        return \round(\array_sum($this->toArray()) / $count, $decimals);
1186
    }
1187
1188
    /**
1189
     * Changes all keys in an array.
1190
     *
1191
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
1192
     *                  or <strong>CASE_LOWER</strong> (default)</p>
1193
     *
1194
     * @return static
1195
     *                <p>(Immutable)</p>
1196
     *
1197
     * @psalm-return static<TKey,T>
1198
     * @psalm-mutation-free
1199
     */
1200 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
1201
    {
1202
        if (
1203 1
            $case !== \CASE_LOWER
1204
            &&
1205 1
            $case !== \CASE_UPPER
1206
        ) {
1207
            $case = \CASE_LOWER;
1208
        }
1209
1210 1
        $return = [];
1211 1
        foreach ($this->getGenerator() as $key => $value) {
1212 1
            \assert(\is_string($key) || \is_int($key) || \is_float($key));
1213
1214 1
            if ($case === \CASE_LOWER) {
1215 1
                $key = \mb_strtolower((string) $key);
1216
            } else {
1217 1
                $key = \mb_strtoupper((string) $key);
1218
            }
1219
1220 1
            $return[$key] = $value;
1221
        }
1222
1223 1
        return static::create(
1224 1
            $return,
1225 1
            $this->iteratorClass,
1226 1
            false
1227
        );
1228
    }
1229
1230
    /**
1231
     * Change the path separator of the array wrapper.
1232
     *
1233
     * By default, the separator is: "."
1234
     *
1235
     * @param string $separator <p>Separator to set.</p>
1236
     *
1237
     * @return $this
1238
     *               <p>(Mutable) Return this Arrayy object.</p>
1239
     *
1240
     * @psalm-return static<TKey,T>
1241
     */
1242 11
    public function changeSeparator($separator): self
1243
    {
1244 11
        $this->pathSeparator = $separator;
1245
1246 11
        return $this;
1247
    }
1248
1249
    /**
1250
     * Create a chunked version of the current array.
1251
     *
1252
     * EXAMPLE: <code>
1253
     * a([-9, -8, -7, 1.32])->chunk(2); // Arrayy[[-9, -8], [-7, 1.32]]
1254
     * </code>
1255
     *
1256
     * @param int  $size         <p>Size of each chunk.</p>
1257
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
1258
     *
1259
     * @return static
1260
     *                <p>(Immutable) A new array of chunks from the original array.</p>
1261
     *
1262
     * @psalm-return static<TKey,T>
1263
     * @psalm-mutation-free
1264
     */
1265 5
    public function chunk($size, $preserveKeys = false): self
1266
    {
1267 5
        if ($preserveKeys) {
1268
            $generator = function () use ($size) {
1269
                $values = [];
1270
                $tmpCounter = 0;
1271
                foreach ($this->getGenerator() as $key => $value) {
1272
                    ++$tmpCounter;
1273
1274
                    $values[$key] = $value;
1275
                    if ($tmpCounter === $size) {
1276
                        yield $values;
1277
1278
                        $values = [];
1279
                        $tmpCounter = 0;
1280
                    }
1281
                }
1282
1283
                if ($values !== []) {
1284
                    yield $values;
1285
                }
1286
            };
1287 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...
1288
            $generator = function () use ($size) {
1289 5
                $values = [];
1290 5
                $tmpCounter = 0;
1291 5
                foreach ($this->getGenerator() as $key => $value) {
1292 5
                    ++$tmpCounter;
1293
1294 5
                    $values[] = $value;
1295 5
                    if ($tmpCounter === $size) {
1296 5
                        yield $values;
1297
1298 5
                        $values = [];
1299 5
                        $tmpCounter = 0;
1300
                    }
1301
                }
1302
1303 5
                if ($values !== []) {
1304 4
                    yield $values;
1305
                }
1306 5
            };
1307
        }
1308
1309 5
        return static::create(
1310 5
            $generator,
1311 5
            $this->iteratorClass,
1312 5
            false
1313
        );
1314
    }
1315
1316
    /**
1317
     * Clean all falsy values from the current array.
1318
     *
1319
     * EXAMPLE: <code>
1320
     * a([-8 => -9, 1, 2 => false])->clean(); // Arrayy[-8 => -9, 1]
1321
     * </code>
1322
     *
1323
     * @return static
1324
     *                <p>(Immutable)</p>
1325
     *
1326
     * @psalm-return static<TKey,T>
1327
     * @psalm-mutation-free
1328
     */
1329 8
    public function clean(): self
1330
    {
1331 8
        return $this->filter(
1332
            static function ($value) {
1333 7
                return (bool) $value;
1334 8
            }
1335
        );
1336
    }
1337
1338
    /**
1339
     * WARNING!!! -> Clear the current full array or a $key of it.
1340
     *
1341
     * EXAMPLE: <code>
1342
     * a([-8 => -9, 1, 2 => false])->clear(); // Arrayy[]
1343
     * </code>
1344
     *
1345
     * @param int|int[]|string|string[]|null $key
1346
     *
1347
     * @return $this
1348
     *               <p>(Mutable) Return this Arrayy object, with an empty array.</p>
1349
     *
1350
     * @psalm-return static<TKey,T>
1351
     */
1352 10
    public function clear($key = null): self
1353
    {
1354 10
        if ($key !== null) {
1355 3
            if (\is_array($key)) {
1356 1
                foreach ($key as $keyTmp) {
1357 1
                    $this->offsetUnset($keyTmp);
1358
                }
1359
            } else {
1360 2
                $this->offsetUnset($key);
1361
            }
1362
1363 3
            return $this;
1364
        }
1365
1366 7
        $this->array = [];
1367 7
        $this->generator = null;
1368
1369 7
        return $this;
1370
    }
1371
1372
    /**
1373
     * Check if an item is in the current array.
1374
     *
1375
     * EXAMPLE: <code>
1376
     * a([1, true])->contains(true); // true
1377
     * </code>
1378
     *
1379
     * @param float|int|string $value
1380
     * @param bool             $recursive
1381
     * @param bool             $strict
1382
     *
1383
     * @return bool
1384
     * @psalm-mutation-free
1385
     */
1386 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
1387
    {
1388 23
        if ($recursive === true) {
1389 18
            return $this->in_array_recursive($value, $this->toArray(), $strict);
1390
        }
1391
1392
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1393 14
        foreach ($this->getGeneratorByReference() as &$valueFromArray) {
1394 11
            if ($strict) {
1395 11
                if ($value === $valueFromArray) {
1396 11
                    return true;
1397
                }
1398
            } else {
1399
                /** @noinspection NestedPositiveIfStatementsInspection */
1400
                if ($value == $valueFromArray) {
1401 7
                    return true;
1402
                }
1403
            }
1404
        }
1405
1406 7
        return false;
1407
    }
1408
1409
    /**
1410
     * Check if an (case-insensitive) string is in the current array.
1411
     *
1412
     * EXAMPLE: <code>
1413
     * a(['E', 'é'])->containsCaseInsensitive('É'); // true
1414
     * </code>
1415
     *
1416
     * @param mixed $value
1417
     * @param bool  $recursive
1418
     *
1419
     * @return bool
1420
     * @psalm-mutation-free
1421
     *
1422
     * @psalm-suppress InvalidCast - hack for int|float|bool support
1423
     */
1424 26
    public function containsCaseInsensitive($value, $recursive = false): bool
1425
    {
1426 26
        if ($value === null) {
1427 2
            return false;
1428
        }
1429
1430 24
        if ($recursive === true) {
1431
            /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1432 24
            foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1433 22
                if (\is_array($valueTmp)) {
1434 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
1435 5
                    if ($return === true) {
1436 5
                        return $return;
1437
                    }
1438 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1439 22
                    return true;
1440
                }
1441
            }
1442
1443 8
            return false;
1444
        }
1445
1446
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
1447 12
        foreach ($this->getGeneratorByReference() as $key => &$valueTmp) {
1448 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1449 11
                return true;
1450
            }
1451
        }
1452
1453 4
        return false;
1454
    }
1455
1456
    /**
1457
     * Check if the given key/index exists in the array.
1458
     *
1459
     * EXAMPLE: <code>
1460
     * a([1 => true])->containsKey(1); // true
1461
     * </code>
1462
     *
1463
     * @param int|string $key <p>key/index to search for</p>
1464
     *
1465
     * @return bool
1466
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1467
     *
1468
     * @psalm-mutation-free
1469
     */
1470 4
    public function containsKey($key): bool
1471
    {
1472 4
        return $this->offsetExists($key);
1473
    }
1474
1475
    /**
1476
     * Check if all given needles are present in the array as key/index.
1477
     *
1478
     * EXAMPLE: <code>
1479
     * a([1 => true])->containsKeys(array(1 => 0)); // true
1480
     * </code>
1481
     *
1482
     * @param array $needles   <p>The keys you are searching for.</p>
1483
     * @param bool  $recursive
1484
     *
1485
     * @return bool
1486
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1487
     *
1488
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1489
     * @psalm-mutation-free
1490
     */
1491 2
    public function containsKeys(array $needles, $recursive = false): bool
1492
    {
1493 2
        if ($recursive === true) {
1494
            return
1495 2
                \count(
1496 2
                    \array_intersect(
1497 2
                        $needles,
1498 2
                        $this->keys(true)->toArray()
1499
                    ),
1500 2
                    \COUNT_RECURSIVE
1501
                )
1502
                ===
1503 2
                \count(
1504 2
                    $needles,
1505 2
                    \COUNT_RECURSIVE
1506
                );
1507
        }
1508
1509 1
        return \count(
1510 1
            \array_intersect($needles, $this->keys()->toArray()),
1511 1
            \COUNT_NORMAL
1512
        )
1513
                ===
1514 1
                \count(
1515 1
                    $needles,
1516 1
                    \COUNT_NORMAL
1517
                );
1518
    }
1519
1520
    /**
1521
     * Check if all given needles are present in the array as key/index.
1522
     *
1523
     * @param array $needles <p>The keys you are searching for.</p>
1524
     *
1525
     * @return bool
1526
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1527
     *
1528
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1529
     * @psalm-mutation-free
1530
     */
1531 1
    public function containsKeysRecursive(array $needles): bool
1532
    {
1533 1
        return $this->containsKeys($needles, true);
1534
    }
1535
1536
    /**
1537
     * alias: for "Arrayy->contains()"
1538
     *
1539
     * @param float|int|string $value
1540
     *
1541
     * @return bool
1542
     *
1543
     * @see Arrayy::contains()
1544
     * @psalm-mutation-free
1545
     */
1546 9
    public function containsValue($value): bool
1547
    {
1548 9
        return $this->contains($value);
1549
    }
1550
1551
    /**
1552
     * alias: for "Arrayy->contains($value, true)"
1553
     *
1554
     * @param float|int|string $value
1555
     *
1556
     * @return bool
1557
     *
1558
     * @see Arrayy::contains()
1559
     * @psalm-mutation-free
1560
     */
1561 18
    public function containsValueRecursive($value): bool
1562
    {
1563 18
        return $this->contains($value, true);
1564
    }
1565
1566
    /**
1567
     * Check if all given needles are present in the array.
1568
     *
1569
     * EXAMPLE: <code>
1570
     * a([1, true])->containsValues(array(1, true)); // true
1571
     * </code>
1572
     *
1573
     * @param array $needles
1574
     *
1575
     * @return bool
1576
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1577
     *
1578
     * @psalm-param array<mixed>|array<T> $needles
1579
     * @psalm-mutation-free
1580
     */
1581 1
    public function containsValues(array $needles): bool
1582
    {
1583 1
        return \count(
1584 1
            \array_intersect(
1585 1
                $needles,
1586 1
                $this->toArray()
1587
            ),
1588 1
            \COUNT_NORMAL
1589
        )
1590
               ===
1591 1
               \count(
1592 1
                   $needles,
1593 1
                   \COUNT_NORMAL
1594
               );
1595
    }
1596
1597
    /**
1598
     * Counts all the values of an array
1599
     *
1600
     * @see          http://php.net/manual/en/function.array-count-values.php
1601
     *
1602
     * @return static
1603
     *                <p>
1604
     *                (Immutable)
1605
     *                An associative Arrayy-object of values from input as
1606
     *                keys and their count as value.
1607
     *                </p>
1608
     *
1609
     * @psalm-return static<TKey,T>
1610
     * @psalm-mutation-free
1611
     */
1612 7
    public function countValues(): self
1613
    {
1614 7
        return self::create(\array_count_values($this->toArray()), $this->iteratorClass);
1615
    }
1616
1617
    /**
1618
     * Creates an Arrayy object.
1619
     *
1620
     * @param mixed  $data
1621
     * @param string $iteratorClass
1622
     * @param bool   $checkPropertiesInConstructor
1623
     *
1624
     * @return static
1625
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1626
     *
1627
     * @psalm-param  class-string<\Arrayy\ArrayyIterator> $iteratorClass
1628
     *
1629
     * @psalm-mutation-free
1630
     */
1631 729
    public static function create(
1632
        $data = [],
1633
        string $iteratorClass = ArrayyIterator::class,
1634
        bool $checkPropertiesInConstructor = true
1635
    ) {
1636 729
        return new static(
1637 729
            $data,
1638 729
            $iteratorClass,
1639 729
            $checkPropertiesInConstructor
1640
        );
1641
    }
1642
1643
    /**
1644
     * Flatten an array with the given character as a key delimiter.
1645
     *
1646
     * EXAMPLE: <code>
1647
     * $dot = a(['foo' => ['abc' => 'xyz', 'bar' => ['baz']]]);
1648
     * $flatten = $dot->flatten();
1649
     * $flatten['foo.abc']; // 'xyz'
1650
     * $flatten['foo.bar.0']; // 'baz'
1651
     * </code>
1652
     *
1653
     * @param string     $delimiter
1654
     * @param string     $prepend
1655
     * @param array|null $items
1656
     *
1657
     * @return array
1658
     */
1659 2
    public function flatten($delimiter = '.', $prepend = '', $items = null)
1660
    {
1661
        // init
1662 2
        $flatten = [];
1663
1664 2
        if ($items === null) {
1665 2
            $items = $this->getArray();
1666
        }
1667
1668 2
        foreach ($items as $key => $value) {
1669 2
            if (\is_array($value) && $value !== []) {
1670 2
                $flatten[] = $this->flatten($delimiter, $prepend . $key . $delimiter, $value);
1671
            } else {
1672 2
                $flatten[] = [$prepend . $key => $value];
1673
            }
1674
        }
1675
1676 2
        if (\count($flatten) === 0) {
1677
            return [];
1678
        }
1679
1680 2
        return \array_merge_recursive([], ...$flatten);
1681
    }
1682
1683
    /**
1684
     * WARNING: Creates an Arrayy object by reference.
1685
     *
1686
     * @param array $array
1687
     *
1688
     * @return $this
1689
     *               <p>(Mutable) Return this Arrayy object.</p>
1690
     *
1691
     * @psalm-param  array<TKey,T> $array
1692
     *
1693
     * @internal This will not check any types because it's set directly as reference.
1694
     */
1695 26
    public function createByReference(array &$array = []): self
1696
    {
1697 26
        $this->array = &$array;
1698 26
        $this->generator = null;
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<TKey,T> $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<TKey,T> $generator
1729
     *
1730
     * @psalm-mutation-free
1731
     */
1732 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1733
    {
1734 4
        return self::create(\iterator_to_array($generator, true));
1735
    }
1736
1737
    /**
1738
     * Create an new Arrayy object via JSON.
1739
     *
1740
     * @param string $json
1741
     *
1742
     * @return static
1743
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1744
     *
1745
     * @psalm-mutation-free
1746
     */
1747 5
    public static function createFromJson(string $json): self
1748
    {
1749 5
        return static::create(\json_decode($json, true));
1750
    }
1751
1752
    /**
1753
     * Create an new Arrayy object via JSON.
1754
     *
1755
     * @param array $array
1756
     *
1757
     * @return static
1758
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1759
     *
1760
     * @psalm-mutation-free
1761
     */
1762 1
    public static function createFromArray(array $array): self
1763
    {
1764 1
        return static::create($array);
1765
    }
1766
1767
    /**
1768
     * Create an new instance filled with values from an object that is iterable.
1769
     *
1770
     * @param \Traversable $object <p>iterable object</p>
1771
     *
1772
     * @return static
1773
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1774
     *
1775
     * @psalm-param \Traversable<array-key,mixed> $object
1776
     *
1777
     * @psalm-mutation-free
1778
     */
1779 4
    public static function createFromObject(\Traversable $object): self
1780
    {
1781
        // init
1782 4
        $arrayy = new static();
1783
1784 4
        if ($object instanceof self) {
1785 4
            $objectArray = $object->getGenerator();
1786
        } else {
1787
            $objectArray = $object;
1788
        }
1789
1790 4
        foreach ($objectArray as $key => $value) {
1791
            /**
1792
             * @psalm-suppress ImpureMethodCall - object is already re-created
1793
             */
1794 3
            $arrayy->internalSet($key, $value);
1795
        }
1796
1797 4
        return $arrayy;
1798
    }
1799
1800
    /**
1801
     * Create an new instance filled with values from an object.
1802
     *
1803
     * @param object $object
1804
     *
1805
     * @return static
1806
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1807
     *
1808
     * @psalm-mutation-free
1809
     */
1810 5
    public static function createFromObjectVars($object): self
1811
    {
1812 5
        return self::create(self::objectToArray($object));
1813
    }
1814
1815
    /**
1816
     * Create an new Arrayy object via string.
1817
     *
1818
     * @param string      $str       <p>The input string.</p>
1819
     * @param string|null $delimiter <p>The boundary string.</p>
1820
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1821
     *                               used.</p>
1822
     *
1823
     * @return static
1824
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1825
     *
1826
     * @psalm-mutation-free
1827
     */
1828 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1829
    {
1830 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

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

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

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

Loading history...
1994
    {
1995 10
        if (\is_callable($function) === false) {
1996
            throw new \InvalidArgumentException('Passed function must be callable');
1997
        }
1998
1999 10
        $this->generatorToArray();
2000
2001 10
        \usort($this->array, $function);
2002
2003 10
        return $this;
2004
    }
2005
2006
    /**
2007
     * Custom sort by value via "usort".
2008
     *
2009
     * @see          http://php.net/manual/en/function.usort.php
2010
     *
2011
     * @param callable $function
2012
     *
2013
     * @throws \InvalidArgumentException
2014
     *
2015
     * @return $this
2016
     *               <p>(Immutable) Return this Arrayy object.</p>
2017
     *
2018
     * @psalm-return static<TKey,T>
2019
     * @psalm-mutation-free
2020
     */
2021 4
    public function customSortValuesImmutable($function): self
2022
    {
2023 4
        $that = clone $this;
2024
2025
        /**
2026
         * @psalm-suppress ImpureMethodCall - object is already cloned
2027
         */
2028 4
        $that->customSortValues($function);
2029
2030 4
        return $that;
2031
    }
2032
2033
    /**
2034
     * Delete the given key or keys.
2035
     *
2036
     * @param int|int[]|string|string[] $keyOrKeys
2037
     *
2038
     * @return void
2039
     */
2040 9
    public function delete($keyOrKeys)
2041
    {
2042 9
        $keyOrKeys = (array) $keyOrKeys;
2043
2044 9
        foreach ($keyOrKeys as $key) {
2045 9
            $this->offsetUnset($key);
2046
        }
2047 9
    }
2048
2049
    /**
2050
     * Return elements where the 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 View Code Duplication
    public function diff(array ...$array): 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...
2066
    {
2067 13
        if (\count($array) > 1) {
2068 1
            $array = \array_merge([], ...$array);
2069
        } else {
2070 13
            $array = $array[0];
2071
        }
2072
2073
        $generator = function () use ($array): \Generator {
2074 13
            foreach ($this->getGenerator() as $key => $value) {
2075 11
                if (\in_array($value, $array, true) === false) {
2076 11
                    yield $key => $value;
2077
                }
2078
            }
2079 13
        };
2080
2081 13
        return static::create(
2082 13
            $generator,
2083 13
            $this->iteratorClass,
2084 13
            false
2085
        );
2086
    }
2087
2088
    /**
2089
     * Return elements where the keys are only in the current array.
2090
     *
2091
     * @param array ...$array
2092
     *
2093
     * @return static
2094
     *                <p>(Immutable)</p>
2095
     *
2096
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
2097
     * @psalm-return static<TKey,T>
2098
     * @psalm-mutation-free
2099
     */
2100 9 View Code Duplication
    public function diffKey(array ...$array): 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...
2101
    {
2102 9
        if (\count($array) > 1) {
2103 1
            $array = \array_replace([], ...$array);
2104
        } else {
2105 8
            $array = $array[0];
2106
        }
2107
2108
        $generator = function () use ($array): \Generator {
2109 9
            foreach ($this->getGenerator() as $key => $value) {
2110 8
                if (\array_key_exists($key, $array) === false) {
2111 8
                    yield $key => $value;
2112
                }
2113
            }
2114 9
        };
2115
2116 9
        return static::create(
2117 9
            $generator,
2118 9
            $this->iteratorClass,
2119 9
            false
2120
        );
2121
    }
2122
2123
    /**
2124
     * Return elements where the values and keys are only in the current array.
2125
     *
2126
     * @param array ...$array
2127
     *
2128
     * @return static
2129
     *                <p>(Immutable)</p>
2130
     *
2131
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2132
     * @psalm-return static<TKey,T>
2133
     * @psalm-mutation-free
2134
     */
2135 9
    public function diffKeyAndValue(array ...$array): self
2136
    {
2137 9
        return static::create(
2138 9
            \array_diff_assoc($this->toArray(), ...$array),
2139 9
            $this->iteratorClass,
2140 9
            false
2141
        );
2142
    }
2143
2144
    /**
2145
     * Return elements where the values are only in the current multi-dimensional array.
2146
     *
2147
     * EXAMPLE: <code>
2148
     * a([1 => [1 => 1], 2 => [2 => 2]])->diffRecursive([1 => [1 => 1]]); // Arrayy[2 => [2 => 2]]
2149
     * </code>
2150
     *
2151
     * @param array                 $array
2152
     * @param array|\Generator|null $helperVariableForRecursion <p>(only for internal usage)</p>
2153
     *
2154
     * @return static
2155
     *                <p>(Immutable)</p>
2156
     *
2157
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2158
     * @psalm-param  null|array<TKey,T>|\Generator<TKey,T> $helperVariableForRecursion
2159
     * @psalm-return static<TKey,T>
2160
     * @psalm-mutation-free
2161
     */
2162 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
2163
    {
2164
        // init
2165 1
        $result = [];
2166
2167
        if (
2168 1
            $helperVariableForRecursion !== null
2169
            &&
2170 1
            \is_array($helperVariableForRecursion)
2171
        ) {
2172
            $arrayForTheLoop = $helperVariableForRecursion;
2173
        } else {
2174 1
            $arrayForTheLoop = $this->getGenerator();
2175
        }
2176
2177 1
        foreach ($arrayForTheLoop as $key => $value) {
2178 1
            if ($value instanceof self) {
2179 1
                $value = $value->toArray();
2180
            }
2181
2182 1
            if (\array_key_exists($key, $array)) {
2183 1
                if ($value !== $array[$key]) {
2184 1
                    $result[$key] = $value;
2185
                }
2186
            } else {
2187 1
                $result[$key] = $value;
2188
            }
2189
        }
2190
2191 1
        return static::create(
2192 1
            $result,
2193 1
            $this->iteratorClass,
2194 1
            false
2195
        );
2196
    }
2197
2198
    /**
2199
     * Return elements where the values that are only in the new $array.
2200
     *
2201
     * EXAMPLE: <code>
2202
     * a([1 => 1])->diffReverse([1 => 1, 2 => 2]); // Arrayy[2 => 2]
2203
     * </code>
2204
     *
2205
     * @param array $array
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     *
2210
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
2211
     * @psalm-return static<TKey,T>
2212
     * @psalm-mutation-free
2213
     */
2214 8
    public function diffReverse(array $array = []): self
2215
    {
2216 8
        return static::create(
2217 8
            \array_diff($array, $this->toArray()),
2218 8
            $this->iteratorClass,
2219 8
            false
2220
        );
2221
    }
2222
2223
    /**
2224
     * Divide an array into two arrays. One with keys and the other with values.
2225
     *
2226
     * EXAMPLE: <code>
2227
     * a(['a' => 1, 'b' => ''])->divide(); // Arrayy[Arrayy['a', 'b'], Arrayy[1, '']]
2228
     * </code>
2229
     *
2230
     * @return static
2231
     *                <p>(Immutable)</p>
2232
     *
2233
     * @psalm-return static<TKey,T>
2234
     * @psalm-mutation-free
2235
     */
2236 1
    public function divide(): self
2237
    {
2238 1
        return static::create(
2239
            [
2240 1
                $this->keys(),
2241 1
                $this->values(),
2242
            ],
2243 1
            $this->iteratorClass,
2244 1
            false
2245
        );
2246
    }
2247
2248
    /**
2249
     * Iterate over the current array and modify the array's value.
2250
     *
2251
     * EXAMPLE: <code>
2252
     * $result = A::create();
2253
     * $closure = function ($value) {
2254
     *     return ':' . $value . ':';
2255
     * };
2256
     * a(['foo', 'bar' => 'bis'])->each($closure); // Arrayy[':foo:', 'bar' => ':bis:']
2257
     * </code>
2258
     *
2259
     * @param \Closure $closure
2260
     *
2261
     * @return static
2262
     *                <p>(Immutable)</p>
2263
     *
2264
     * @psalm-return static<TKey,T>
2265
     * @psalm-mutation-free
2266
     */
2267 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...
2268
    {
2269
        // init
2270 5
        $array = [];
2271
2272 5
        foreach ($this->getGenerator() as $key => $value) {
2273 5
            $array[$key] = $closure($value, $key);
2274
        }
2275
2276 5
        return static::create(
2277 5
            $array,
2278 5
            $this->iteratorClass,
2279 5
            false
2280
        );
2281
    }
2282
2283
    /**
2284
     * Sets the internal iterator to the last element in the array and returns this element.
2285
     *
2286
     * @return mixed
2287
     */
2288
    public function end()
2289
    {
2290
        return \end($this->array);
2291
    }
2292
2293
    /**
2294
     * Check if a value is in the current array using a closure.
2295
     *
2296
     * EXAMPLE: <code>
2297
     * $callable = function ($value, $key) {
2298
     *     return 2 === $key and 'two' === $value;
2299
     * };
2300
     * a(['foo', 2 => 'two'])->exists($callable); // true
2301
     * </code>
2302
     *
2303
     * @param \Closure $closure
2304
     *
2305
     * @return bool
2306
     *              <p>Returns true if the given value is found, false otherwise.</p>
2307
     */
2308 4
    public function exists(\Closure $closure): bool
2309
    {
2310
        // init
2311 4
        $isExists = false;
2312
2313 4
        foreach ($this->getGenerator() as $key => $value) {
2314 3
            if ($closure($value, $key)) {
2315 1
                $isExists = true;
2316
2317 3
                break;
2318
            }
2319
        }
2320
2321 4
        return $isExists;
2322
    }
2323
2324
    /**
2325
     * Fill the array until "$num" with "$default" values.
2326
     *
2327
     * EXAMPLE: <code>
2328
     * a(['bar'])->fillWithDefaults(3, 'foo'); // Arrayy['bar', 'foo', 'foo']
2329
     * </code>
2330
     *
2331
     * @param int   $num
2332
     * @param mixed $default
2333
     *
2334
     * @return static
2335
     *                <p>(Immutable)</p>
2336
     *
2337
     * @psalm-return static<TKey,T>
2338
     * @psalm-mutation-free
2339
     */
2340 8
    public function fillWithDefaults(int $num, $default = null): self
2341
    {
2342 8
        if ($num < 0) {
2343 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
2344
        }
2345
2346 7
        $this->generatorToArray();
2347
2348 7
        $tmpArray = $this->array;
2349
2350 7
        $count = \count($tmpArray);
2351
2352 7
        while ($count < $num) {
2353 4
            $tmpArray[] = $default;
2354 4
            ++$count;
2355
        }
2356
2357 7
        return static::create(
2358 7
            $tmpArray,
2359 7
            $this->iteratorClass,
2360 7
            false
2361
        );
2362
    }
2363
2364
    /**
2365
     * Find all items in an array that pass the truth test.
2366
     *
2367
     * EXAMPLE: <code>
2368
     * $closure = function ($value) {
2369
     *     return $value % 2 !== 0;
2370
     * }
2371
     * a([1, 2, 3, 4])->filter($closure); // Arrayy[0 => 1, 2 => 3]
2372
     * </code>
2373
     *
2374
     * @param \Closure|null $closure [optional] <p>
2375
     *                               The callback function to use
2376
     *                               </p>
2377
     *                               <p>
2378
     *                               If no callback is supplied, all entries of
2379
     *                               input equal to false (see
2380
     *                               converting to
2381
     *                               boolean) will be removed.
2382
     *                               </p>
2383
     * @param int           $flag    [optional] <p>
2384
     *                               Flag determining what arguments are sent to <i>callback</i>:
2385
     *                               </p>
2386
     *                               <ul>
2387
     *                               <li>
2388
     *                               <b>ARRAY_FILTER_USE_KEY</b> (1) - pass key as the only argument
2389
     *                               to <i>callback</i> instead of the value
2390
     *                               </li>
2391
     *                               <li>
2392
     *                               <b>ARRAY_FILTER_USE_BOTH</b> (2) - pass both value and key as
2393
     *                               arguments to <i>callback</i> instead of the value
2394
     *                               </li>
2395
     *                               </ul>
2396
     *
2397
     * @return static
2398
     *                <p>(Immutable)</p>
2399
     *
2400
     * @psalm-param null|\Closure(T=,TKey=):bool|\Closure(T=):bool||\Closure(TKey=):bool $closure
2401
     * @psalm-return static<TKey,T>
2402
     * @psalm-mutation-free
2403
     */
2404 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
2405
    {
2406 12
        if (!$closure) {
2407 1
            return $this->clean();
2408
        }
2409
2410 12
        if ($flag === \ARRAY_FILTER_USE_KEY) {
2411
            $generator = function () use ($closure) {
2412 1
                foreach ($this->getGenerator() as $key => $value) {
2413 1
                    if ($closure($key) === true) {
2414 1
                        yield $key => $value;
2415
                    }
2416
                }
2417 1
            };
2418 12
        } elseif ($flag === \ARRAY_FILTER_USE_BOTH) {
2419
            $generator = function () use ($closure) {
2420 11
                foreach ($this->getGenerator() as $key => $value) {
2421 10
                    if ($closure($value, $key) === true) {
2422 10
                        yield $key => $value;
2423
                    }
2424
                }
2425 12
            };
2426
        } else {
2427
            $generator = function () use ($closure) {
2428 1
                foreach ($this->getGenerator() as $key => $value) {
2429 1
                    if ($closure($value) === true) {
2430 1
                        yield $key => $value;
2431
                    }
2432
                }
2433 1
            };
2434
        }
2435
2436 12
        return static::create(
2437 12
            $generator,
2438 12
            $this->iteratorClass,
2439 12
            false
2440
        );
2441
    }
2442
2443
    /**
2444
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
2445
     * property within that.
2446
     *
2447
     * @param string $property
2448
     * @param mixed  $value
2449
     * @param string $comparisonOp
2450
     *                             <p>
2451
     *                             'eq' (equals),<br />
2452
     *                             'gt' (greater),<br />
2453
     *                             'gte' || 'ge' (greater or equals),<br />
2454
     *                             'lt' (less),<br />
2455
     *                             'lte' || 'le' (less or equals),<br />
2456
     *                             'ne' (not equals),<br />
2457
     *                             'contains',<br />
2458
     *                             'notContains',<br />
2459
     *                             'newer' (via strtotime),<br />
2460
     *                             'older' (via strtotime),<br />
2461
     *                             </p>
2462
     *
2463
     * @return static
2464
     *                <p>(Immutable)</p>
2465
     *
2466
     * @psalm-param mixed|T $value
2467
     * @psalm-return static<TKey,T>
2468
     * @psalm-mutation-free
2469
     *
2470
     * @psalm-suppress MissingClosureReturnType
2471
     * @psalm-suppress MissingClosureParamType
2472
     */
2473 1
    public function filterBy(
2474
        string $property,
2475
        $value,
2476
        string $comparisonOp = null
2477
    ): self {
2478 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...
2479 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
2480
        }
2481
2482
        $ops = [
2483
            'eq' => static function ($item, $prop, $value): bool {
2484 1
                return $item[$prop] === $value;
2485 1
            },
2486
            'gt' => static function ($item, $prop, $value): bool {
2487
                return $item[$prop] > $value;
2488 1
            },
2489
            'ge' => static function ($item, $prop, $value): bool {
2490
                return $item[$prop] >= $value;
2491 1
            },
2492
            'gte' => static function ($item, $prop, $value): bool {
2493
                return $item[$prop] >= $value;
2494 1
            },
2495
            'lt' => static function ($item, $prop, $value): bool {
2496 1
                return $item[$prop] < $value;
2497 1
            },
2498
            'le' => static function ($item, $prop, $value): bool {
2499
                return $item[$prop] <= $value;
2500 1
            },
2501
            'lte' => static function ($item, $prop, $value): bool {
2502
                return $item[$prop] <= $value;
2503 1
            },
2504
            'ne' => static function ($item, $prop, $value): bool {
2505
                return $item[$prop] !== $value;
2506 1
            },
2507
            'contains' => static function ($item, $prop, $value): bool {
2508 1
                return \in_array($item[$prop], (array) $value, true);
2509 1
            },
2510
            'notContains' => static function ($item, $prop, $value): bool {
2511
                return !\in_array($item[$prop], (array) $value, true);
2512 1
            },
2513
            'newer' => static function ($item, $prop, $value): bool {
2514
                return \strtotime($item[$prop]) > \strtotime($value);
2515 1
            },
2516
            'older' => static function ($item, $prop, $value): bool {
2517
                return \strtotime($item[$prop]) < \strtotime($value);
2518 1
            },
2519
        ];
2520
2521 1
        $result = \array_values(
2522 1
            \array_filter(
2523 1
                $this->toArray(false, true),
2524
                static function ($item) use (
2525 1
                    $property,
2526 1
                    $value,
2527 1
                    $ops,
2528 1
                    $comparisonOp
2529
                ) {
2530 1
                    $item = (array) $item;
2531 1
                    $itemArrayy = static::create($item);
2532 1
                    $item[$property] = $itemArrayy->get($property, []);
2533
2534 1
                    return $ops[$comparisonOp]($item, $property, $value);
2535 1
                }
2536
            )
2537
        );
2538
2539 1
        return static::create(
2540 1
            $result,
2541 1
            $this->iteratorClass,
2542 1
            false
2543
        );
2544
    }
2545
2546
    /**
2547
     * Find the first item in an array that passes the truth test, otherwise return false.
2548
     *
2549
     * EXAMPLE: <code>
2550
     * $search = 'foo';
2551
     * $closure = function ($value, $key) use ($search) {
2552
     *     return $value === $search;
2553
     * };
2554
     * a(['foo', 'bar', 'lall'])->find($closure); // 'foo'
2555
     * </code>
2556
     *
2557
     * @param \Closure $closure
2558
     *
2559
     * @return false|mixed
2560
     *                     <p>Return false if we did not find the value.</p>
2561
     */
2562 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...
2563
    {
2564 8
        foreach ($this->getGenerator() as $key => $value) {
2565 6
            if ($closure($value, $key)) {
2566 6
                return $value;
2567
            }
2568
        }
2569
2570 3
        return false;
2571
    }
2572
2573
    /**
2574
     * find by ...
2575
     *
2576
     * EXAMPLE: <code>
2577
     * $array = [
2578
     *     0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01'],
2579
     *     1 => ['id' => 456, 'name' => 'bar', 'group' => 'primary', 'value' => 1468, 'when' => '2014-07-15'],
2580
     * ];
2581
     * a($array)->filterBy('name', 'foo'); // Arrayy[0 => ['id' => 123, 'name' => 'foo', 'group' => 'primary', 'value' => 123456, 'when' => '2014-01-01']]
2582
     * </code>
2583
     *
2584
     * @param string $property
2585
     * @param mixed  $value
2586
     * @param string $comparisonOp
2587
     *
2588
     * @return static
2589
     *                <p>(Immutable)</p>
2590
     *
2591
     * @psalm-param mixed|T $value
2592
     * @psalm-return static<TKey,T>
2593
     * @psalm-mutation-free
2594
     */
2595 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
2596
    {
2597 1
        return $this->filterBy($property, $value, $comparisonOp);
2598
    }
2599
2600
    /**
2601
     * Get the first value from the current array.
2602
     *
2603
     * EXAMPLE: <code>
2604
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->first(); // 'foo'
2605
     * </code>
2606
     *
2607
     * @return mixed
2608
     *               <p>Return null if there wasn't a element.</p>
2609
     */
2610 22
    public function first()
2611
    {
2612 22
        $key_first = $this->firstKey();
2613 22
        if ($key_first === null) {
2614 3
            return null;
2615
        }
2616
2617 19
        return $this->get($key_first);
2618
    }
2619
2620
    /**
2621
     * Get the first key from the current array.
2622
     *
2623
     * @return mixed
2624
     *               <p>Return null if there wasn't a element.</p>
2625
     * @psalm-mutation-free
2626
     */
2627 29
    public function firstKey()
2628
    {
2629 29
        $this->generatorToArray();
2630
2631 29
        return \array_key_first($this->array);
2632
    }
2633
2634
    /**
2635
     * Get the first value(s) from the current array.
2636
     * And will return an empty array if there was no first entry.
2637
     *
2638
     * EXAMPLE: <code>
2639
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsImmutable(2); // Arrayy[0 => 'foo', 1 => 'bar']
2640
     * </code>
2641
     *
2642
     * @param int|null $number <p>How many values you will take?</p>
2643
     *
2644
     * @return static
2645
     *                <p>(Immutable)</p>
2646
     *
2647
     * @psalm-return static<TKey,T>
2648
     * @psalm-mutation-free
2649
     */
2650 37
    public function firstsImmutable(int $number = null): self
2651
    {
2652 37
        $arrayTmp = $this->toArray();
2653
2654 37
        if ($number === null) {
2655 14
            $array = (array) \array_shift($arrayTmp);
2656
        } else {
2657 23
            $array = \array_splice($arrayTmp, 0, $number);
2658
        }
2659
2660 37
        return static::create(
2661 37
            $array,
2662 37
            $this->iteratorClass,
2663 37
            false
2664
        );
2665
    }
2666
2667
    /**
2668
     * Get the first value(s) from the current array.
2669
     * And will return an empty array if there was no first entry.
2670
     *
2671
     * @param int|null $number <p>How many values you will take?</p>
2672
     *
2673
     * @return static
2674
     *                <p>(Immutable)</p>
2675
     *
2676
     * @psalm-return static<TKey,T>
2677
     * @psalm-mutation-free
2678
     */
2679 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...
2680
    {
2681 3
        $arrayTmp = $this->keys()->toArray();
2682
2683 3
        if ($number === null) {
2684
            $array = (array) \array_shift($arrayTmp);
2685
        } else {
2686 3
            $array = \array_splice($arrayTmp, 0, $number);
2687
        }
2688
2689 3
        return static::create(
2690 3
            $array,
2691 3
            $this->iteratorClass,
2692 3
            false
2693
        );
2694
    }
2695
2696
    /**
2697
     * Get and remove the first value(s) from the current array.
2698
     * And will return an empty array if there was no first entry.
2699
     *
2700
     * EXAMPLE: <code>
2701
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->firstsMutable(); // 'foo'
2702
     * </code>
2703
     *
2704
     * @param int|null $number <p>How many values you will take?</p>
2705
     *
2706
     * @return $this
2707
     *               <p>(Mutable)</p>
2708
     *
2709
     * @psalm-return static<TKey,T>
2710
     */
2711 34
    public function firstsMutable(int $number = null): self
2712
    {
2713 34
        $this->generatorToArray();
2714
2715 34
        if ($number === null) {
2716 19
            $this->array = (array) \array_shift($this->array);
2717
        } else {
2718 15
            $this->array = \array_splice($this->array, 0, $number);
2719
        }
2720
2721 34
        return $this;
2722
    }
2723
2724
    /**
2725
     * Exchanges all keys with their associated values in an array.
2726
     *
2727
     * EXAMPLE: <code>
2728
     * a([0 => 'foo', 1 => 'bar'])->flip(); // Arrayy['foo' => 0, 'bar' => 1]
2729
     * </code>
2730
     *
2731
     * @return static
2732
     *                <p>(Immutable)</p>
2733
     *
2734
     * @psalm-return static<array-key,TKey>
2735
     * @psalm-mutation-free
2736
     */
2737 1
    public function flip(): self
2738
    {
2739
        $generator = function (): \Generator {
2740 1
            foreach ($this->getGenerator() as $key => $value) {
2741 1
                yield (string) $value => $key;
2742
            }
2743 1
        };
2744
2745 1
        return static::create(
2746 1
            $generator,
2747 1
            $this->iteratorClass,
2748 1
            false
2749
        );
2750
    }
2751
2752
    /**
2753
     * Get a value from an array (optional using dot-notation).
2754
     *
2755
     * EXAMPLE: <code>
2756
     * $arrayy = a(['user' => ['lastname' => 'Moelleken']]);
2757
     * $arrayy->get('user.lastname'); // 'Moelleken'
2758
     * // ---
2759
     * $arrayy = new A();
2760
     * $arrayy['user'] = ['lastname' => 'Moelleken'];
2761
     * $arrayy['user.firstname'] = 'Lars';
2762
     * $arrayy['user']['lastname']; // Moelleken
2763
     * $arrayy['user.lastname']; // Moelleken
2764
     * $arrayy['user.firstname']; // Lars
2765
     * </code>
2766
     *
2767
     * @param mixed $key            <p>The key to look for.</p>
2768
     * @param mixed $fallback       <p>Value to fallback to.</p>
2769
     * @param array $array          <p>The array to get from, if it's set to "null" we use the current array from the
2770
     *                              class.</p>
2771
     * @param bool  $useByReference
2772
     *
2773
     * @return mixed|static
2774
     *
2775
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
2776
     * @psalm-mutation-free
2777
     */
2778 248
    public function get(
2779
        $key = null,
2780
        $fallback = null,
2781
        array $array = null,
2782
        bool $useByReference = false
2783
    ) {
2784 248
        if ($array === null && $key === null) {
2785 1
            if ($useByReference) {
2786
                return $this;
2787
            }
2788
2789 1
            return clone $this;
2790
        }
2791
2792 248
        if ($array !== null) {
2793 4
            if ($useByReference) {
2794
                $usedArray = &$array;
2795
            } else {
2796 4
                $usedArray = $array;
2797
            }
2798
        } else {
2799 245
            $this->generatorToArray();
2800
2801 245
            if ($useByReference) {
2802 133
                $usedArray = &$this->array;
2803
            } else {
2804 130
                $usedArray = $this->array;
2805
            }
2806
        }
2807
2808 248
        if ($key === null) {
2809 1
            return static::create(
2810 1
                [],
2811 1
                $this->iteratorClass,
2812 1
                false
2813 1
            )->createByReference($usedArray);
2814
        }
2815
2816
        // php cast "bool"-index into "int"-index
2817 248
        if ((bool) $key === $key) {
2818 3
            $key = (int) $key;
2819
        }
2820
2821 248
        if (\array_key_exists($key, $usedArray) === true) {
2822 210
            if (\is_array($usedArray[$key])) {
2823 18
                return static::create(
2824 18
                    [],
2825 18
                    $this->iteratorClass,
2826 18
                    false
2827 18
                )->createByReference($usedArray[$key]);
2828
            }
2829
2830 196
            return $usedArray[$key];
2831
        }
2832
2833
        // crawl through array, get key according to object or not
2834 61
        $usePath = false;
2835
        if (
2836 61
            $this->pathSeparator
2837
            &&
2838 61
            (string) $key === $key
2839
            &&
2840 61
            \strpos($key, $this->pathSeparator) !== false
2841
        ) {
2842 31
            $segments = \explode($this->pathSeparator, (string) $key);
2843 31
            if ($segments !== false) {
2844 31
                $usePath = true;
2845 31
                $usedArrayTmp = $usedArray; // do not use the reference for dot-annotations
2846
2847 31
                foreach ($segments as $segment) {
2848
                    if (
2849
                        (
2850 31
                            \is_array($usedArrayTmp)
2851
                            ||
2852 31
                            $usedArrayTmp instanceof \ArrayAccess
2853
                        )
2854
                        &&
2855 31
                        isset($usedArrayTmp[$segment])
2856
                    ) {
2857 30
                        $usedArrayTmp = $usedArrayTmp[$segment];
2858
2859 30
                        continue;
2860
                    }
2861
2862
                    if (
2863 14
                        \is_object($usedArrayTmp) === true
2864
                        &&
2865 14
                        \property_exists($usedArrayTmp, $segment)
2866
                    ) {
2867 1
                        $usedArrayTmp = $usedArrayTmp->{$segment};
2868
2869 1
                        continue;
2870
                    }
2871
2872 13
                    if (isset($segments[0]) && $segments[0] === '*') {
2873 1
                        $segmentsTmp = $segments;
2874 1
                        unset($segmentsTmp[0]);
2875 1
                        $keyTmp = \implode('.', $segmentsTmp);
2876 1
                        $returnTmp = static::create(
2877 1
                            [],
2878 1
                            $this->iteratorClass,
2879 1
                            false
2880
                        );
2881 1
                        foreach ($this->getAll() as $dataTmp) {
2882 1
                            if ($dataTmp instanceof self) {
2883
                                $returnTmp->add($dataTmp->get($keyTmp));
2884
2885
                                continue;
2886
                            }
2887
2888
                            if (
2889
                                (
2890 1
                                    \is_array($dataTmp)
2891
                                    ||
2892 1
                                    $dataTmp instanceof \ArrayAccess
2893
                                )
2894
                                &&
2895 1
                                isset($dataTmp[$keyTmp])
2896
                            ) {
2897
                                $returnTmp->add($dataTmp[$keyTmp]);
2898
2899
                                continue;
2900
                            }
2901
2902
                            if (
2903 1
                                \is_object($dataTmp) === true
2904
                                &&
2905 1
                                \property_exists($dataTmp, $keyTmp)
2906
                            ) {
2907 1
                                $returnTmp->add($dataTmp->{$keyTmp});
2908
2909
                                /** @noinspection UnnecessaryContinueInspection */
2910 1
                                continue;
2911
                            }
2912
                        }
2913
2914 1
                        if ($returnTmp->count() > 0) {
2915 1
                            return $returnTmp;
2916
                        }
2917
                    }
2918
2919 12
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2920
                }
2921
            }
2922
        }
2923
2924 58
        if (isset($usedArrayTmp)) {
2925 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...
2926
                return $fallback instanceof \Closure ? $fallback() : $fallback;
2927
            }
2928
2929 28
            if (\is_array($usedArrayTmp)) {
2930 6
                return static::create(
2931 6
                    [],
2932 6
                    $this->iteratorClass,
2933 6
                    false
2934 6
                )->createByReference($usedArrayTmp);
2935
            }
2936
2937 28
            return $usedArrayTmp;
2938
        }
2939
2940 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...
2941 30
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2942
        }
2943
2944
        return static::create(
2945
            [],
2946
            $this->iteratorClass,
2947
            false
2948
        )->createByReference($usedArray);
2949
    }
2950
2951
    /**
2952
     * alias: for "Arrayy->toArray()"
2953
     *
2954
     * @return array
2955
     *
2956
     * @see          Arrayy::getArray()
2957
     *
2958
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2959
     */
2960 15
    public function getAll(): array
2961
    {
2962 15
        return $this->toArray();
2963
    }
2964
2965
    /**
2966
     * Get the current array from the "Arrayy"-object.
2967
     *
2968
     * alias for "toArray()"
2969
     *
2970
     * @param bool $convertAllArrayyElements <p>
2971
     *                                       Convert all Child-"Arrayy" objects also to arrays.
2972
     *                                       </p>
2973
     * @param bool $preserveKeys             <p>
2974
     *                                       e.g.: A generator maybe return the same key more then once,
2975
     *                                       so maybe you will ignore the keys.
2976
     *                                       </p>
2977
     *
2978
     * @return array
2979
     *
2980
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2981
     * @psalm-mutation-free
2982
     *
2983
     * @see Arrayy::toArray()
2984
     */
2985 513
    public function getArray(
2986
        bool $convertAllArrayyElements = false,
2987
        bool $preserveKeys = true
2988
    ): array {
2989 513
        return $this->toArray(
2990 513
            $convertAllArrayyElements,
2991 513
            $preserveKeys
2992
        );
2993
    }
2994
2995
    /**
2996
     * @param string $json
2997
     *
2998
     * @return $this
2999
     */
3000 3
    public static function createFromJsonMapper(string $json)
3001
    {
3002
        // init
3003 3
        $class = static::create();
3004
3005 3
        $jsonObject = \json_decode($json, false);
3006
3007 3
        $mapper = new \Arrayy\Mapper\Json();
3008 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...
3009
            if ($class->checkPropertiesMismatchInConstructor) {
3010
                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...
3011
            }
3012
        };
3013
3014 3
        return $mapper->map($jsonObject, $class);
3015
    }
3016
3017
    /**
3018
     * @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...
3019
     *
3020
     * @internal
3021
     */
3022 6
    public function getPhpDocPropertiesFromClass()
3023
    {
3024 6
        if ($this->properties === []) {
3025 1
            $this->properties = $this->getPropertiesFromPhpDoc();
3026
        }
3027
3028 6
        return $this->properties;
3029
    }
3030
3031
    /**
3032
     * Get the current array from the "Arrayy"-object as list.
3033
     *
3034
     * alias for "toList()"
3035
     *
3036
     * @param bool $convertAllArrayyElements <p>
3037
     *                                       Convert all Child-"Arrayy" objects also to arrays.
3038
     *                                       </p>
3039
     *
3040
     * @return array
3041
     *
3042
     * @psalm-return array<int,mixed>|array<int,T>
3043
     * @psalm-mutation-free
3044
     *
3045
     * @see Arrayy::toList()
3046
     */
3047 1
    public function getList(bool $convertAllArrayyElements = false): array
3048
    {
3049 1
        return $this->toList($convertAllArrayyElements);
3050
    }
3051
3052
    /**
3053
     * Returns the values from a single column of the input array, identified by
3054
     * the $columnKey, can be used to extract data-columns from multi-arrays.
3055
     *
3056
     * EXAMPLE: <code>
3057
     * a([['foo' => 'bar', 'id' => 1], ['foo => 'lall', 'id' => 2]])->getColumn('foo', 'id'); // Arrayy[1 => 'bar', 2 => 'lall']
3058
     * </code>
3059
     *
3060
     * INFO: Optionally, you may provide an $indexKey to index the values in the returned
3061
     *       array by the values from the $indexKey column in the input array.
3062
     *
3063
     * @param int|string|null $columnKey
3064
     * @param int|string|null $indexKey
3065
     *
3066
     * @return static
3067
     *                <p>(Immutable)</p>
3068
     *
3069
     * @psalm-return static<TKey,T>
3070
     * @psalm-mutation-free
3071
     */
3072 1
    public function getColumn($columnKey = null, $indexKey = null): self
3073
    {
3074 1
        if ($columnKey === null && $indexKey === null) {
3075
            $generator = function () {
3076 1
                foreach ($this->getGenerator() as $key => $value) {
3077 1
                    yield $value;
3078
                }
3079 1
            };
3080
        } else {
3081
            $generator = function () use ($columnKey, $indexKey) {
3082 1
                foreach ($this->getGenerator() as $key => $value) {
3083
                    // reset
3084 1
                    $newKey = null;
3085 1
                    $newValue = null;
3086 1
                    $newValueFound = false;
3087
3088 1
                    if ($indexKey !== null) {
3089 1
                        foreach ($value as $keyInner => $valueInner) {
3090 1
                            if ($indexKey === $keyInner) {
3091 1
                                $newKey = $valueInner;
3092
                            }
3093
3094 1
                            if ($columnKey === $keyInner) {
3095 1
                                $newValue = $valueInner;
3096 1
                                $newValueFound = true;
3097
                            }
3098
                        }
3099
                    } else {
3100 1
                        foreach ($value as $keyInner => $valueInner) {
3101 1
                            if ($columnKey === $keyInner) {
3102 1
                                $newValue = $valueInner;
3103 1
                                $newValueFound = true;
3104
                            }
3105
                        }
3106
                    }
3107
3108 1
                    if ($newValueFound === false) {
3109 1
                        if ($newKey !== null) {
3110 1
                            yield $newKey => $value;
3111
                        } else {
3112 1
                            yield $value;
3113
                        }
3114
                    } else {
3115
                        /** @noinspection NestedPositiveIfStatementsInspection */
3116 1
                        if ($newKey !== null) {
3117 1
                            yield $newKey => $newValue;
3118
                        } else {
3119 1
                            yield $newValue;
3120
                        }
3121
                    }
3122
                }
3123 1
            };
3124
        }
3125
3126 1
        return static::create(
3127 1
            $generator,
3128 1
            $this->iteratorClass,
3129 1
            false
3130
        );
3131
    }
3132
3133
    /**
3134
     * Get the current array from the "Arrayy"-object as generator by reference.
3135
     *
3136
     * @return \Generator
3137
     *
3138
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3139
     */
3140 75
    public function &getGeneratorByReference(): \Generator
3141
    {
3142 75
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3143
            // -> false-positive -> see "&" from method
3144
            /** @noinspection YieldFromCanBeUsedInspection */
3145 17
            foreach ($this->generator as $key => $value) {
3146 17
                yield $key => $value;
3147
            }
3148
3149 5
            return;
3150
        }
3151
3152
        // -> false-positive -> see "&$value"
3153
        /** @noinspection YieldFromCanBeUsedInspection */
3154
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3155 59
        foreach ($this->array as $key => &$value) {
3156 54
            yield $key => $value;
3157
        }
3158 35
    }
3159
3160
    /**
3161
     * Get the current array from the "Arrayy"-object as generator.
3162
     *
3163
     * @return \Generator
3164
     *
3165
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
3166
     * @psalm-mutation-free
3167
     */
3168 1072
    public function getGenerator(): \Generator
3169
    {
3170 1072
        if ($this->generator instanceof ArrayyRewindableGenerator) {
3171 69
            yield from $this->generator;
3172
3173 69
            return;
3174
        }
3175
3176 1070
        yield from $this->array;
3177 1029
    }
3178
3179
    /**
3180
     * alias: for "Arrayy->keys()"
3181
     *
3182
     * @return static
3183
     *                <p>(Immutable)</p>
3184
     *
3185
     * @see          Arrayy::keys()
3186
     *
3187
     * @psalm-return static<array-key,TKey>
3188
     * @psalm-mutation-free
3189
     */
3190 2
    public function getKeys()
3191
    {
3192 2
        return $this->keys();
3193
    }
3194
3195
    /**
3196
     * Get the current array from the "Arrayy"-object as object.
3197
     *
3198
     * @return \stdClass
3199
     */
3200 4
    public function getObject(): \stdClass
3201
    {
3202 4
        return self::arrayToObject($this->toArray());
3203
    }
3204
3205
    /**
3206
     * alias: for "Arrayy->randomImmutable()"
3207
     *
3208
     * @return static
3209
     *                <p>(Immutable)</p>
3210
     *
3211
     * @see          Arrayy::randomImmutable()
3212
     *
3213
     * @psalm-return static<int|array-key,T>
3214
     */
3215 4
    public function getRandom(): self
3216
    {
3217 4
        return $this->randomImmutable();
3218
    }
3219
3220
    /**
3221
     * alias: for "Arrayy->randomKey()"
3222
     *
3223
     * @return mixed
3224
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3225
     *
3226
     * @see Arrayy::randomKey()
3227
     */
3228 3
    public function getRandomKey()
3229
    {
3230 3
        return $this->randomKey();
3231
    }
3232
3233
    /**
3234
     * alias: for "Arrayy->randomKeys()"
3235
     *
3236
     * @param int $number
3237
     *
3238
     * @return static
3239
     *                <p>(Immutable)</p>
3240
     *
3241
     * @see          Arrayy::randomKeys()
3242
     *
3243
     * @psalm-return static<TKey,T>
3244
     */
3245 8
    public function getRandomKeys(int $number): self
3246
    {
3247 8
        return $this->randomKeys($number);
3248
    }
3249
3250
    /**
3251
     * alias: for "Arrayy->randomValue()"
3252
     *
3253
     * @return mixed
3254
     *               <p>Get a random value or null if there wasn't a value.</p>
3255
     *
3256
     * @see Arrayy::randomValue()
3257
     */
3258 3
    public function getRandomValue()
3259
    {
3260 3
        return $this->randomValue();
3261
    }
3262
3263
    /**
3264
     * alias: for "Arrayy->randomValues()"
3265
     *
3266
     * @param int $number
3267
     *
3268
     * @return static
3269
     *                <p>(Immutable)</p>
3270
     *
3271
     * @see          Arrayy::randomValues()
3272
     *
3273
     * @psalm-return static<TKey,T>
3274
     */
3275 6
    public function getRandomValues(int $number): self
3276
    {
3277 6
        return $this->randomValues($number);
3278
    }
3279
3280
    /**
3281
     * Gets all values.
3282
     *
3283
     * @return static
3284
     *                <p>The values of all elements in this array, in the order they
3285
     *                appear in the array.</p>
3286
     *
3287
     * @psalm-return static<TKey,T>
3288
     */
3289 4
    public function getValues()
3290
    {
3291 4
        $this->generatorToArray(false);
3292
3293 4
        return static::create(
3294 4
            \array_values($this->array),
3295 4
            $this->iteratorClass,
3296 4
            false
3297
        );
3298
    }
3299
3300
    /**
3301
     * Gets all values via Generator.
3302
     *
3303
     * @return \Generator
3304
     *                    <p>The values of all elements in this array, in the order they
3305
     *                    appear in the array as Generator.</p>
3306
     *
3307
     * @psalm-return \Generator<TKey,T>
3308
     */
3309 4
    public function getValuesYield(): \Generator
3310
    {
3311 4
        yield from $this->getGenerator();
3312 4
    }
3313
3314
    /**
3315
     * Group values from a array according to the results of a closure.
3316
     *
3317
     * @param callable|string $grouper  <p>A callable function name.</p>
3318
     * @param bool            $saveKeys
3319
     *
3320
     * @return static
3321
     *                <p>(Immutable)</p>
3322
     *
3323
     * @psalm-return static<TKey,T>
3324
     * @psalm-mutation-free
3325
     */
3326 4
    public function group($grouper, bool $saveKeys = false): self
3327
    {
3328
        // init
3329 4
        $result = [];
3330
3331
        // Iterate over values, group by property/results from closure.
3332 4
        foreach ($this->getGenerator() as $key => $value) {
3333 4
            if (\is_callable($grouper) === true) {
3334 3
                $groupKey = $grouper($value, $key);
3335
            } else {
3336 1
                $groupKey = $this->get($grouper);
3337
            }
3338
3339 4
            $newValue = $this->get($groupKey, null, $result);
3340
3341 4
            if ($groupKey instanceof self) {
3342
                $groupKey = $groupKey->toArray();
3343
            }
3344
3345 4
            if ($newValue instanceof self) {
3346 4
                $newValue = $newValue->toArray();
3347
            }
3348
3349
            // Add to results.
3350 4
            if ($groupKey !== null) {
3351 3
                if ($saveKeys) {
3352 2
                    $result[$groupKey] = $newValue;
3353 2
                    $result[$groupKey][$key] = $value;
3354
                } else {
3355 1
                    $result[$groupKey] = $newValue;
3356 4
                    $result[$groupKey][] = $value;
3357
                }
3358
            }
3359
        }
3360
3361 4
        return static::create(
3362 4
            $result,
3363 4
            $this->iteratorClass,
3364 4
            false
3365
        );
3366
    }
3367
3368
    /**
3369
     * Check if an array has a given key.
3370
     *
3371
     * @param mixed $key
3372
     *
3373
     * @return bool
3374
     */
3375 30
    public function has($key): bool
3376
    {
3377 30
        static $UN_FOUND = null;
3378
3379 30
        if ($UN_FOUND === null) {
3380
            // Generate unique string to use as marker.
3381 1
            $UN_FOUND = 'arrayy--' . \uniqid('arrayy', true);
3382
        }
3383
3384 30
        if (\is_array($key)) {
3385 1
            if ($key === []) {
3386
                return false;
3387
            }
3388
3389 1
            foreach ($key as $keyTmp) {
3390 1
                $found = ($this->get($keyTmp, $UN_FOUND) !== $UN_FOUND);
3391 1
                if ($found === false) {
3392 1
                    return false;
3393
                }
3394
            }
3395
3396 1
            return true;
3397
        }
3398
3399 29
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
3400
    }
3401
3402
    /**
3403
     * Check if an array has a given value.
3404
     *
3405
     * INFO: If you need to search recursive please use ```contains($value, true)```.
3406
     *
3407
     * @param mixed $value
3408
     *
3409
     * @return bool
3410
     */
3411 1
    public function hasValue($value): bool
3412
    {
3413 1
        return $this->contains($value);
3414
    }
3415
3416
    /**
3417
     * Implodes the values of this array.
3418
     *
3419
     * EXAMPLE: <code>
3420
     * a([0 => -9, 1, 2])->implode('|'); // '-9|1|2'
3421
     * </code>
3422
     *
3423
     * @param string $glue
3424
     * @param string $prefix
3425
     *
3426
     * @return string
3427
     * @psalm-mutation-free
3428
     */
3429 28
    public function implode(string $glue = '', string $prefix = ''): string
3430
    {
3431 28
        return $prefix . $this->implode_recursive($glue, $this->toArray(), false);
3432
    }
3433
3434
    /**
3435
     * Implodes the keys of this array.
3436
     *
3437
     * @param string $glue
3438
     *
3439
     * @return string
3440
     * @psalm-mutation-free
3441
     */
3442 8
    public function implodeKeys(string $glue = ''): string
3443
    {
3444 8
        return $this->implode_recursive($glue, $this->toArray(), true);
3445
    }
3446
3447
    /**
3448
     * Given a list and an iterate-function that returns
3449
     * a key for each element in the list (or a property name),
3450
     * returns an object with an index of each item.
3451
     *
3452
     * @param mixed $key
3453
     *
3454
     * @return static
3455
     *                <p>(Immutable)</p>
3456
     *
3457
     * @psalm-return static<TKey,T>
3458
     * @psalm-mutation-free
3459
     */
3460 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...
3461
    {
3462
        // init
3463 4
        $results = [];
3464
3465 4
        foreach ($this->getGenerator() as $a) {
3466 4
            if (\array_key_exists($key, $a) === true) {
3467 4
                $results[$a[$key]] = $a;
3468
            }
3469
        }
3470
3471 4
        return static::create(
3472 4
            $results,
3473 4
            $this->iteratorClass,
3474 4
            false
3475
        );
3476
    }
3477
3478
    /**
3479
     * alias: for "Arrayy->searchIndex()"
3480
     *
3481
     * @param mixed $value <p>The value to search for.</p>
3482
     *
3483
     * @return false|mixed
3484
     *
3485
     * @see Arrayy::searchIndex()
3486
     */
3487 4
    public function indexOf($value)
3488
    {
3489 4
        return $this->searchIndex($value);
3490
    }
3491
3492
    /**
3493
     * Get everything but the last..$to items.
3494
     *
3495
     * EXAMPLE: <code>
3496
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->initial(2); // Arrayy[0 => 'foo']
3497
     * </code>
3498
     *
3499
     * @param int $to
3500
     *
3501
     * @return static
3502
     *                <p>(Immutable)</p>
3503
     *
3504
     * @psalm-return static<TKey,T>
3505
     * @psalm-mutation-free
3506
     */
3507 12
    public function initial(int $to = 1): self
3508
    {
3509 12
        return $this->firstsImmutable(\count($this->toArray(), \COUNT_NORMAL) - $to);
3510
    }
3511
3512
    /**
3513
     * Return an array with all elements found in input array.
3514
     *
3515
     * EXAMPLE: <code>
3516
     * a(['foo', 'bar'])->intersection(['bar', 'baz']); // Arrayy['bar']
3517
     * </code>
3518
     *
3519
     * @param array $search
3520
     * @param bool  $keepKeys
3521
     *
3522
     * @return static
3523
     *                <p>(Immutable)</p>
3524
     *
3525
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $search
3526
     * @psalm-return static<TKey,T>
3527
     * @psalm-mutation-free
3528
     */
3529 4
    public function intersection(array $search, bool $keepKeys = false): self
3530
    {
3531 4
        if ($keepKeys) {
3532
            /**
3533
             * @psalm-suppress MissingClosureReturnType
3534
             * @psalm-suppress MissingClosureParamType
3535
             */
3536 1
            return static::create(
3537 1
                \array_uintersect(
3538 1
                    $this->toArray(),
3539 1
                    $search,
3540
                    static function ($a, $b) {
3541 1
                        return $a === $b ? 0 : -1;
3542 1
                    }
3543
                ),
3544 1
                $this->iteratorClass,
3545 1
                false
3546
            );
3547
        }
3548
3549 3
        return static::create(
3550 3
            \array_values(\array_intersect($this->toArray(), $search)),
3551 3
            $this->iteratorClass,
3552 3
            false
3553
        );
3554
    }
3555
3556
    /**
3557
     * Return an array with all elements found in input array.
3558
     *
3559
     * @param array ...$array
3560
     *
3561
     * @return static
3562
     *                <p>(Immutable)</p>
3563
     *
3564
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$array
3565
     * @psalm-return static<TKey,T>
3566
     * @psalm-mutation-free
3567
     */
3568 1
    public function intersectionMulti(...$array): self
3569
    {
3570 1
        return static::create(
3571 1
            \array_values(\array_intersect($this->toArray(), ...$array)),
3572 1
            $this->iteratorClass,
3573 1
            false
3574
        );
3575
    }
3576
3577
    /**
3578
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
3579
     *
3580
     * EXAMPLE: <code>
3581
     * a(['foo', 'bar'])->intersects(['föö', 'bär']); // false
3582
     * </code>
3583
     *
3584
     * @param array $search
3585
     *
3586
     * @return bool
3587
     *
3588
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
3589
     */
3590 1
    public function intersects(array $search): bool
3591
    {
3592 1
        return $this->intersection($search)->count() > 0;
3593
    }
3594
3595
    /**
3596
     * Invoke a function on all of an array's values.
3597
     *
3598
     * @param callable $callable
3599
     * @param mixed    $arguments
3600
     *
3601
     * @return static
3602
     *                <p>(Immutable)</p>
3603
     *
3604
     * @psalm-param  callable(T=,mixed):mixed $callable
3605
     * @psalm-return static<TKey,T>
3606
     * @psalm-mutation-free
3607
     */
3608 1
    public function invoke($callable, $arguments = []): self
3609
    {
3610
        // If one argument given for each iteration, create an array for it.
3611 1
        if (!\is_array($arguments)) {
3612 1
            $arguments = \array_fill(
3613 1
                0,
3614 1
                $this->count(),
3615 1
                $arguments
3616
            );
3617
        }
3618
3619
        // If the callable has arguments, pass them.
3620 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...
3621 1
            $array = \array_map($callable, $this->toArray(), $arguments);
3622
        } else {
3623 1
            $array = $this->map($callable);
3624
        }
3625
3626 1
        return static::create(
3627 1
            $array,
3628 1
            $this->iteratorClass,
3629 1
            false
3630
        );
3631
    }
3632
3633
    /**
3634
     * Check whether array is associative or not.
3635
     *
3636
     * EXAMPLE: <code>
3637
     * a(['foo' => 'bar', 2, 3])->isAssoc(); // true
3638
     * </code>
3639
     *
3640
     * @param bool $recursive
3641
     *
3642
     * @return bool
3643
     *              <p>Returns true if associative, false otherwise.</p>
3644
     */
3645 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...
3646
    {
3647 15
        if ($this->isEmpty()) {
3648 3
            return false;
3649
        }
3650
3651
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3652 13
        foreach ($this->keys($recursive)->getGeneratorByReference() as &$key) {
3653 13
            if ((string) $key !== $key) {
3654 13
                return false;
3655
            }
3656
        }
3657
3658 3
        return true;
3659
    }
3660
3661
    /**
3662
     * Check if a given key or keys are empty.
3663
     *
3664
     * @param int|int[]|string|string[]|null $keys
3665
     *
3666
     * @return bool
3667
     *              <p>Returns true if empty, false otherwise.</p>
3668
     * @psalm-mutation-free
3669
     */
3670 45
    public function isEmpty($keys = null): bool
3671
    {
3672 45
        if ($this->generator) {
3673
            return $this->toArray() === [];
3674
        }
3675
3676 45
        if ($keys === null) {
3677 43
            return $this->array === [];
3678
        }
3679
3680 2
        foreach ((array) $keys as $key) {
3681 2
            if (!empty($this->get($key))) {
3682 2
                return false;
3683
            }
3684
        }
3685
3686 2
        return true;
3687
    }
3688
3689
    /**
3690
     * Check if the current array is equal to the given "$array" or not.
3691
     *
3692
     * EXAMPLE: <code>
3693
     * a(['💩'])->isEqual(['💩']); // true
3694
     * </code>
3695
     *
3696
     * @param array $array
3697
     *
3698
     * @return bool
3699
     *
3700
     * @psalm-param array<mixed,mixed> $array
3701
     */
3702 1
    public function isEqual(array $array): bool
3703
    {
3704 1
        return $this->toArray() === $array;
3705
    }
3706
3707
    /**
3708
     * Check if the current array is a multi-array.
3709
     *
3710
     * EXAMPLE: <code>
3711
     * a(['foo' => [1, 2 , 3]])->isMultiArray(); // true
3712
     * </code>
3713
     *
3714
     * @return bool
3715
     */
3716 22
    public function isMultiArray(): bool
3717
    {
3718 22
        foreach ($this->getGenerator() as $key => $value) {
3719 20
            if (\is_array($value)) {
3720 20
                return true;
3721
            }
3722
        }
3723
3724 18
        return false;
3725
    }
3726
3727
    /**
3728
     * Check whether array is numeric or not.
3729
     *
3730
     * @return bool
3731
     *              <p>Returns true if numeric, false otherwise.</p>
3732
     */
3733 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...
3734
    {
3735 5
        if ($this->isEmpty()) {
3736 2
            return false;
3737
        }
3738
3739
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3740 4
        foreach ($this->keys()->getGeneratorByReference() as &$key) {
3741 4
            if ((int) $key !== $key) {
3742 4
                return false;
3743
            }
3744
        }
3745
3746 2
        return true;
3747
    }
3748
3749
    /**
3750
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
3751
     *
3752
     * EXAMPLE: <code>
3753
     * a([0 => 'foo', 1 => 'lall', 2 => 'foobar'])->isSequential(); // true
3754
     * </code>
3755
     *
3756
     * INFO: If the array is empty we count it as non-sequential.
3757
     *
3758
     * @param bool $recursive
3759
     *
3760
     * @return bool
3761
     * @psalm-mutation-free
3762
     */
3763 10
    public function isSequential(bool $recursive = false): bool
3764
    {
3765 10
        $i = 0;
3766 10
        foreach ($this->getGenerator() as $key => $value) {
3767
            /** @noinspection IsIterableCanBeUsedInspection */
3768
            /** @phpstan-ignore-next-line */
3769
            if (
3770 9
                $recursive
3771
                &&
3772 9
                (\is_array($value) || $value instanceof \Traversable)
3773
                &&
3774 9
                self::create($value)->isSequential() === false
3775
            ) {
3776 1
                return false;
3777
            }
3778
3779 9
            if ($key !== $i) {
3780 3
                return false;
3781
            }
3782
3783 8
            ++$i;
3784
        }
3785
3786
        /** @noinspection IfReturnReturnSimplificationInspection */
3787 9
        if ($i === 0) {
3788 3
            return false;
3789
        }
3790
3791 8
        return true;
3792
    }
3793
3794
    /**
3795
     * @return array
3796
     *
3797
     * @psalm-return array<mixed,mixed>|array<TKey,T>
3798
     */
3799 2
    public function jsonSerialize(): array
3800
    {
3801 2
        return $this->toArray();
3802
    }
3803
3804
    /**
3805
     * Gets the key/index of the element at the current internal iterator position.
3806
     *
3807
     * @return int|string|null
3808
     */
3809
    public function key()
3810
    {
3811
        return \key($this->array);
3812
    }
3813
3814
    /**
3815
     * Checks if the given key exists in the provided array.
3816
     *
3817
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
3818
     *       then you need to use "Arrayy->offsetExists()".
3819
     *
3820
     * @param int|string $key the key to look for
3821
     *
3822
     * @return bool
3823
     * @psalm-mutation-free
3824
     */
3825 174
    public function keyExists($key): bool
3826
    {
3827 174
        foreach ($this->getGenerator() as $keyTmp => $value) {
3828 169
            if ($key === $keyTmp) {
3829 169
                return true;
3830
            }
3831
        }
3832
3833 131
        return false;
3834
    }
3835
3836
    /**
3837
     * Get all keys from the current array.
3838
     *
3839
     * EXAMPLE: <code>
3840
     * a([1 => 'foo', 2 => 'foo2', 3 => 'bar'])->keys(); // Arrayy[1, 2, 3]
3841
     * </code>
3842
     *
3843
     * @param bool       $recursive     [optional] <p>
3844
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
3845
     *                                  </p>
3846
     * @param mixed|null $search_values [optional] <p>
3847
     *                                  If specified, then only keys containing these values are returned.
3848
     *                                  </p>
3849
     * @param bool       $strict        [optional] <p>
3850
     *                                  Determines if strict comparison (===) should be used during the search.
3851
     *                                  </p>
3852
     *
3853
     * @return static
3854
     *                <p>(Immutable) An array of all the keys in input.</p>
3855
     *
3856
     * @psalm-return static<array-key,TKey>
3857
     * @psalm-mutation-free
3858
     */
3859 29
    public function keys(
3860
        bool $recursive = false,
3861
        $search_values = null,
3862
        bool $strict = true
3863
    ): self {
3864
3865
        // recursive
3866
3867 29
        if ($recursive === true) {
3868 4
            $array = $this->array_keys_recursive(
3869 4
                null,
3870 4
                $search_values,
3871 4
                $strict
3872
            );
3873
3874 4
            return static::create(
3875 4
                $array,
3876 4
                $this->iteratorClass,
3877 4
                false
3878
            );
3879
        }
3880
3881
        // non recursive
3882
3883 28
        if ($search_values === null) {
3884
            $arrayFunction = function (): \Generator {
3885 28
                foreach ($this->getGenerator() as $key => $value) {
3886 26
                    yield $key;
3887
                }
3888 28
            };
3889
        } else {
3890
            $arrayFunction = function () use ($search_values, $strict): \Generator {
3891 1
                $is_array_tmp = \is_array($search_values);
3892
3893
                /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
3894 1
                foreach ($this->getGeneratorByReference() as $key => &$value) {
3895 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...
3896
                        (
3897 1
                            $is_array_tmp === false
3898
                            &&
3899 1
                            $strict === true
3900
                            &&
3901 1
                            $search_values === $value
3902
                        )
3903
                        ||
3904
                        (
3905 1
                            $is_array_tmp === false
3906
                            &&
3907 1
                            $strict === false
3908
                            &&
3909 1
                            $search_values == $value
3910
                        )
3911
                        ||
3912
                        (
3913 1
                            $is_array_tmp === true
3914
                            &&
3915 1
                            \in_array($value, $search_values, $strict)
3916
                        )
3917
                    ) {
3918 1
                        yield $key;
3919
                    }
3920
                }
3921 1
            };
3922
        }
3923
3924 28
        return static::create(
3925 28
            $arrayFunction,
3926 28
            $this->iteratorClass,
3927 28
            false
3928
        );
3929
    }
3930
3931
    /**
3932
     * Sort an array by key in reverse order.
3933
     *
3934
     * @param int $sort_flags [optional] <p>
3935
     *                        You may modify the behavior of the sort using the optional
3936
     *                        parameter sort_flags, for details
3937
     *                        see sort.
3938
     *                        </p>
3939
     *
3940
     * @return $this
3941
     *               <p>(Mutable) Return this Arrayy object.</p>
3942
     *
3943
     * @psalm-return static<TKey,T>
3944
     */
3945 4
    public function krsort(int $sort_flags = 0): self
3946
    {
3947 4
        $this->generatorToArray();
3948
3949 4
        \krsort($this->array, $sort_flags);
3950
3951 4
        return $this;
3952
    }
3953
3954
    /**
3955
     * Sort an array by key in reverse order.
3956
     *
3957
     * @param int $sort_flags [optional] <p>
3958
     *                        You may modify the behavior of the sort using the optional
3959
     *                        parameter sort_flags, for details
3960
     *                        see sort.
3961
     *                        </p>
3962
     *
3963
     * @return $this
3964
     *               <p>(Immutable) Return this Arrayy object.</p>
3965
     *
3966
     * @psalm-return static<TKey,T>
3967
     * @psalm-mutation-free
3968
     */
3969 4
    public function krsortImmutable(int $sort_flags = 0): self
3970
    {
3971 4
        $that = clone $this;
3972
3973
        /**
3974
         * @psalm-suppress ImpureMethodCall - object is already cloned
3975
         */
3976 4
        $that->krsort($sort_flags);
3977
3978 4
        return $that;
3979
    }
3980
3981
    /**
3982
     * Get the last value from the current array.
3983
     *
3984
     * EXAMPLE: <code>
3985
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->last(); // 'lall'
3986
     * </code>
3987
     *
3988
     * @return mixed|null
3989
     *                    <p>Return null if there wasn't a element.</p>
3990
     * @psalm-mutation-free
3991
     */
3992 17
    public function last()
3993
    {
3994 17
        $key_last = $this->lastKey();
3995 17
        if ($key_last === null) {
3996 2
            return null;
3997
        }
3998
3999 15
        return $this->get($key_last);
4000
    }
4001
4002
    /**
4003
     * Get the last key from the current array.
4004
     *
4005
     * @return mixed|null
4006
     *                    <p>Return null if there wasn't a element.</p>
4007
     * @psalm-mutation-free
4008
     */
4009 21
    public function lastKey()
4010
    {
4011 21
        $this->generatorToArray();
4012
4013 21
        return \array_key_last($this->array);
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 static
4026
     *                <p>(Immutable)</p>
4027
     *
4028
     * @psalm-return static<TKey,T>
4029
     * @psalm-mutation-free
4030
     */
4031 13
    public function lastsImmutable(int $number = null): self
4032
    {
4033 13
        if ($this->isEmpty()) {
4034 1
            return static::create(
4035 1
                [],
4036 1
                $this->iteratorClass,
4037 1
                false
4038
            );
4039
        }
4040
4041 12
        if ($number === null) {
4042 8
            $poppedValue = $this->last();
4043
4044 8
            if ($poppedValue === null) {
4045 1
                $poppedValue = [$poppedValue];
4046
            } else {
4047 7
                $poppedValue = (array) $poppedValue;
4048
            }
4049
4050 8
            $arrayy = static::create(
4051 8
                $poppedValue,
4052 8
                $this->iteratorClass,
4053 8
                false
4054
            );
4055
        } else {
4056 4
            $arrayy = $this->rest(-$number);
4057
        }
4058
4059 12
        return $arrayy;
4060
    }
4061
4062
    /**
4063
     * Get the last value(s) from the current array.
4064
     *
4065
     * EXAMPLE: <code>
4066
     * a([2 => 'foo', 3 => 'bar', 4 => 'lall'])->lasts(2); // Arrayy[0 => 'bar', 1 => 'lall']
4067
     * </code>
4068
     *
4069
     * @param int|null $number
4070
     *
4071
     * @return $this
4072
     *               <p>(Mutable)</p>
4073
     *
4074
     * @psalm-return static<TKey,T>
4075
     */
4076 13
    public function lastsMutable(int $number = null): self
4077
    {
4078 13
        if ($this->isEmpty()) {
4079 1
            return $this;
4080
        }
4081
4082 12
        $this->array = $this->lastsImmutable($number)->toArray();
4083 12
        $this->generator = null;
4084
4085 12
        return $this;
4086
    }
4087
4088
    /**
4089
     * Count the values from the current array.
4090
     *
4091
     * alias: for "Arrayy->count()"
4092
     *
4093
     * @param int $mode
4094
     *
4095
     * @return int
4096
     *
4097
     * @see Arrayy::count()
4098
     */
4099 20
    public function length(int $mode = \COUNT_NORMAL): int
4100
    {
4101 20
        return $this->count($mode);
4102
    }
4103
4104
    /**
4105
     * Apply the given function to the every element of the array,
4106
     * collecting the results.
4107
     *
4108
     * EXAMPLE: <code>
4109
     * a(['foo', 'Foo'])->map('mb_strtoupper'); // Arrayy['FOO', 'FOO']
4110
     * </code>
4111
     *
4112
     * @param callable $callable
4113
     * @param bool     $useKeyAsSecondParameter
4114
     * @param mixed    ...$arguments
4115
     *
4116
     * @return static
4117
     *                <p>(Immutable) Arrayy object with modified elements.</p>
4118
     *
4119
     * @psalm-param  callable(T,TKey=,mixed=):mixed $callable
4120
     * @psalm-return static<TKey,T>
4121
     * @psalm-mutation-free
4122
     */
4123 6
    public function map(
4124
        callable $callable,
4125
        bool $useKeyAsSecondParameter = false,
4126
        ...$arguments
4127
    ) {
4128
        /**
4129
         * @psalm-suppress ImpureFunctionCall - func_num_args is only used to detect the number of args
4130
         */
4131 6
        $useArguments = \func_num_args() > 2;
4132
4133 6
        return static::create(
4134
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
4135 6
                foreach ($this->getGenerator() as $key => $value) {
4136 5
                    if ($useArguments) {
4137 3
                        if ($useKeyAsSecondParameter) {
4138
                            yield $key => $callable($value, $key, ...$arguments);
4139
                        } else {
4140 3
                            yield $key => $callable($value, ...$arguments);
4141
                        }
4142
                    } else {
4143
                        /** @noinspection NestedPositiveIfStatementsInspection */
4144 5
                        if ($useKeyAsSecondParameter) {
4145
                            yield $key => $callable($value, $key);
4146
                        } else {
4147 5
                            yield $key => $callable($value);
4148
                        }
4149
                    }
4150
                }
4151 6
            },
4152 6
            $this->iteratorClass,
4153 6
            false
4154
        );
4155
    }
4156
4157
    /**
4158
     * Check if all items in current array match a truth test.
4159
     *
4160
     * EXAMPLE: <code>
4161
     * $closure = function ($value, $key) {
4162
     *     return ($value % 2 === 0);
4163
     * };
4164
     * a([2, 4, 8])->matches($closure); // true
4165
     * </code>
4166
     *
4167
     * @param \Closure $closure
4168
     *
4169
     * @return bool
4170
     */
4171 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...
4172
    {
4173 15
        if ($this->count() === 0) {
4174 2
            return false;
4175
        }
4176
4177 13
        foreach ($this->getGenerator() as $key => $value) {
4178 13
            $value = $closure($value, $key);
4179
4180 13
            if ($value === false) {
4181 13
                return false;
4182
            }
4183
        }
4184
4185 7
        return true;
4186
    }
4187
4188
    /**
4189
     * Check if any item in the current array matches a truth test.
4190
     *
4191
     * EXAMPLE: <code>
4192
     * $closure = function ($value, $key) {
4193
     *     return ($value % 2 === 0);
4194
     * };
4195
     * a([1, 4, 7])->matches($closure); // true
4196
     * </code>
4197
     *
4198
     * @param \Closure $closure
4199
     *
4200
     * @return bool
4201
     */
4202 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...
4203
    {
4204 14
        if ($this->count() === 0) {
4205 2
            return false;
4206
        }
4207
4208 12
        foreach ($this->getGenerator() as $key => $value) {
4209 12
            $value = $closure($value, $key);
4210
4211 12
            if ($value === true) {
4212 12
                return true;
4213
            }
4214
        }
4215
4216 4
        return false;
4217
    }
4218
4219
    /**
4220
     * Get the max value from an array.
4221
     *
4222
     * EXAMPLE: <code>
4223
     * a([-9, -8, -7, 1.32])->max(); // 1.32
4224
     * </code>
4225
     *
4226
     * @return false|mixed
4227
     *                     <p>Will return false if there are no values.</p>
4228
     */
4229 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...
4230
    {
4231 10
        if ($this->count() === 0) {
4232 1
            return false;
4233
        }
4234
4235 9
        $max = false;
4236
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4237 9
        foreach ($this->getGeneratorByReference() as &$value) {
4238
            if (
4239 9
                $max === false
4240
                ||
4241 9
                $value > $max
4242
            ) {
4243 9
                $max = $value;
4244
            }
4245
        }
4246
4247 9
        return $max;
4248
    }
4249
4250
    /**
4251
     * Merge the new $array into the current array.
4252
     *
4253
     * - keep key,value from the current array, also if the index is in the new $array
4254
     *
4255
     * EXAMPLE: <code>
4256
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4257
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4258
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[1 => 'one', 'foo' => 'bar2', 3 => 'three']
4259
     * // ---
4260
     * $array1 = [0 => 'one', 1 => 'foo'];
4261
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4262
     * a($array1)->mergeAppendKeepIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2']
4263
     * </code>
4264
     *
4265
     * @param array $array
4266
     * @param bool  $recursive
4267
     *
4268
     * @return static
4269
     *                <p>(Immutable)</p>
4270
     *
4271
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4272
     * @psalm-return static<int|TKey,T>
4273
     * @psalm-mutation-free
4274
     */
4275 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...
4276
    {
4277 33
        if ($recursive === true) {
4278 9
            $array = $this->getArrayRecursiveHelperArrayy($array);
4279 9
            $result = \array_replace_recursive($this->toArray(), $array);
4280
        } else {
4281 24
            $result = \array_replace($this->toArray(), $array);
4282
        }
4283
4284 33
        return static::create(
4285 33
            $result,
4286 33
            $this->iteratorClass,
4287 33
            false
4288
        );
4289
    }
4290
4291
    /**
4292
     * Merge the new $array into the current array.
4293
     *
4294
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
4295
     * - create new indexes
4296
     *
4297
     * EXAMPLE: <code>
4298
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4299
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4300
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 'foo' => 'bar2', 1 => 'three']
4301
     * // ---
4302
     * $array1 = [0 => 'one', 1 => 'foo'];
4303
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4304
     * a($array1)->mergeAppendNewIndex($array2); // Arrayy[0 => 'one', 1 => 'foo', 2 => 'foo', 3 => 'bar2']
4305
     * </code>
4306
     *
4307
     * @param array $array
4308
     * @param bool  $recursive
4309
     *
4310
     * @return static
4311
     *                <p>(Immutable)</p>
4312
     *
4313
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4314
     * @psalm-return static<TKey,T>
4315
     * @psalm-mutation-free
4316
     */
4317 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...
4318
    {
4319 20
        if ($recursive === true) {
4320 5
            $array = $this->getArrayRecursiveHelperArrayy($array);
4321 5
            $result = \array_merge_recursive($this->toArray(), $array);
4322
        } else {
4323 15
            $result = \array_merge($this->toArray(), $array);
4324
        }
4325
4326 20
        return static::create(
4327 20
            $result,
4328 20
            $this->iteratorClass,
4329 20
            false
4330
        );
4331
    }
4332
4333
    /**
4334
     * Merge the the current array into the $array.
4335
     *
4336
     * - use key,value from the new $array, also if the index is in the current array
4337
     *
4338
     * EXAMPLE: <code>
4339
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4340
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4341
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy['foo' => 'bar1', 3 => 'three', 1 => 'one']
4342
     * // ---
4343
     * $array1 = [0 => 'one', 1 => 'foo'];
4344
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4345
     * a($array1)->mergePrependKeepIndex($array2); // Arrayy[0 => 'one', 1 => 'foo']
4346
     * </code>
4347
     *
4348
     * @param array $array
4349
     * @param bool  $recursive
4350
     *
4351
     * @return static
4352
     *                <p>(Immutable)</p>
4353
     *
4354
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4355
     * @psalm-return static<TKey,T>
4356
     * @psalm-mutation-free
4357
     */
4358 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...
4359
    {
4360 17
        if ($recursive === true) {
4361 4
            $array = $this->getArrayRecursiveHelperArrayy($array);
4362 4
            $result = \array_replace_recursive($array, $this->toArray());
4363
        } else {
4364 13
            $result = \array_replace($array, $this->toArray());
4365
        }
4366
4367 17
        return static::create(
4368 17
            $result,
4369 17
            $this->iteratorClass,
4370 17
            false
4371
        );
4372
    }
4373
4374
    /**
4375
     * Merge the current array into the new $array.
4376
     *
4377
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
4378
     * - create new indexes
4379
     *
4380
     * EXAMPLE: <code>
4381
     * $array1 = [1 => 'one', 'foo' => 'bar1'];
4382
     * $array2 = ['foo' => 'bar2', 3 => 'three'];
4383
     * a($array1)->mergePrependNewIndex($array2); // Arrayy['foo' => 'bar1', 0 => 'three', 1 => 'one']
4384
     * // ---
4385
     * $array1 = [0 => 'one', 1 => 'foo'];
4386
     * $array2 = [0 => 'foo', 1 => 'bar2'];
4387
     * a($array1)->mergePrependNewIndex($array2); // Arrayy[0 => 'foo', 1 => 'bar2', 2 => 'one', 3 => 'foo']
4388
     * </code>
4389
     *
4390
     * @param array $array
4391
     * @param bool  $recursive
4392
     *
4393
     * @return static
4394
     *                <p>(Immutable)</p>
4395
     *
4396
     * @psalm-param  array<mixed,mixed>|array<TKey,T> $array
4397
     * @psalm-return static<TKey,T>
4398
     * @psalm-mutation-free
4399
     */
4400 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...
4401
    {
4402 21
        if ($recursive === true) {
4403 7
            $array = $this->getArrayRecursiveHelperArrayy($array);
4404 7
            $result = \array_merge_recursive($array, $this->toArray());
4405
        } else {
4406 14
            $result = \array_merge($array, $this->toArray());
4407
        }
4408
4409 21
        return static::create(
4410 21
            $result,
4411 21
            $this->iteratorClass,
4412 21
            false
4413
        );
4414
    }
4415
4416
    /**
4417
     * @return ArrayyMeta|mixed|static
4418
     */
4419 18
    public static function meta()
4420
    {
4421 18
        return (new ArrayyMeta())->getMetaObject(static::class);
4422
    }
4423
4424
    /**
4425
     * Get the min value from an array.
4426
     *
4427
     * EXAMPLE: <code>
4428
     * a([-9, -8, -7, 1.32])->min(); // -9
4429
     * </code>
4430
     *
4431
     * @return false|mixed
4432
     *                     <p>Will return false if there are no values.</p>
4433
     */
4434 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...
4435
    {
4436 10
        if ($this->count() === 0) {
4437 1
            return false;
4438
        }
4439
4440 9
        $min = false;
4441
        /** @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection */
4442 9
        foreach ($this->getGeneratorByReference() as &$value) {
4443
            if (
4444 9
                $min === false
4445
                ||
4446 9
                $value < $min
4447
            ) {
4448 9
                $min = $value;
4449
            }
4450
        }
4451
4452 9
        return $min;
4453
    }
4454
4455
    /**
4456
     * Get the most used value from the array.
4457
     *
4458
     * @return mixed|null
4459
     *                    <p>(Immutable) Return null if there wasn't a element.</p>
4460
     * @psalm-mutation-free
4461
     */
4462 3
    public function mostUsedValue()
4463
    {
4464 3
        return $this->countValues()->arsortImmutable()->firstKey();
4465
    }
4466
4467
    /**
4468
     * Get the most used value from the array.
4469
     *
4470
     * @param int|null $number <p>How many values you will take?</p>
4471
     *
4472
     * @return static
4473
     *                <p>(Immutable)</p>
4474
     *
4475
     * @psalm-return static<TKey,T>
4476
     * @psalm-mutation-free
4477
     */
4478 3
    public function mostUsedValues(int $number = null): self
4479
    {
4480 3
        return $this->countValues()->arsortImmutable()->firstsKeys($number);
4481
    }
4482
4483
    /**
4484
     * Move an array element to a new index.
4485
     *
4486
     * EXAMPLE: <code>
4487
     * $arr2 = new A(['A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e']);
4488
     * $newArr2 = $arr2->moveElement('D', 1); // Arrayy['A' => 'a', 'D' => 'd', 'B' => 'b', 'C' => 'c', 'E' => 'e']
4489
     * </code>
4490
     *
4491
     * @param int|string $from
4492
     * @param int        $to
4493
     *
4494
     * @return static
4495
     *                <p>(Immutable)</p>
4496
     *
4497
     * @psalm-return static<TKey,T>
4498
     * @psalm-mutation-free
4499
     */
4500 1
    public function moveElement($from, $to): self
4501
    {
4502 1
        $array = $this->toArray();
4503
4504 1
        if ((int) $from === $from) {
4505 1
            $tmp = \array_splice($array, $from, 1);
4506 1
            \array_splice($array, (int) $to, 0, $tmp);
4507 1
            $output = $array;
4508 1
        } elseif ((string) $from === $from) {
4509 1
            $indexToMove = \array_search($from, \array_keys($array), true);
4510 1
            $itemToMove = $array[$from];
4511 1
            if ($indexToMove !== false) {
4512 1
                \array_splice($array, $indexToMove, 1);
4513
            }
4514 1
            $i = 0;
4515 1
            $output = [];
4516 1
            foreach ($array as $key => $item) {
4517 1
                if ($i === $to) {
4518 1
                    $output[$from] = $itemToMove;
4519
                }
4520 1
                $output[$key] = $item;
4521 1
                ++$i;
4522
            }
4523
        } else {
4524
            $output = [];
4525
        }
4526
4527 1
        return static::create(
4528 1
            $output,
4529 1
            $this->iteratorClass,
4530 1
            false
4531
        );
4532
    }
4533
4534
    /**
4535
     * Move an array element to the first place.
4536
     *
4537
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4538
     *       loss the keys of an indexed array.
4539
     *
4540
     * @param int|string $key
4541
     *
4542
     * @return static
4543
     *                <p>(Immutable)</p>
4544
     *
4545
     * @psalm-return static<TKey,T>
4546
     * @psalm-mutation-free
4547
     */
4548 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...
4549
    {
4550 1
        $array = $this->toArray();
4551
4552 1
        if ($this->offsetExists($key)) {
4553 1
            $tmpValue = $this->get($key);
4554 1
            unset($array[$key]);
4555 1
            $array = [$key => $tmpValue] + $array;
4556
        }
4557
4558 1
        return static::create(
4559 1
            $array,
4560 1
            $this->iteratorClass,
4561 1
            false
4562
        );
4563
    }
4564
4565
    /**
4566
     * Move an array element to the last place.
4567
     *
4568
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
4569
     *       loss the keys of an indexed array.
4570
     *
4571
     * @param int|string $key
4572
     *
4573
     * @return static
4574
     *                <p>(Immutable)</p>
4575
     *
4576
     * @psalm-return static<TKey,T>
4577
     * @psalm-mutation-free
4578
     */
4579 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...
4580
    {
4581 1
        $array = $this->toArray();
4582
4583 1
        if ($this->offsetExists($key)) {
4584 1
            $tmpValue = $this->get($key);
4585 1
            unset($array[$key]);
4586 1
            $array += [$key => $tmpValue];
4587
        }
4588
4589 1
        return static::create(
4590 1
            $array,
4591 1
            $this->iteratorClass,
4592 1
            false
4593
        );
4594
    }
4595
4596
    /**
4597
     * Moves the internal iterator position to the next element and returns this element.
4598
     *
4599
     * @return false|mixed
4600
     *                     <p>(Mutable) Will return false if there are no values.</p>
4601
     */
4602
    public function next()
4603
    {
4604
        return \next($this->array);
4605
    }
4606
4607
    /**
4608
     * Get the next nth keys and values from the array.
4609
     *
4610
     * @param int $step
4611
     * @param int $offset
4612
     *
4613
     * @return static
4614
     *                <p>(Immutable)</p>
4615
     *
4616
     * @psalm-return static<TKey,T>
4617
     * @psalm-mutation-free
4618
     */
4619 1
    public function nth(int $step, int $offset = 0): self
4620
    {
4621
        $arrayFunction = function () use ($step, $offset): \Generator {
4622 1
            $position = 0;
4623 1
            foreach ($this->getGenerator() as $key => $value) {
4624 1
                if ($position++ % $step !== $offset) {
4625 1
                    continue;
4626
                }
4627
4628 1
                yield $key => $value;
4629
            }
4630 1
        };
4631
4632 1
        return static::create(
4633 1
            $arrayFunction,
4634 1
            $this->iteratorClass,
4635 1
            false
4636
        );
4637
    }
4638
4639
    /**
4640
     * Get a subset of the items from the given array.
4641
     *
4642
     * @param int[]|string[] $keys
4643
     *
4644
     * @return static
4645
     *                <p>(Immutable)</p>
4646
     *
4647
     * @psalm-param array-key[] $keys
4648
     * @psalm-return static<TKey,T>
4649
     * @psalm-mutation-free
4650
     */
4651 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...
4652
    {
4653 1
        $keys = \array_flip($keys);
4654
4655
        $generator = function () use ($keys): \Generator {
4656 1
            foreach ($this->getGenerator() as $key => $value) {
4657 1
                if (isset($keys[$key])) {
4658 1
                    yield $key => $value;
4659
                }
4660
            }
4661 1
        };
4662
4663 1
        return static::create(
4664 1
            $generator,
4665 1
            $this->iteratorClass,
4666 1
            false
4667
        );
4668
    }
4669
4670
    /**
4671
     * Pad array to the specified size with a given value.
4672
     *
4673
     * @param int   $size  <p>Size of the result array.</p>
4674
     * @param mixed $value <p>Empty value by default.</p>
4675
     *
4676
     * @return static
4677
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
4678
     *
4679
     * @psalm-return static<TKey,T>
4680
     * @psalm-mutation-free
4681
     */
4682 5
    public function pad(int $size, $value): self
4683
    {
4684 5
        return static::create(
4685 5
            \array_pad($this->toArray(), $size, $value),
4686 5
            $this->iteratorClass,
4687 5
            false
4688
        );
4689
    }
4690
4691
    /**
4692
     * Partitions this array in two array according to a predicate.
4693
     * Keys are preserved in the resulting array.
4694
     *
4695
     * @param \Closure $closure
4696
     *                          <p>The predicate on which to partition.</p>
4697
     *
4698
     * @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...
4699
     *                    <p>An array with two elements. The first element contains the array
4700
     *                    of elements where the predicate returned TRUE, the second element
4701
     *                    contains the array of elements where the predicate returned FALSE.</p>
4702
     *
4703
     * @psalm-return array<int, static<TKey,T>>
4704
     */
4705 1
    public function partition(\Closure $closure): array
4706
    {
4707
        // init
4708 1
        $matches = [];
4709 1
        $noMatches = [];
4710
4711 1
        foreach ($this->getGenerator() as $key => $value) {
4712 1
            if ($closure($value, $key)) {
4713 1
                $matches[$key] = $value;
4714
            } else {
4715 1
                $noMatches[$key] = $value;
4716
            }
4717
        }
4718
4719 1
        return [self::create($matches), self::create($noMatches)];
4720
    }
4721
4722
    /**
4723
     * Pop a specified value off the end of the current array.
4724
     *
4725
     * @return mixed|null
4726
     *                    <p>(Mutable) The popped element from the current array or null if the array is e.g. empty.</p>
4727
     */
4728 5
    public function pop()
4729
    {
4730 5
        $this->generatorToArray();
4731
4732 5
        return \array_pop($this->array);
4733
    }
4734
4735
    /**
4736
     * Prepend a (key) + value to the current array.
4737
     *
4738
     * EXAMPLE: <code>
4739
     * a(['fòô' => 'bàř'])->prepend('foo'); // Arrayy[0 => 'foo', 'fòô' => 'bàř']
4740
     * </code>
4741
     *
4742
     * @param mixed $value
4743
     * @param mixed $key
4744
     *
4745
     * @return $this
4746
     *               <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
4747
     *
4748
     * @psalm-return static<TKey,T>
4749
     */
4750 11
    public function prepend($value, $key = null)
4751
    {
4752 11
        $this->generatorToArray();
4753
4754 11
        if ($this->properties !== []) {
4755 3
            $this->checkType($key, $value);
4756
        }
4757
4758 9
        if ($key === null) {
4759 8
            \array_unshift($this->array, $value);
4760
        } else {
4761 2
            $this->array = [$key => $value] + $this->array;
4762
        }
4763
4764 9
        return $this;
4765
    }
4766
4767
    /**
4768
     * Prepend a (key) + value to the current array.
4769
     *
4770
     * EXAMPLE: <code>
4771
     * a(['fòô' => 'bàř'])->prependImmutable('foo')->getArray(); // [0 => 'foo', 'fòô' => 'bàř']
4772
     * </code>
4773
     *
4774
     * @param mixed $value
4775
     * @param mixed $key
4776
     *
4777
     * @return $this
4778
     *               <p>(Immutable) Return this Arrayy object, with the prepended value.</p>
4779
     *
4780
     * @psalm-return static<TKey,T>
4781
     * @psalm-mutation-free
4782
     */
4783 1 View Code Duplication
    public function prependImmutable($value, $key = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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