Completed
Push — master ( f69cb7...f8d0bc )
by Lars
01:30
created

Arrayy::indexOf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
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 51
    public function __clone()
126
    {
127 51
        if (!\is_array($this->properties)) {
128
            $this->properties = clone $this->properties;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->properties of type object is incompatible with the declared type array of property $properties.

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

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

Loading history...
129
        }
130
131 51
        if ($this->generator !== null) {
132
            $this->generator = clone $this->generator;
133
        }
134 51
    }
135
136
    /**
137
     * Call object as function.
138
     *
139
     * @param mixed $key
140
     *
141
     * @return mixed
142
     */
143 1
    public function __invoke($key = null)
144
    {
145 1
        if ($key !== null) {
146 1
            $this->generatorToArray();
147
148 1
            return $this->array[$key] ?? false;
149
        }
150
151
        return $this->toArray();
152
    }
153
154
    /**
155
     * Whether or not an element exists by key.
156
     *
157
     * @param mixed $key
158
     *
159
     * @return bool
160
     *              <p>True is the key/index exists, otherwise false.</p>
161
     */
162
    public function __isset($key): bool
163
    {
164
        return $this->offsetExists($key);
165
    }
166
167
    /**
168
     * Assigns a value to the specified element.
169
     *
170
     * @param mixed $key
171
     * @param mixed $value
172
     *
173
     * @return void
174
     */
175 3
    public function __set($key, $value)
176
    {
177 3
        $this->internalSet($key, $value);
178 3
    }
179
180
    /**
181
     * magic to string
182
     *
183
     * @return string
184
     */
185 15
    public function __toString(): string
186
    {
187 15
        return $this->toString();
188
    }
189
190
    /**
191
     * Unset element by key.
192
     *
193
     * @param mixed $key
194
     */
195
    public function __unset($key)
196
    {
197
        $this->internalRemove($key);
198
    }
199
200
    /**
201
     * Get a value by key.
202
     *
203
     * @param mixed $key
204
     *
205
     * @return mixed
206
     *               <p>Get a Value from the current array.</p>
207
     */
208 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
460 1
        return $this->array;
461
    }
462
463
    /**
464
     * Creates a copy of the ArrayyObject.
465
     *
466
     * @return array
467
     *
468
     * @psalm-return array<mixed,mixed>|array<TKey,T>
469
     */
470 6
    public function getArrayCopy(): array
471
    {
472 6
        $this->generatorToArray();
473
474 6
        return $this->array;
475
    }
476
477
    /**
478
     * Returns a new iterator, thus implementing the \Iterator interface.
479
     *
480
     * EXAMPLE: <code>
481
     * a(['foo', 'bar'])->getIterator(); // ArrayyIterator['foo', 'bar']
482
     * </code>
483
     *
484
     * @return \Iterator<mixed, mixed>
0 ignored issues
show
Documentation introduced by
The doc-type \Iterator<mixed, could not be parsed: Expected "|" or "end of type", but got "<" at position 9. (view supported doc-types)

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

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