Completed
Push — master ( 6f26bb...f7aeb2 )
by Lars
02:02
created

Arrayy::checkType()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.049

Importance

Changes 0
Metric Value
cc 7
nc 4
nop 2
dl 0
loc 18
ccs 9
cts 10
cp 0.9
crap 7.049
rs 8.8333
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\TypeCheck\TypeCheckArray;
11
use Arrayy\TypeCheck\TypeCheckInterface;
12
use Arrayy\TypeCheck\TypeCheckPhpDoc;
13
14
/**
15
 * Methods to manage arrays.
16
 *
17
 * For the full copyright and license information, please view the LICENSE
18
 * file that was distributed with this source code.
19
 *
20
 * @template TKey of array-key
21
 * @template T
22
 * @template-extends \ArrayObject<TKey,T>
23
 * @template-implements \IteratorAggregate<TKey,T>
24
 * @template-implements \ArrayAccess<TKey|null,T>
25
 */
26
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
27
{
28
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
29
30
    /**
31
     * @var array
32
     *
33
     * @psalm-var array<TKey,T>
34
     */
35
    protected $array = [];
36
37
    /**
38
     * @var \Arrayy\ArrayyRewindableGenerator|null
39
     *
40
     * @psalm-var \Arrayy\ArrayyRewindableGenerator<TKey,T>|null
41
     */
42
    protected $generator;
43
44
    /**
45
     * @var string
46
     *
47
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
48
     */
49
    protected $iteratorClass = ArrayyIterator::class;
50
51
    /**
52
     * @var string
53
     */
54
    protected $pathSeparator = '.';
55
56
    /**
57
     * @var bool
58
     */
59
    protected $checkPropertyTypes = false;
60
61
    /**
62
     * @var bool
63
     */
64
    protected $checkForMissingPropertiesInConstructor = false;
65
66
    /**
67
     * @var bool
68
     */
69
    protected $checkPropertiesMismatchInConstructor = false;
70
71
    /**
72
     * @var bool
73
     */
74
    protected $checkPropertiesMismatch = true;
75
76
    /**
77
     * @var array<TypeCheckInterface>|\Arrayy\Type\TypeInterface|TypeCheckArray<TypeCheckInterface>
78
     */
79
    protected $properties = [];
80
81
    /**
82
     * Initializes
83
     *
84
     * @param mixed  $data                         <p>
85
     *                                             Should be an array or a generator, otherwise it will try
86
     *                                             to convert it into an array.
87
     *                                             </p>
88
     * @param string $iteratorClass                optional <p>
89
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
90
     *                                             need this option.
91
     *                                             </p>
92
     * @param bool   $checkPropertiesInConstructor optional <p>
93
     *                                             You need to extend the "Arrayy"-class and you need to set
94
     *                                             the $checkPropertiesMismatchInConstructor class property
95
     *                                             to
96
     *                                             true, otherwise this option didn't not work anyway.
97
     *                                             </p>
98
     *
99
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
100
     */
101 1066
    public function __construct(
102
        $data = [],
103
        string $iteratorClass = ArrayyIterator::class,
104
        bool $checkPropertiesInConstructor = true
105
    ) {
106 1066
        $data = $this->fallbackForArray($data);
107
108
        // used only for serialize + unserialize, all other methods are overwritten
109 1064
        parent::__construct([], 0, $iteratorClass);
110
111 1064
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
112
113 1057
        $this->setIteratorClass($iteratorClass);
114 1057
    }
115
116
    /**
117
     * Call object as function.
118
     *
119
     * @param mixed $key
120
     *
121
     * @return mixed
122
     */
123 1
    public function __invoke($key = null)
124
    {
125 1
        if ($key !== null) {
126 1
            $this->generatorToArray();
127
128 1
            return $this->array[$key] ?? false;
129
        }
130
131
        return $this->getArray();
132
    }
133
134
    /**
135
     * Whether or not an element exists by key.
136
     *
137
     * @param mixed $key
138
     *
139
     * @return bool
140
     *              <p>True is the key/index exists, otherwise false.</p>
141
     */
142
    public function __isset($key): bool
143
    {
144
        return $this->offsetExists($key);
145
    }
146
147
    /**
148
     * Assigns a value to the specified element.
149
     *
150
     * @param mixed $key
151
     * @param mixed $value
152
     *
153
     * @return void
154
     */
155 2
    public function __set($key, $value)
156
    {
157 2
        $this->internalSet($key, $value);
158 2
    }
159
160
    /**
161
     * magic to string
162
     *
163
     * @return string
164
     */
165 15
    public function __toString(): string
166
    {
167 15
        return $this->toString();
168
    }
169
170
    /**
171
     * Unset element by key.
172
     *
173
     * @param mixed $key
174
     */
175
    public function __unset($key)
176
    {
177
        $this->internalRemove($key);
178
    }
179
180
    /**
181
     * Get a value by key.
182
     *
183
     * @param mixed $key
184
     *
185
     * @return mixed
186
     *               <p>Get a Value from the current array.</p>
187
     */
188 4
    public function &__get($key)
189
    {
190 4
        $return = $this->get($key);
191
192 4
        if (\is_array($return) === true) {
193
            return static::create($return, $this->iteratorClass, false);
194
        }
195
196 4
        return $return;
197
    }
198
199
    /**
200
     * alias: for "Arrayy->append()"
201
     *
202
     * @param mixed $value
203
     *
204
     * @return static
205
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
206
     *
207
     * @see Arrayy::append()
208
     */
209 3
    public function add($value)
210
    {
211 3
        return $this->append($value);
212
    }
213
214
    /**
215
     * Append a (key) + value to the current array.
216
     *
217
     * @param mixed $value
218
     * @param mixed $key
219
     *
220
     * @return static
221
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
222
     */
223 13
    public function append($value, $key = null): self
224
    {
225 13
        $this->generatorToArray();
226
227 13
        if ($this->properties !== []) {
228 4
            $this->checkType($key, $value);
229
        }
230
231 12
        if ($key !== null) {
232
            if (
233
                isset($this->array[$key])
234
                &&
235
                \is_array($this->array[$key]) === true
236
            ) {
237
                $this->array[$key][] = $value;
238
            } else {
239
                $this->array[$key] = $value;
240
            }
241
        } else {
242 12
            $this->array[] = $value;
243
        }
244
245 12
        return $this;
246
    }
247
248
    /**
249
     * Sort the entries by value.
250
     *
251
     * @param int $sort_flags [optional] <p>
252
     *                        You may modify the behavior of the sort using the optional
253
     *                        parameter sort_flags, for details
254
     *                        see sort.
255
     *                        </p>
256
     *
257
     * @return static
258
     *                <p>(Mutable) Return this Arrayy object.</p>
259
     */
260 4
    public function asort(int $sort_flags = 0): self
261
    {
262 4
        $this->generatorToArray();
263
264 4
        \asort($this->array, $sort_flags);
265
266 4
        return $this;
267
    }
268
269
    /**
270
     * Counts all elements in an array, or something in an object.
271
     *
272
     * <p>
273
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
274
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
275
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
276
     * implemented and used in PHP.
277
     * </p>
278
     *
279
     * @see http://php.net/manual/en/function.count.php
280
     *
281
     * @param int $mode [optional] If the optional mode parameter is set to
282
     *                  COUNT_RECURSIVE (or 1), count
283
     *                  will recursively count the array. This is particularly useful for
284
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
285
     *
286
     * @return int
287
     *             <p>
288
     *             The number of elements in var, which is
289
     *             typically an array, since anything else will have one
290
     *             element.
291
     *             </p>
292
     *             <p>
293
     *             If var is not an array or an object with
294
     *             implemented Countable interface,
295
     *             1 will be returned.
296
     *             There is one exception, if var is &null;,
297
     *             0 will be returned.
298
     *             </p>
299
     *             <p>
300
     *             Caution: count may return 0 for a variable that isn't set,
301
     *             but it may also return 0 for a variable that has been initialized with an
302
     *             empty array. Use isset to test if a variable is set.
303
     *             </p>
304
     */
305 145
    public function count(int $mode = \COUNT_NORMAL): int
306
    {
307
        if (
308 145
            $this->generator
309
            &&
310 145
            $mode === \COUNT_NORMAL
311
        ) {
312 4
            return \iterator_count($this->generator);
313
        }
314
315 141
        return \count($this->getArray(), $mode);
316
    }
317
318
    /**
319
     * Exchange the array for another one.
320
     *
321
     * @param array|static $data
322
     *
323
     * @return array
324
     *
325
     * @psalm-param  array<TKey,T>|self<TKey,T> $data
326
     * @psalm-return array<TKey,T>
327
     */
328 1
    public function exchangeArray($data): array
329
    {
330 1
        $this->array = $this->fallbackForArray($data);
331
332 1
        return $this->array;
333
    }
334
335
    /**
336
     * Creates a copy of the ArrayyObject.
337
     *
338
     * @return array
339
     *
340
     * @psalm-return array<TKey,T>
341
     */
342 5
    public function getArrayCopy(): array
343
    {
344 5
        $this->generatorToArray();
345
346 5
        return $this->array;
347
    }
348
349
    /**
350
     * Returns a new iterator, thus implementing the \Iterator interface.
351
     *
352
     * @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...
353
     *                          <p>An iterator for the values in the array.</p>
354
     */
355 24
    public function getIterator(): \Iterator
356
    {
357 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
358 1
            return $this->generator;
359
        }
360
361 23
        $iterator = $this->getIteratorClass();
362
363 23
        if ($iterator === ArrayyIterator::class) {
364 23
            return new $iterator($this->getArray(), 0, static::class);
365
        }
366
367
        return new $iterator($this->getArray());
368
    }
369
370
    /**
371
     * Gets the iterator classname for the ArrayObject.
372
     *
373
     * @return string
374
     *
375
     * @psalm-return class-string
376
     */
377 23
    public function getIteratorClass(): string
378
    {
379 23
        return $this->iteratorClass;
380
    }
381
382
    /**
383
     * Sort the entries by key
384
     *
385
     * @param int $sort_flags [optional] <p>
386
     *                        You may modify the behavior of the sort using the optional
387
     *                        parameter sort_flags, for details
388
     *                        see sort.
389
     *                        </p>
390
     *
391
     * @return static
392
     *                <p>(Mutable) Return this Arrayy object.</p>
393
     */
394 4
    public function ksort(int $sort_flags = 0): self
395
    {
396 4
        $this->generatorToArray();
397
398 4
        \ksort($this->array, $sort_flags);
399
400 4
        return $this;
401
    }
402
403
    /**
404
     * Sort an array using a case insensitive "natural order" algorithm
405
     *
406
     * @return static
407
     *                <p>(Mutable) Return this Arrayy object.</p>
408
     */
409
    public function natcasesort(): self
410
    {
411
        $this->generatorToArray();
412
413
        \natcasesort($this->array);
414
415
        return $this;
416
    }
417
418
    /**
419
     * Sort entries using a "natural order" algorithm
420
     *
421
     * @return static
422
     *                <p>(Mutable) Return this Arrayy object.</p>
423
     */
424 1
    public function natsort(): self
425
    {
426 1
        $this->generatorToArray();
427
428 1
        \natsort($this->array);
429
430 1
        return $this;
431
    }
432
433
    /**
434
     * Whether or not an offset exists.
435
     *
436
     * @param bool|int|string $offset
437
     *
438
     * @return bool
439
     *
440
     * @noinspection PhpSillyAssignmentInspection
441
     */
442 130
    public function offsetExists($offset): bool
443
    {
444 130
        $this->generatorToArray();
445
446 130
        if ($this->array === []) {
447 5
            return false;
448
        }
449
450
        // php cast "bool"-index into "int"-index
451 125
        if ((bool) $offset === $offset) {
452 1
            $offset = (int) $offset;
453
        }
454
455
        /** @var int|string $offset - hint for phpstan */
456 125
        $offset = $offset;
0 ignored issues
show
Bug introduced by
Why assign $offset to itself?

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

This assignement can be removed without consequences.

Loading history...
457
458 125
        $tmpReturn = $this->keyExists($offset);
459
460
        if (
461 125
            $tmpReturn === true
462
            ||
463
            (
464 92
                $tmpReturn === false
465
                &&
466 125
                \strpos((string) $offset, $this->pathSeparator) === false
467
            )
468
        ) {
469 123
            return $tmpReturn;
470
        }
471
472 3
        $offsetExists = false;
473
474
        if (
475 3
            $this->pathSeparator
476
            &&
477 3
            (string) $offset === $offset
478
            &&
479 3
            \strpos($offset, $this->pathSeparator) !== false
480
        ) {
481 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
482 3
            if ($explodedPath !== false) {
483 3
                $lastOffset = \array_pop($explodedPath);
484 3
                if ($lastOffset !== null) {
485 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
486
487 3
                    $this->callAtPath(
488 3
                        $containerPath,
489
                        static function ($container) use ($lastOffset, &$offsetExists) {
490 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
491 3
                        }
492
                    );
493
                }
494
            }
495
        }
496
497 3
        return $offsetExists;
498
    }
499
500
    /**
501
     * Returns the value at specified offset.
502
     *
503
     * @param int|string $offset
504
     *
505
     * @return mixed
506
     *               <p>Will return null if the offset did not exists.</p>
507
     */
508 101
    public function offsetGet($offset)
509
    {
510 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
511
    }
512
513
    /**
514
     * Assigns a value to the specified offset + check the type.
515
     *
516
     * @param int|string|null $offset
517
     * @param mixed           $value
518
     *
519
     * @return void
520
     */
521 21
    public function offsetSet($offset, $value)
522
    {
523 21
        $this->generatorToArray();
524
525 21
        if ($offset === null) {
526 5
            if ($this->properties !== []) {
527 1
                $this->checkType(null, $value);
528
            }
529
530 4
            $this->array[] = $value;
531
        } else {
532 16
            $this->internalSet(
533 16
                $offset,
534 16
                $value,
535 16
                true
536
            );
537
        }
538 20
    }
539
540
    /**
541
     * Unset an offset.
542
     *
543
     * @param int|string $offset
544
     *
545
     * @return void
546
     */
547 12
    public function offsetUnset($offset)
548
    {
549 12
        $this->generatorToArray();
550
551 12
        if ($this->array === []) {
552 3
            return;
553
        }
554
555 10
        if ($this->keyExists($offset)) {
556 7
            unset($this->array[$offset]);
557
558 7
            return;
559
        }
560
561
        if (
562 5
            $this->pathSeparator
563
            &&
564 5
            (string) $offset === $offset
565
            &&
566 5
            \strpos($offset, $this->pathSeparator) !== false
567
        ) {
568 2
            $path = \explode($this->pathSeparator, (string) $offset);
569
570 2
            if ($path !== false) {
571 2
                $pathToUnset = \array_pop($path);
572
573 2
                $this->callAtPath(
574 2
                    \implode($this->pathSeparator, $path),
575
                    static function (&$offset) use ($pathToUnset) {
576 2
                        unset($offset[$pathToUnset]);
577 2
                    }
578
                );
579
            }
580
        }
581
582 5
        unset($this->array[$offset]);
583 5
    }
584
585
    /**
586
     * Serialize the current "Arrayy"-object.
587
     *
588
     * @return string
589
     */
590 2
    public function serialize(): string
591
    {
592 2
        $this->generatorToArray();
593
594 2
        if (\PHP_VERSION_ID < 70400) {
595 2
            return parent::serialize();
596
        }
597
598
        return \serialize($this);
599
    }
600
601
    /**
602
     * Sets the iterator classname for the current "Arrayy"-object.
603
     *
604
     * @param string $iteratorClass
605
     *
606
     * @throws \InvalidArgumentException
607
     *
608
     * @return void
609
     *
610
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
611
     */
612 1057
    public function setIteratorClass($iteratorClass)
613
    {
614 1057
        if (\class_exists($iteratorClass)) {
615 1057
            $this->iteratorClass = $iteratorClass;
616
617 1057
            return;
618
        }
619
620
        if (\strpos($iteratorClass, '\\') === 0) {
621
            $iteratorClass = '\\' . $iteratorClass;
622
            if (\class_exists($iteratorClass)) {
623
                $this->iteratorClass = $iteratorClass;
624
625
                return;
626
            }
627
        }
628
629
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
630
    }
631
632
    /**
633
     * Sort the entries with a user-defined comparison function and maintain key association.
634
     *
635
     * @param callable $function
636
     *
637
     * @throws \InvalidArgumentException
638
     *
639
     * @return static
640
     *                <p>(Mutable) Return this Arrayy object.</p>
641
     */
642 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...
643
    {
644
        if (!\is_callable($function)) {
645
            throw new \InvalidArgumentException('Passed function must be callable');
646
        }
647
648
        $this->generatorToArray();
649
650
        \uasort($this->array, $function);
651
652
        return $this;
653
    }
654
655
    /**
656
     * Sort the entries by keys using a user-defined comparison function.
657
     *
658
     * @param callable $function
659
     *
660
     * @throws \InvalidArgumentException
661
     *
662
     * @return static
663
     *                <p>(Mutable) Return this Arrayy object.</p>
664
     */
665 5
    public function uksort($function): self
666
    {
667 5
        return $this->customSortKeys($function);
668
    }
669
670
    /**
671
     * Unserialize an string and return the instance of the "Arrayy"-class.
672
     *
673
     * @param string $string
674
     *
675
     * @return static
676
     */
677 2
    public function unserialize($string): self
678
    {
679 2
        if (\PHP_VERSION_ID < 70400) {
680 2
            parent::unserialize($string);
681
682 2
            return $this;
683
        }
684
685
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
686
    }
687
688
    /**
689
     * Append a (key) + values to the current array.
690
     *
691
     * @param array $values
692
     * @param mixed $key
693
     *
694
     * @return static
695
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
696
     *
697
     * @psalm-param array<mixed,T> $values
698
     * @psalm-param TKey|null $key
699
     */
700 1
    public function appendArrayValues(array $values, $key = null)
701
    {
702 1
        $this->generatorToArray();
703
704 1
        if ($key !== null) {
705
            if (
706 1
                isset($this->array[$key])
707
                &&
708 1
                \is_array($this->array[$key]) === true
709
            ) {
710 1
                foreach ($values as $value) {
711 1
                    $this->array[$key][] = $value;
712
                }
713
            } else {
714 1
                foreach ($values as $value) {
715
                    $this->array[$key] = $value;
716
                }
717
            }
718
        } else {
719
            foreach ($values as $value) {
720
                $this->array[] = $value;
721
            }
722
        }
723
724 1
        return $this;
725
    }
726
727
    /**
728
     * Add a suffix to each key.
729
     *
730
     * @param mixed $prefix
731
     *
732
     * @return static
733
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
734
     */
735 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...
736
    {
737
        // init
738 10
        $result = [];
739
740 10
        foreach ($this->getGenerator() as $key => $item) {
741 9
            if ($item instanceof self) {
742
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
743 9
            } elseif (\is_array($item) === true) {
744
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
745
                    ->appendToEachKey($prefix)
746
                    ->toArray();
747
            } else {
748 9
                $result[$prefix . $key] = $item;
749
            }
750
        }
751
752 10
        return self::create($result, $this->iteratorClass, false);
753
    }
754
755
    /**
756
     * Add a prefix to each value.
757
     *
758
     * @param mixed $prefix
759
     *
760
     * @return static
761
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
762
     */
763 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...
764
    {
765
        // init
766 10
        $result = [];
767
768 10
        foreach ($this->getGenerator() as $key => $item) {
769 9
            if ($item instanceof self) {
770
                $result[$key] = $item->appendToEachValue($prefix);
771 9
            } elseif (\is_array($item) === true) {
772
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
773 9
            } elseif (\is_object($item) === true) {
774 1
                $result[$key] = $item;
775
            } else {
776 8
                $result[$key] = $prefix . $item;
777
            }
778
        }
779
780 10
        return self::create($result, $this->iteratorClass, false);
781
    }
782
783
    /**
784
     * Sort an array in reverse order and maintain index association.
785
     *
786
     * @return static
787
     *                <p>(Mutable) Return this Arrayy object.</p>
788
     */
789 10
    public function arsort(): self
790
    {
791 10
        $this->generatorToArray();
792
793 10
        \arsort($this->array);
794
795 10
        return $this;
796
    }
797
798
    /**
799
     * Iterate over the current array and execute a callback for each loop.
800
     *
801
     * @param \Closure $closure
802
     *
803
     * @return static
804
     *                <p>(Immutable)</p>
805
     */
806 2
    public function at(\Closure $closure): self
807
    {
808 2
        $arrayy = clone $this;
809
810 2
        foreach ($arrayy->getGenerator() as $key => $value) {
811 2
            $closure($value, $key);
812
        }
813
814 2
        return static::create(
815 2
            $arrayy->toArray(),
816 2
            $this->iteratorClass,
817 2
            false
818
        );
819
    }
820
821
    /**
822
     * Returns the average value of the current array.
823
     *
824
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
825
     *
826
     * @return float|int
827
     *                   <p>The average value.</p>
828
     */
829 10
    public function average($decimals = 0)
830
    {
831 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
832
833 10
        if (!$count) {
834 2
            return 0;
835
        }
836
837 8
        if ((int) $decimals !== $decimals) {
838 3
            $decimals = 0;
839
        }
840
841 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
842
    }
843
844
    /**
845
     * Changes all keys in an array.
846
     *
847
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
848
     *                  or <strong>CASE_LOWER</strong> (default)</p>
849
     *
850
     * @return static
851
     *                <p>(Immutable)</p>
852
     */
853 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
854
    {
855
        if (
856 1
            $case !== \CASE_LOWER
857
            &&
858 1
            $case !== \CASE_UPPER
859
        ) {
860
            $case = \CASE_LOWER;
861
        }
862
863 1
        $return = [];
864 1
        foreach ($this->getGenerator() as $key => $value) {
865 1
            if ($case === \CASE_LOWER) {
866 1
                $key = \mb_strtolower((string) $key);
867
            } else {
868 1
                $key = \mb_strtoupper((string) $key);
869
            }
870
871 1
            $return[$key] = $value;
872
        }
873
874 1
        return static::create(
875 1
            $return,
876 1
            $this->iteratorClass,
877 1
            false
878
        );
879
    }
880
881
    /**
882
     * Change the path separator of the array wrapper.
883
     *
884
     * By default, the separator is: "."
885
     *
886
     * @param string $separator <p>Separator to set.</p>
887
     *
888
     * @return static
889
     *                <p>Mutable</p>
890
     */
891 11
    public function changeSeparator($separator): self
892
    {
893 11
        $this->pathSeparator = $separator;
894
895 11
        return $this;
896
    }
897
898
    /**
899
     * Create a chunked version of the current array.
900
     *
901
     * @param int  $size         <p>Size of each chunk.</p>
902
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
903
     *
904
     * @return static
905
     *                <p>(Immutable) A new array of chunks from the original array.</p>
906
     */
907 5
    public function chunk($size, $preserveKeys = false): self
908
    {
909 5
        return static::create(
910 5
            \array_chunk($this->getArray(), $size, $preserveKeys),
911 5
            $this->iteratorClass,
912 5
            false
913
        );
914
    }
915
916
    /**
917
     * Clean all falsy values from the current array.
918
     *
919
     * @return static
920
     *                <p>(Immutable)</p>
921
     */
922 8
    public function clean(): self
923
    {
924 8
        return $this->filter(
925
            static function ($value) {
926 7
                return (bool) $value;
927 8
            }
928
        );
929
    }
930
931
    /**
932
     * WARNING!!! -> Clear the current array.
933
     *
934
     * @return static
935
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
936
     */
937 5
    public function clear(): self
938
    {
939 5
        $this->array = [];
940 5
        $this->generator = null;
941
942 5
        return $this;
943
    }
944
945
    /**
946
     * Check if an item is in the current array.
947
     *
948
     * @param float|int|string $value
949
     * @param bool             $recursive
950
     * @param bool             $strict
951
     *
952
     * @return bool
953
     */
954 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
955
    {
956 23
        if ($recursive === true) {
957 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
958
        }
959
960 14
        foreach ($this->getGenerator() as $valueFromArray) {
961 11
            if ($strict) {
962 11
                if ($value === $valueFromArray) {
963 11
                    return true;
964
                }
965
            } else {
966
                /** @noinspection NestedPositiveIfStatementsInspection */
967
                if ($value == $valueFromArray) {
968
                    return true;
969
                }
970
            }
971
        }
972
973 7
        return false;
974
    }
975
976
    /**
977
     * Check if an (case-insensitive) string is in the current array.
978
     *
979
     * @param string $value
980
     * @param bool   $recursive
981
     *
982
     * @return bool
983
     */
984 26
    public function containsCaseInsensitive($value, $recursive = false): bool
985
    {
986 26
        if ($recursive === true) {
987 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
988 22
                if (\is_array($valueTmp) === true) {
989 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
990 5
                    if ($return === true) {
991 5
                        return $return;
992
                    }
993 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
994 16
                    return true;
995
                }
996
            }
997
998 10
            return false;
999
        }
1000
1001 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
1002 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
1003 8
                return true;
1004
            }
1005
        }
1006
1007 5
        return false;
1008
    }
1009
1010
    /**
1011
     * Check if the given key/index exists in the array.
1012
     *
1013
     * @param int|string $key <p>key/index to search for</p>
1014
     *
1015
     * @return bool
1016
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1017
     */
1018 4
    public function containsKey($key): bool
1019
    {
1020 4
        return $this->offsetExists($key);
1021
    }
1022
1023
    /**
1024
     * Check if all given needles are present in the array as key/index.
1025
     *
1026
     * @param array $needles   <p>The keys you are searching for.</p>
1027
     * @param bool  $recursive
1028
     *
1029
     * @return bool
1030
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1031
     *
1032
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1033
     */
1034 2
    public function containsKeys(array $needles, $recursive = false): bool
1035
    {
1036 2
        if ($recursive === true) {
1037
            return
1038 2
                \count(
1039 2
                    \array_intersect(
1040 2
                        $needles,
1041 2
                        $this->keys(true)->getArray()
1042
                    ),
1043 2
                    \COUNT_RECURSIVE
1044
                )
1045
                ===
1046 2
                \count(
1047 2
                    $needles,
1048 2
                    \COUNT_RECURSIVE
1049
                );
1050
        }
1051
1052 1
        return \count(
1053 1
            \array_intersect($needles, $this->keys()->getArray()),
1054 1
            \COUNT_NORMAL
1055
        )
1056
               ===
1057 1
               \count(
1058 1
                   $needles,
1059 1
                   \COUNT_NORMAL
1060
               );
1061
    }
1062
1063
    /**
1064
     * Check if all given needles are present in the array as key/index.
1065
     *
1066
     * @param array $needles <p>The keys you are searching for.</p>
1067
     *
1068
     * @return bool
1069
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1070
     *
1071
     * @psalm-param array<mixed,mixed>|array<TKey> $needles
1072
     */
1073 1
    public function containsKeysRecursive(array $needles): bool
1074
    {
1075 1
        return $this->containsKeys($needles, true);
1076
    }
1077
1078
    /**
1079
     * alias: for "Arrayy->contains()"
1080
     *
1081
     * @param float|int|string $value
1082
     *
1083
     * @return bool
1084
     *
1085
     * @see Arrayy::contains()
1086
     */
1087 9
    public function containsValue($value): bool
1088
    {
1089 9
        return $this->contains($value);
1090
    }
1091
1092
    /**
1093
     * alias: for "Arrayy->contains($value, true)"
1094
     *
1095
     * @param float|int|string $value
1096
     *
1097
     * @return bool
1098
     *
1099
     * @see Arrayy::contains()
1100
     */
1101 18
    public function containsValueRecursive($value): bool
1102
    {
1103 18
        return $this->contains($value, true);
1104
    }
1105
1106
    /**
1107
     * Check if all given needles are present in the array.
1108
     *
1109
     * @param array $needles
1110
     *
1111
     * @return bool
1112
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1113
     *
1114
     * @psalm-param array<mixed>|array<T> $needles
1115
     */
1116 1
    public function containsValues(array $needles): bool
1117
    {
1118 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1119
               ===
1120 1
               \count($needles, \COUNT_NORMAL);
1121
    }
1122
1123
    /**
1124
     * Counts all the values of an array
1125
     *
1126
     * @see http://php.net/manual/en/function.array-count-values.php
1127
     *
1128
     * @return static
1129
     *                <p>
1130
     *                (Immutable)
1131
     *                An associative Arrayy-object of values from input as
1132
     *                keys and their count as value.
1133
     *                </p>
1134
     */
1135 7
    public function countValues(): self
1136
    {
1137 7
        return self::create(\array_count_values($this->getArray()), $this->iteratorClass);
1138
    }
1139
1140
    /**
1141
     * Creates an Arrayy object.
1142
     *
1143
     * @param mixed  $data
1144
     * @param string $iteratorClass
1145
     * @param bool   $checkPropertiesInConstructor
1146
     *
1147
     * @return static
1148
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1149
     *
1150
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
1151
     */
1152 670
    public static function create(
1153
        $data = [],
1154
        string $iteratorClass = ArrayyIterator::class,
1155
        bool $checkPropertiesInConstructor = true
1156
    ) {
1157 670
        return new static(
1158 670
            $data,
1159 670
            $iteratorClass,
1160 670
            $checkPropertiesInConstructor
1161
        );
1162
    }
1163
1164
    /**
1165
     * WARNING: Creates an Arrayy object by reference.
1166
     *
1167
     * @param array $array
1168
     *
1169
     * @return static
1170
     *                <p>(Mutable) Return this Arrayy object.</p>
1171
     *
1172
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
1173
     */
1174 1
    public function createByReference(array &$array = []): self
1175
    {
1176 1
        $array = $this->fallbackForArray($array);
1177
1178 1
        $this->array = &$array;
1179
1180 1
        return $this;
1181
    }
1182
1183
    /**
1184
     * Create an new instance from a callable function which will return an Generator.
1185
     *
1186
     * @param callable $generatorFunction
1187
     *
1188
     * @return static
1189
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1190
     *
1191
     * @psalm-param callable():\Generator<TKey,T> $generatorFunction
1192
     */
1193 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1194
    {
1195 5
        return self::create($generatorFunction);
1196
    }
1197
1198
    /**
1199
     * Create an new instance filled with a copy of values from a "Generator"-object.
1200
     *
1201
     * @param \Generator $generator
1202
     *
1203
     * @return static
1204
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1205
     *
1206
     * @psalm-param \Generator<TKey,T> $generator
1207
     */
1208 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1209
    {
1210 4
        return self::create(\iterator_to_array($generator, true));
1211
    }
1212
1213
    /**
1214
     * Create an new Arrayy object via JSON.
1215
     *
1216
     * @param string $json
1217
     *
1218
     * @return static
1219
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1220
     */
1221 5
    public static function createFromJson(string $json): self
1222
    {
1223 5
        return static::create(\json_decode($json, true));
1224
    }
1225
1226
    /**
1227
     * Create an new instance filled with values from an object that is iterable.
1228
     *
1229
     * @param \Traversable $object <p>iterable object</p>
1230
     *
1231
     * @return static
1232
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1233
     *
1234
     * @psalm-param \Traversable<TKey,T> $object
1235
     */
1236 4
    public static function createFromObject(\Traversable $object): self
1237
    {
1238
        // init
1239 4
        $array = self::create();
1240
1241 4
        if ($object instanceof self) {
1242 4
            $objectArray = $object->getGenerator();
1243
        } else {
1244
            $objectArray = $object;
1245
        }
1246
1247 4
        foreach ($objectArray as $key => $value) {
1248 3
            $array[$key] = $value;
1249
        }
1250
1251 4
        return $array;
1252
    }
1253
1254
    /**
1255
     * Create an new instance filled with values from an object.
1256
     *
1257
     * @param object $object
1258
     *
1259
     * @return static
1260
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1261
     */
1262 5
    public static function createFromObjectVars($object): self
1263
    {
1264 5
        return self::create(self::objectToArray($object));
1265
    }
1266
1267
    /**
1268
     * Create an new Arrayy object via string.
1269
     *
1270
     * @param string      $str       <p>The input string.</p>
1271
     * @param string|null $delimiter <p>The boundary string.</p>
1272
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1273
     *                               used.</p>
1274
     *
1275
     * @return static
1276
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1277
     */
1278 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1279
    {
1280 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...
1281 1
            \preg_match_all($regEx, $str, $array);
1282
1283 1
            if (!empty($array)) {
1284 1
                $array = $array[0];
1285
            }
1286
        } else {
1287
            /** @noinspection NestedPositiveIfStatementsInspection */
1288 9
            if ($delimiter !== null) {
1289 7
                $array = \explode($delimiter, $str);
1290
            } else {
1291 2
                $array = [$str];
1292
            }
1293
        }
1294
1295
        // trim all string in the array
1296 10
        \array_walk(
1297 10
            $array,
1298
            static function (&$val) {
1299 10
                if ((string) $val === $val) {
1300 10
                    $val = \trim($val);
1301
                }
1302 10
            }
1303
        );
1304
1305 10
        return static::create($array);
1306
    }
1307
1308
    /**
1309
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1310
     *
1311
     * @param \Traversable $traversable
1312
     *
1313
     * @return static
1314
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1315
     *
1316
     * @psalm-param \Traversable<TKey,T> $traversable
1317
     */
1318 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1319
    {
1320 1
        return self::create(\iterator_to_array($traversable, true));
1321
    }
1322
1323
    /**
1324
     * Create an new instance containing a range of elements.
1325
     *
1326
     * @param mixed $low  <p>First value of the sequence.</p>
1327
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1328
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1329
     *
1330
     * @return static
1331
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1332
     */
1333 2
    public static function createWithRange($low, $high, int $step = 1): self
1334
    {
1335 2
        return static::create(\range($low, $high, $step));
1336
    }
1337
1338
    /**
1339
     * Gets the element of the array at the current internal iterator position.
1340
     *
1341
     * @return false|mixed
1342
     */
1343
    public function current()
1344
    {
1345
        return \current($this->array);
1346
    }
1347
1348
    /**
1349
     * Custom sort by index via "uksort".
1350
     *
1351
     * @see http://php.net/manual/en/function.uksort.php
1352
     *
1353
     * @param callable $function
1354
     *
1355
     * @throws \InvalidArgumentException
1356
     *
1357
     * @return static
1358
     *                <p>(Mutable) Return this Arrayy object.</p>
1359
     */
1360 5
    public function customSortKeys(callable $function): self
1361
    {
1362 5
        $this->generatorToArray();
1363
1364 5
        \uksort($this->array, $function);
1365
1366 5
        return $this;
1367
    }
1368
1369
    /**
1370
     * Custom sort by value via "usort".
1371
     *
1372
     * @see http://php.net/manual/en/function.usort.php
1373
     *
1374
     * @param callable $function
1375
     *
1376
     * @throws \InvalidArgumentException
1377
     *
1378
     * @return static
1379
     *                <p>(Mutable) Return this Arrayy object.</p>
1380
     */
1381 6 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...
1382
    {
1383 6
        if (\is_callable($function) === false) {
1384
            throw new \InvalidArgumentException('Passed function must be callable');
1385
        }
1386
1387 6
        $this->generatorToArray();
1388
1389 6
        \usort($this->array, $function);
1390
1391 6
        return $this;
1392
    }
1393
1394
    /**
1395
     * Delete the given key or keys.
1396
     *
1397
     * @param int|int[]|string|string[] $keyOrKeys
1398
     *
1399
     * @return void
1400
     */
1401 4
    public function delete($keyOrKeys)
1402
    {
1403 4
        $keyOrKeys = (array) $keyOrKeys;
1404
1405 4
        foreach ($keyOrKeys as $key) {
1406 4
            $this->offsetUnset($key);
1407
        }
1408 4
    }
1409
1410
    /**
1411
     * Return values that are only in the current array.
1412
     *
1413
     * @param array ...$array
1414
     *
1415
     * @return static
1416
     *                <p>(Immutable)</p>
1417
     *
1418
     * @psalm-param array<mixed,mixed>|array<TKey,T> ...$array
1419
     */
1420 13
    public function diff(...$array): self
1421
    {
1422 13
        return static::create(
1423 13
            \array_diff($this->getArray(), ...$array),
1424 13
            $this->iteratorClass,
1425 13
            false
1426
        );
1427
    }
1428
1429
    /**
1430
     * Return values that are only in the current array.
1431
     *
1432
     * @param array ...$array
1433
     *
1434
     * @return static
1435
     *                <p>(Immutable)</p>
1436
     *
1437
     * @psalm-param array<mixed,mixed>|array<TKey,T> ...$array
1438
     */
1439 8
    public function diffKey(...$array): self
1440
    {
1441 8
        return static::create(
1442 8
            \array_diff_key($this->getArray(), ...$array),
1443 8
            $this->iteratorClass,
1444 8
            false
1445
        );
1446
    }
1447
1448
    /**
1449
     * Return values and Keys that are only in the current array.
1450
     *
1451
     * @param array $array
1452
     *
1453
     * @return static
1454
     *                <p>(Immutable)</p>
1455
     *
1456
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
1457
     */
1458 8
    public function diffKeyAndValue(array $array = []): self
1459
    {
1460 8
        return static::create(
1461 8
            \array_diff_assoc($this->getArray(), $array),
1462 8
            $this->iteratorClass,
1463 8
            false
1464
        );
1465
    }
1466
1467
    /**
1468
     * Return values that are only in the current multi-dimensional array.
1469
     *
1470
     * @param array      $array
1471
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1472
     *
1473
     * @return static
1474
     *                <p>(Immutable)</p>
1475
     *
1476
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
1477
     * @psalm-param null|array<TKey,T> $helperVariableForRecursion
1478
     */
1479 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1480
    {
1481
        // init
1482 1
        $result = [];
1483
1484
        if (
1485 1
            $helperVariableForRecursion !== null
1486
            &&
1487 1
            \is_array($helperVariableForRecursion) === true
1488
        ) {
1489
            $arrayForTheLoop = $helperVariableForRecursion;
1490
        } else {
1491 1
            $arrayForTheLoop = $this->getGenerator();
1492
        }
1493
1494 1
        foreach ($arrayForTheLoop as $key => $value) {
1495 1
            if ($value instanceof self) {
1496
                $value = $value->getArray();
1497
            }
1498
1499 1
            if (\array_key_exists($key, $array)) {
1500 1
                if ($value !== $array[$key]) {
1501 1
                    $result[$key] = $value;
1502
                }
1503
            } else {
1504 1
                $result[$key] = $value;
1505
            }
1506
        }
1507
1508 1
        return static::create(
1509 1
            $result,
1510 1
            $this->iteratorClass,
1511 1
            false
1512
        );
1513
    }
1514
1515
    /**
1516
     * Return values that are only in the new $array.
1517
     *
1518
     * @param array $array
1519
     *
1520
     * @return static
1521
     *                <p>(Immutable)</p>
1522
     *
1523
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
1524
     */
1525 8
    public function diffReverse(array $array = []): self
1526
    {
1527 8
        return static::create(
1528 8
            \array_diff($array, $this->getArray()),
1529 8
            $this->iteratorClass,
1530 8
            false
1531
        );
1532
    }
1533
1534
    /**
1535
     * Divide an array into two arrays. One with keys and the other with values.
1536
     *
1537
     * @return static
1538
     *                <p>(Immutable)</p>
1539
     */
1540 1
    public function divide(): self
1541
    {
1542 1
        return static::create(
1543
            [
1544 1
                $this->keys(),
1545 1
                $this->values(),
1546
            ],
1547 1
            $this->iteratorClass,
1548 1
            false
1549
        );
1550
    }
1551
1552
    /**
1553
     * Iterate over the current array and modify the array's value.
1554
     *
1555
     * @param \Closure $closure
1556
     *
1557
     * @return static
1558
     *                <p>(Immutable)</p>
1559
     */
1560 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...
1561
    {
1562
        // init
1563 5
        $array = [];
1564
1565 5
        foreach ($this->getGenerator() as $key => $value) {
1566 5
            $array[$key] = $closure($value, $key);
1567
        }
1568
1569 5
        return static::create(
1570 5
            $array,
1571 5
            $this->iteratorClass,
1572 5
            false
1573
        );
1574
    }
1575
1576
    /**
1577
     * Sets the internal iterator to the last element in the array and returns this element.
1578
     *
1579
     * @return mixed
1580
     */
1581
    public function end()
1582
    {
1583
        return \end($this->array);
1584
    }
1585
1586
    /**
1587
     * Check if a value is in the current array using a closure.
1588
     *
1589
     * @param \Closure $closure
1590
     *
1591
     * @return bool
1592
     *              <p>Returns true if the given value is found, false otherwise.</p>
1593
     */
1594 4
    public function exists(\Closure $closure): bool
1595
    {
1596
        // init
1597 4
        $isExists = false;
1598
1599 4
        foreach ($this->getGenerator() as $key => $value) {
1600 3
            if ($closure($value, $key)) {
1601 1
                $isExists = true;
1602
1603 1
                break;
1604
            }
1605
        }
1606
1607 4
        return $isExists;
1608
    }
1609
1610
    /**
1611
     * Fill the array until "$num" with "$default" values.
1612
     *
1613
     * @param int   $num
1614
     * @param mixed $default
1615
     *
1616
     * @return static
1617
     *                <p>(Immutable)</p>
1618
     */
1619 8
    public function fillWithDefaults(int $num, $default = null): self
1620
    {
1621 8
        if ($num < 0) {
1622 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1623
        }
1624
1625 7
        $this->generatorToArray();
1626
1627 7
        $tmpArray = $this->array;
1628
1629 7
        $count = \count($tmpArray);
1630
1631 7
        while ($count < $num) {
1632 4
            $tmpArray[] = $default;
1633 4
            ++$count;
1634
        }
1635
1636 7
        return static::create(
1637 7
            $tmpArray,
1638 7
            $this->iteratorClass,
1639 7
            false
1640
        );
1641
    }
1642
1643
    /**
1644
     * Find all items in an array that pass the truth test.
1645
     *
1646
     * @param \Closure|null $closure [optional] <p>
1647
     *                               The callback function to use
1648
     *                               </p>
1649
     *                               <p>
1650
     *                               If no callback is supplied, all entries of
1651
     *                               input equal to false (see
1652
     *                               converting to
1653
     *                               boolean) will be removed.
1654
     *                               </p>
1655
     * @param int           $flag    [optional] <p>
1656
     *                               Flag determining what arguments are sent to <i>callback</i>:
1657
     *                               </p><ul>
1658
     *                               <li>
1659
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1660
     *                               to <i>callback</i> instead of the value</span>
1661
     *                               </li>
1662
     *                               <li>
1663
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1664
     *                               arguments to <i>callback</i> instead of the value</span>
1665
     *                               </li>
1666
     *                               </ul>
1667
     *
1668
     * @return static
1669
     *                <p>(Immutable)</p>
1670
     */
1671 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1672
    {
1673 12
        if (!$closure) {
1674 1
            return $this->clean();
1675
        }
1676
1677 12
        return static::create(
1678 12
            \array_filter($this->getArray(), $closure, $flag),
1679 12
            $this->iteratorClass,
1680 12
            false
1681
        );
1682
    }
1683
1684
    /**
1685
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1686
     * property within that.
1687
     *
1688
     * @param string          $property
1689
     * @param string|string[] $value
1690
     * @param string          $comparisonOp
1691
     *                                      <p>
1692
     *                                      'eq' (equals),<br />
1693
     *                                      'gt' (greater),<br />
1694
     *                                      'gte' || 'ge' (greater or equals),<br />
1695
     *                                      'lt' (less),<br />
1696
     *                                      'lte' || 'le' (less or equals),<br />
1697
     *                                      'ne' (not equals),<br />
1698
     *                                      'contains',<br />
1699
     *                                      'notContains',<br />
1700
     *                                      'newer' (via strtotime),<br />
1701
     *                                      'older' (via strtotime),<br />
1702
     *                                      </p>
1703
     *
1704
     * @return static
1705
     *                <p>(Immutable)</p>
1706
     */
1707 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1708
    {
1709 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...
1710 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1711
        }
1712
1713
        $ops = [
1714
            'eq' => static function ($item, $prop, $value): bool {
1715 1
                return $item[$prop] === $value;
1716 1
            },
1717
            'gt' => static function ($item, $prop, $value): bool {
1718
                return $item[$prop] > $value;
1719 1
            },
1720
            'ge' => static function ($item, $prop, $value): bool {
1721
                return $item[$prop] >= $value;
1722 1
            },
1723
            'gte' => static function ($item, $prop, $value): bool {
1724
                return $item[$prop] >= $value;
1725 1
            },
1726
            'lt' => static function ($item, $prop, $value): bool {
1727 1
                return $item[$prop] < $value;
1728 1
            },
1729
            'le' => static function ($item, $prop, $value): bool {
1730
                return $item[$prop] <= $value;
1731 1
            },
1732
            'lte' => static function ($item, $prop, $value): bool {
1733
                return $item[$prop] <= $value;
1734 1
            },
1735
            'ne' => static function ($item, $prop, $value): bool {
1736
                return $item[$prop] !== $value;
1737 1
            },
1738
            'contains' => static function ($item, $prop, $value): bool {
1739 1
                return \in_array($item[$prop], (array) $value, true);
1740 1
            },
1741
            'notContains' => static function ($item, $prop, $value): bool {
1742
                return !\in_array($item[$prop], (array) $value, true);
1743 1
            },
1744
            'newer' => static function ($item, $prop, $value): bool {
1745
                return \strtotime($item[$prop]) > \strtotime($value);
1746 1
            },
1747
            'older' => static function ($item, $prop, $value): bool {
1748
                return \strtotime($item[$prop]) < \strtotime($value);
1749 1
            },
1750
        ];
1751
1752 1
        $result = \array_values(
1753 1
            \array_filter(
1754 1
                $this->getArray(),
1755
                static function ($item) use (
1756 1
                    $property,
1757 1
                    $value,
1758 1
                    $ops,
1759 1
                    $comparisonOp
1760
                ) {
1761 1
                    $item = (array) $item;
1762 1
                    $itemArrayy = static::create($item);
1763 1
                    $item[$property] = $itemArrayy->get($property, []);
1764
1765 1
                    return $ops[$comparisonOp]($item, $property, $value);
1766 1
                }
1767
            )
1768
        );
1769
1770 1
        return static::create(
1771 1
            $result,
1772 1
            $this->iteratorClass,
1773 1
            false
1774
        );
1775
    }
1776
1777
    /**
1778
     * Find the first item in an array that passes the truth test,
1779
     *  otherwise return false
1780
     *
1781
     * @param \Closure $closure
1782
     *
1783
     * @return false|mixed
1784
     *                     <p>Return false if we did not find the value.</p>
1785
     */
1786 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...
1787
    {
1788 8
        foreach ($this->getGenerator() as $key => $value) {
1789 6
            if ($closure($value, $key)) {
1790 5
                return $value;
1791
            }
1792
        }
1793
1794 3
        return false;
1795
    }
1796
1797
    /**
1798
     * find by ...
1799
     *
1800
     * @param string          $property
1801
     * @param string|string[] $value
1802
     * @param string          $comparisonOp
1803
     *
1804
     * @return static
1805
     *                <p>(Immutable)</p>
1806
     */
1807 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1808
    {
1809 1
        return $this->filterBy($property, $value, $comparisonOp);
1810
    }
1811
1812
    /**
1813
     * Get the first value from the current array.
1814
     *
1815
     * @return mixed
1816
     *               <p>Return null if there wasn't a element.</p>
1817
     */
1818 21
    public function first()
1819
    {
1820 21
        $key_first = $this->firstKey();
1821 21
        if ($key_first === null) {
1822 3
            return null;
1823
        }
1824
1825 18
        return $this->get($key_first);
1826
    }
1827
1828
    /**
1829
     * Get the first key from the current array.
1830
     *
1831
     * @return mixed
1832
     *               <p>Return null if there wasn't a element.</p>
1833
     */
1834 28
    public function firstKey()
1835
    {
1836 28
        $this->generatorToArray();
1837
1838 28
        return \array_key_first($this->array);
1839
    }
1840
1841
    /**
1842
     * Get the first value(s) from the current array.
1843
     * And will return an empty array if there was no first entry.
1844
     *
1845
     * @param int|null $number <p>How many values you will take?</p>
1846
     *
1847
     * @return static
1848
     *                <p>(Immutable)</p>
1849
     */
1850 37 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1851
    {
1852 37
        $arrayTmp = $this->getArray();
1853
1854 37
        if ($number === null) {
1855 14
            $array = (array) \array_shift($arrayTmp);
1856
        } else {
1857 23
            $number = (int) $number;
1858 23
            $array = \array_splice($arrayTmp, 0, $number);
1859
        }
1860
1861 37
        return static::create(
1862 37
            $array,
1863 37
            $this->iteratorClass,
1864 37
            false
1865
        );
1866
    }
1867
1868
    /**
1869
     * Get the first value(s) from the current array.
1870
     * And will return an empty array if there was no first entry.
1871
     *
1872
     * @param int|null $number <p>How many values you will take?</p>
1873
     *
1874
     * @return static
1875
     *                <p>(Immutable)</p>
1876
     */
1877 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...
1878
    {
1879 3
        $arrayTmp = $this->keys()->getArray();
1880
1881 3
        if ($number === null) {
1882
            $array = (array) \array_shift($arrayTmp);
1883
        } else {
1884 3
            $number = (int) $number;
1885 3
            $array = \array_splice($arrayTmp, 0, $number);
1886
        }
1887
1888 3
        return static::create(
1889 3
            $array,
1890 3
            $this->iteratorClass,
1891 3
            false
1892
        );
1893
    }
1894
1895
    /**
1896
     * Get and rmove the first value(s) from the current array.
1897
     * And will return an empty array if there was no first entry.
1898
     *
1899
     * @param int|null $number <p>How many values you will take?</p>
1900
     *
1901
     * @return static
1902
     *                <p>(Mutable)</p>
1903
     */
1904 34
    public function firstsMutable(int $number = null): self
1905
    {
1906 34
        $this->generatorToArray();
1907
1908 34
        if ($number === null) {
1909 19
            $this->array = (array) \array_shift($this->array);
1910
        } else {
1911 15
            $number = (int) $number;
1912 15
            $this->array = \array_splice($this->array, 0, $number);
1913
        }
1914
1915 34
        return $this;
1916
    }
1917
1918
    /**
1919
     * Exchanges all keys with their associated values in an array.
1920
     *
1921
     * @return static
1922
     *                <p>(Immutable)</p>
1923
     */
1924 1
    public function flip(): self
1925
    {
1926 1
        return static::create(
1927 1
            \array_flip($this->getArray()),
1928 1
            $this->iteratorClass,
1929 1
            false
1930
        );
1931
    }
1932
1933
    /**
1934
     * Get a value from an array (optional using dot-notation).
1935
     *
1936
     * @param mixed $key      <p>The key to look for.</p>
1937
     * @param mixed $fallback <p>Value to fallback to.</p>
1938
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1939
     *                        class.</p>
1940
     *
1941
     * @return mixed|static
1942
     *
1943
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
1944
     */
1945 172
    public function get($key, $fallback = null, array $array = null)
1946
    {
1947 172
        if ($array !== null) {
1948 4
            $usedArray = $array;
1949
        } else {
1950 169
            $this->generatorToArray();
1951
1952 169
            $usedArray = $this->array;
1953
        }
1954
1955 172
        if ($key === null) {
1956 1
            return static::create(
1957 1
                $usedArray,
1958 1
                $this->iteratorClass,
1959 1
                false
1960
            );
1961
        }
1962
1963
        // php cast "bool"-index into "int"-index
1964 172
        if ((bool) $key === $key) {
1965 3
            $key = (int) $key;
1966
        }
1967
1968 172
        if (\array_key_exists($key, $usedArray) === true) {
1969 162
            if (\is_array($usedArray[$key]) === true) {
1970 11
                return static::create(
1971 11
                    $usedArray[$key],
1972 11
                    $this->iteratorClass,
1973 11
                    false
1974
                );
1975
            }
1976
1977 154
            return $usedArray[$key];
1978
        }
1979
1980
        // crawl through array, get key according to object or not
1981 24
        $usePath = false;
1982
        if (
1983 24
            $this->pathSeparator
1984
            &&
1985 24
            (string) $key === $key
1986
            &&
1987 24
            \strpos($key, $this->pathSeparator) !== false
1988
        ) {
1989 7
            $segments = \explode($this->pathSeparator, (string) $key);
1990 7
            if ($segments !== false) {
1991 7
                $usePath = true;
1992
1993 7
                foreach ($segments as $segment) {
1994
                    if (
1995
                        (
1996 7
                            \is_array($usedArray) === true
1997
                            ||
1998 7
                            $usedArray instanceof \ArrayAccess
1999
                        )
2000
                        &&
2001 7
                        isset($usedArray[$segment])
2002
                    ) {
2003 7
                        $usedArray = $usedArray[$segment];
2004
2005 7
                        continue;
2006
                    }
2007
2008
                    if (
2009 6
                        \is_object($usedArray) === true
2010
                        &&
2011 6
                        \property_exists($usedArray, $segment)
2012
                    ) {
2013 1
                        $usedArray = $usedArray->{$segment};
2014
2015 1
                        continue;
2016
                    }
2017
2018 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
2019
                }
2020
            }
2021
        }
2022
2023 24
        if (!$usePath && !isset($usedArray[$key])) {
2024 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
2025
        }
2026
2027 7
        if (\is_array($usedArray) === true) {
2028 1
            return static::create(
2029 1
                $usedArray,
2030 1
                $this->iteratorClass,
2031 1
                false
2032
            );
2033
        }
2034
2035 7
        return $usedArray;
2036
    }
2037
2038
    /**
2039
     * alias: for "Arrayy->getArray()"
2040
     *
2041
     * @return array
2042
     *
2043
     * @see          Arrayy::getArray()
2044
     *
2045
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2046
     */
2047 1
    public function getAll(): array
2048
    {
2049 1
        return $this->getArray();
2050
    }
2051
2052
    /**
2053
     * Get the current array from the "Arrayy"-object.
2054
     *
2055
     * @param bool $convertAllArrayyElements
2056
     *
2057
     * @return array
2058
     *
2059
     * @psalm-return array<mixed,mixed>|array<TKey,T>
2060
     */
2061 859
    public function getArray($convertAllArrayyElements = false): array
2062
    {
2063
        // init
2064 859
        $array = [];
2065
2066 859
        if ($convertAllArrayyElements) {
2067 1
            foreach ($this->getGenerator() as $key => $value) {
2068 1
                if ($value instanceof self) {
2069 1
                    $value = $value->getArray(true);
2070
                }
2071
2072 1
                $array[$key] = $value;
2073
            }
2074
        } else {
2075 859
            foreach ($this->getGenerator() as $key => $value) {
2076 752
                $array[$key] = $value;
2077
            }
2078
        }
2079
2080 859
        return $array;
2081
    }
2082
2083
    /**
2084
     * Returns the values from a single column of the input array, identified by
2085
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2086
     *
2087
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2088
     * array by the values from the $indexKey column in the input array.
2089
     *
2090
     * @param mixed $columnKey
2091
     * @param mixed $indexKey
2092
     *
2093
     * @return static
2094
     *                <p>(Immutable)</p>
2095
     */
2096 1
    public function getColumn($columnKey = null, $indexKey = null): self
2097
    {
2098 1
        return static::create(
2099 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2100 1
            $this->iteratorClass,
2101 1
            false
2102
        );
2103
    }
2104
2105
    /**
2106
     * Get the current array from the "Arrayy"-object as generator.
2107
     *
2108
     * @return \Generator
2109
     *
2110
     * @psalm-return \Generator<mixed,T>|\Generator<TKey,T>
2111
     */
2112 942
    public function getGenerator(): \Generator
2113
    {
2114 942
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2115 40
            yield from $this->generator;
2116
        }
2117
2118 942
        yield from $this->array;
2119 898
    }
2120
2121
    /**
2122
     * alias: for "Arrayy->keys()"
2123
     *
2124
     * @return static
2125
     *                <p>(Immutable)</p>
2126
     *
2127
     * @see Arrayy::keys()
2128
     */
2129 2
    public function getKeys()
2130
    {
2131 2
        return $this->keys();
2132
    }
2133
2134
    /**
2135
     * Get the current array from the "Arrayy"-object as object.
2136
     *
2137
     * @return \stdClass
2138
     */
2139 4
    public function getObject(): \stdClass
2140
    {
2141 4
        return self::arrayToObject($this->getArray());
2142
    }
2143
2144
    /**
2145
     * alias: for "Arrayy->randomImmutable()"
2146
     *
2147
     * @return static
2148
     *                <p>(Immutable)</p>
2149
     *
2150
     * @see Arrayy::randomImmutable()
2151
     */
2152 4
    public function getRandom(): self
2153
    {
2154 4
        return $this->randomImmutable();
2155
    }
2156
2157
    /**
2158
     * alias: for "Arrayy->randomKey()"
2159
     *
2160
     * @return mixed
2161
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2162
     *
2163
     * @see Arrayy::randomKey()
2164
     */
2165 3
    public function getRandomKey()
2166
    {
2167 3
        return $this->randomKey();
2168
    }
2169
2170
    /**
2171
     * alias: for "Arrayy->randomKeys()"
2172
     *
2173
     * @param int $number
2174
     *
2175
     * @return static
2176
     *                <p>(Immutable)</p>
2177
     *
2178
     * @see Arrayy::randomKeys()
2179
     */
2180 8
    public function getRandomKeys(int $number): self
2181
    {
2182 8
        return $this->randomKeys($number);
2183
    }
2184
2185
    /**
2186
     * alias: for "Arrayy->randomValue()"
2187
     *
2188
     * @return mixed
2189
     *               <p>Get a random value or null if there wasn't a value.</p>
2190
     *
2191
     * @see Arrayy::randomValue()
2192
     */
2193 3
    public function getRandomValue()
2194
    {
2195 3
        return $this->randomValue();
2196
    }
2197
2198
    /**
2199
     * alias: for "Arrayy->randomValues()"
2200
     *
2201
     * @param int $number
2202
     *
2203
     * @return static
2204
     *                <p>(Immutable)</p>
2205
     *
2206
     * @see Arrayy::randomValues()
2207
     */
2208 6
    public function getRandomValues(int $number): self
2209
    {
2210 6
        return $this->randomValues($number);
2211
    }
2212
2213
    /**
2214
     * Gets all values.
2215
     *
2216
     * @return static
2217
     *                <p>The values of all elements in this array, in the order they
2218
     *                appear in the array.</p>
2219
     */
2220 4
    public function getValues()
2221
    {
2222 4
        $this->generatorToArray();
2223
2224 4
        return static::create(
2225 4
            \array_values($this->array),
2226 4
            $this->iteratorClass,
2227 4
            false
2228
        );
2229
    }
2230
2231
    /**
2232
     * Gets all values via Generator.
2233
     *
2234
     * @return \Generator
2235
     *                    <p>The values of all elements in this array, in the order they
2236
     *                    appear in the array as Generator.</p>
2237
     *
2238
     * @psalm-return \Generator<TKey,T>
2239
     */
2240 4
    public function getValuesYield(): \Generator
2241
    {
2242 4
        yield from $this->getGenerator();
2243 4
    }
2244
2245
    /**
2246
     * Group values from a array according to the results of a closure.
2247
     *
2248
     * @param callable|string $grouper  <p>A callable function name.</p>
2249
     * @param bool            $saveKeys
2250
     *
2251
     * @return static
2252
     *                <p>(Immutable)</p>
2253
     */
2254 4
    public function group($grouper, bool $saveKeys = false): self
2255
    {
2256
        // init
2257 4
        $result = [];
2258
2259
        // Iterate over values, group by property/results from closure.
2260 4
        foreach ($this->getGenerator() as $key => $value) {
2261 4
            if (\is_callable($grouper) === true) {
2262 3
                $groupKey = $grouper($value, $key);
2263
            } else {
2264 1
                $groupKey = $this->get($grouper);
2265
            }
2266
2267 4
            $newValue = $this->get($groupKey, null, $result);
2268
2269 4
            if ($groupKey instanceof self) {
2270
                $groupKey = $groupKey->getArray();
2271
            }
2272
2273 4
            if ($newValue instanceof self) {
2274 4
                $newValue = $newValue->getArray();
2275
            }
2276
2277
            // Add to results.
2278 4
            if ($groupKey !== null) {
2279 3
                if ($saveKeys) {
2280 2
                    $result[$groupKey] = $newValue;
2281 2
                    $result[$groupKey][$key] = $value;
2282
                } else {
2283 1
                    $result[$groupKey] = $newValue;
2284 1
                    $result[$groupKey][] = $value;
2285
                }
2286
            }
2287
        }
2288
2289 4
        return static::create(
2290 4
            $result,
2291 4
            $this->iteratorClass,
2292 4
            false
2293
        );
2294
    }
2295
2296
    /**
2297
     * Check if an array has a given key.
2298
     *
2299
     * @param mixed $key
2300
     *
2301
     * @return bool
2302
     */
2303 23
    public function has($key): bool
2304
    {
2305 23
        static $UN_FOUND = null;
2306
2307 23
        if ($UN_FOUND === null) {
2308
            // Generate unique string to use as marker.
2309 1
            $UN_FOUND = \uniqid('arrayy', true);
2310
        }
2311
2312 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2313
    }
2314
2315
    /**
2316
     * Check if an array has a given value.
2317
     *
2318
     * INFO: if you need to search recursive please use ```contains()```
2319
     *
2320
     * @param mixed $value
2321
     *
2322
     * @return bool
2323
     */
2324 1
    public function hasValue($value): bool
2325
    {
2326 1
        return $this->contains($value);
2327
    }
2328
2329
    /**
2330
     * Implodes the values of this array.
2331
     *
2332
     * @param string $glue
2333
     *
2334
     * @return string
2335
     */
2336 28
    public function implode(string $glue = ''): string
2337
    {
2338 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2339
    }
2340
2341
    /**
2342
     * Implodes the keys of this array.
2343
     *
2344
     * @param string $glue
2345
     *
2346
     * @return string
2347
     */
2348 8
    public function implodeKeys(string $glue = ''): string
2349
    {
2350 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2351
    }
2352
2353
    /**
2354
     * Given a list and an iterate-function that returns
2355
     * a key for each element in the list (or a property name),
2356
     * returns an object with an index of each item.
2357
     *
2358
     * @param mixed $key
2359
     *
2360
     * @return static
2361
     *                <p>(Immutable)</p>
2362
     */
2363 4
    public function indexBy($key): self
2364
    {
2365
        // init
2366 4
        $results = [];
2367
2368 4
        foreach ($this->getGenerator() as $a) {
2369 4
            if (\array_key_exists($key, $a) === true) {
2370 3
                $results[$a[$key]] = $a;
2371
            }
2372
        }
2373
2374 4
        return static::create(
2375 4
            $results,
2376 4
            $this->iteratorClass,
2377 4
            false
2378
        );
2379
    }
2380
2381
    /**
2382
     * alias: for "Arrayy->searchIndex()"
2383
     *
2384
     * @param mixed $value <p>The value to search for.</p>
2385
     *
2386
     * @return false|mixed
2387
     *
2388
     * @see Arrayy::searchIndex()
2389
     */
2390 4
    public function indexOf($value)
2391
    {
2392 4
        return $this->searchIndex($value);
2393
    }
2394
2395
    /**
2396
     * Get everything but the last..$to items.
2397
     *
2398
     * @param int $to
2399
     *
2400
     * @return static
2401
     *                <p>(Immutable)</p>
2402
     */
2403 12
    public function initial(int $to = 1): self
2404
    {
2405 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2406
    }
2407
2408
    /**
2409
     * Return an array with all elements found in input array.
2410
     *
2411
     * @param array $search
2412
     * @param bool  $keepKeys
2413
     *
2414
     * @return static
2415
     *                <p>(Immutable)</p>
2416
     *
2417
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2418
     */
2419 4
    public function intersection(array $search, bool $keepKeys = false): self
2420
    {
2421 4
        if ($keepKeys) {
2422 1
            return static::create(
2423 1
                \array_uintersect(
2424 1
                    $this->getArray(),
2425 1
                    $search,
2426
                    static function ($a, $b) {
2427 1
                        return $a === $b ? 0 : -1;
2428 1
                    }
2429
                ),
2430 1
                $this->iteratorClass,
2431 1
                false
2432
            );
2433
        }
2434
2435 3
        return static::create(
2436 3
            \array_values(\array_intersect($this->getArray(), $search)),
2437 3
            $this->iteratorClass,
2438 3
            false
2439
        );
2440
    }
2441
2442
    /**
2443
     * Return an array with all elements found in input array.
2444
     *
2445
     * @param array ...$array
2446
     *
2447
     * @return static
2448
     *                <p>(Immutable)</p>
2449
     *
2450
     * @psalm-param array<mixed,mixed>|array<TKey,T> ...$array
2451
     */
2452 1
    public function intersectionMulti(...$array): self
2453
    {
2454 1
        return static::create(
2455 1
            \array_values(\array_intersect($this->getArray(), ...$array)),
2456 1
            $this->iteratorClass,
2457 1
            false
2458
        );
2459
    }
2460
2461
    /**
2462
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2463
     *
2464
     * @param array $search
2465
     *
2466
     * @return bool
2467
     *
2468
     * @psalm-param array<mixed,mixed>|array<TKey,T> $search
2469
     */
2470 1
    public function intersects(array $search): bool
2471
    {
2472 1
        return $this->intersection($search)->count() > 0;
2473
    }
2474
2475
    /**
2476
     * Invoke a function on all of an array's values.
2477
     *
2478
     * @param callable $callable
2479
     * @param mixed    $arguments
2480
     *
2481
     * @return static
2482
     *                <p>(Immutable)</p>
2483
     */
2484 1
    public function invoke($callable, $arguments = []): self
2485
    {
2486
        // If one argument given for each iteration, create an array for it.
2487 1
        if (\is_array($arguments) === false) {
2488 1
            $arguments = \array_fill(
2489 1
                0,
2490 1
                $this->count(),
2491 1
                $arguments
2492
            );
2493
        }
2494
2495
        // If the callable has arguments, pass them.
2496 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...
2497 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2498
        } else {
2499 1
            $array = $this->map($callable);
2500
        }
2501
2502 1
        return static::create(
2503 1
            $array,
2504 1
            $this->iteratorClass,
2505 1
            false
2506
        );
2507
    }
2508
2509
    /**
2510
     * Check whether array is associative or not.
2511
     *
2512
     * @param bool $recursive
2513
     *
2514
     * @return bool
2515
     *              <p>Returns true if associative, false otherwise.</p>
2516
     */
2517 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...
2518
    {
2519 15
        if ($this->isEmpty()) {
2520 3
            return false;
2521
        }
2522
2523 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2524 13
            if ((string) $key !== $key) {
2525 11
                return false;
2526
            }
2527
        }
2528
2529 3
        return true;
2530
    }
2531
2532
    /**
2533
     * Check if a given key or keys are empty.
2534
     *
2535
     * @param int|int[]|string|string[]|null $keys
2536
     *
2537
     * @return bool
2538
     *              <p>Returns true if empty, false otherwise.</p>
2539
     */
2540 38
    public function isEmpty($keys = null): bool
2541
    {
2542 38
        if ($this->generator) {
2543
            return $this->getArray() === [];
2544
        }
2545
2546 38
        if ($keys === null) {
2547 38
            return $this->array === [];
2548
        }
2549
2550
        foreach ((array) $keys as $key) {
2551
            if (!empty($this->get($key))) {
2552
                return false;
2553
            }
2554
        }
2555
2556
        return true;
2557
    }
2558
2559
    /**
2560
     * Check if the current array is equal to the given "$array" or not.
2561
     *
2562
     * @param array $array
2563
     *
2564
     * @return bool
2565
     *
2566
     * @psalm-param array<mixed,mixed> $array
2567
     */
2568 1
    public function isEqual(array $array): bool
2569
    {
2570 1
        return $this->getArray() === $array;
2571
    }
2572
2573
    /**
2574
     * Check if the current array is a multi-array.
2575
     *
2576
     * @return bool
2577
     */
2578 22
    public function isMultiArray(): bool
2579
    {
2580
        return !(
2581 22
            \count($this->getArray(), \COUNT_NORMAL)
2582
            ===
2583 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2584
        );
2585
    }
2586
2587
    /**
2588
     * Check whether array is numeric or not.
2589
     *
2590
     * @return bool
2591
     *              <p>Returns true if numeric, false otherwise.</p>
2592
     */
2593 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...
2594
    {
2595 5
        if ($this->isEmpty()) {
2596 2
            return false;
2597
        }
2598
2599 4
        foreach ($this->keys()->getGenerator() as $key) {
2600 4
            if ((int) $key !== $key) {
2601 2
                return false;
2602
            }
2603
        }
2604
2605 2
        return true;
2606
    }
2607
2608
    /**
2609
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2610
     *
2611
     * @param bool $recursive
2612
     *
2613
     * @return bool
2614
     */
2615 9
    public function isSequential(bool $recursive = false): bool
2616
    {
2617
2618
        // recursive
2619
2620 9
        if ($recursive === true) {
2621
            return $this->array_keys_recursive($this->getArray())
2622
                   ===
2623
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2624
        }
2625
2626
        // non recursive
2627
2628 9
        return \array_keys($this->getArray())
2629
               ===
2630 9
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2631
    }
2632
2633
    /**
2634
     * @return array
2635
     *
2636
     * @psalm-return array<TKey,T>
2637
     */
2638
    public function jsonSerialize(): array
2639
    {
2640
        return $this->getArray();
2641
    }
2642
2643
    /**
2644
     * Gets the key/index of the element at the current internal iterator position.
2645
     *
2646
     * @return int|string|null
2647
     */
2648
    public function key()
2649
    {
2650
        return \key($this->array);
2651
    }
2652
2653
    /**
2654
     * Checks if the given key exists in the provided array.
2655
     *
2656
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2657
     *       then you need to use "Arrayy->offsetExists()".
2658
     *
2659
     * @param int|string $key the key to look for
2660
     *
2661
     * @return bool
2662
     */
2663 127
    public function keyExists($key): bool
2664
    {
2665 127
        return \array_key_exists($key, $this->array);
2666
    }
2667
2668
    /**
2669
     * Get all keys from the current array.
2670
     *
2671
     * @param bool       $recursive     [optional] <p>
2672
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2673
     *                                  </p>
2674
     * @param mixed|null $search_values [optional] <p>
2675
     *                                  If specified, then only keys containing these values are returned.
2676
     *                                  </p>
2677
     * @param bool       $strict        [optional] <p>
2678
     *                                  Determines if strict comparison (===) should be used during the search.
2679
     *                                  </p>
2680
     *
2681
     * @return static
2682
     *                <p>(Immutable) An array of all the keys in input.</p>
2683
     */
2684 29
    public function keys(
2685
        bool $recursive = false,
2686
        $search_values = null,
2687
        bool $strict = true
2688
    ): self {
2689
2690
        // recursive
2691
2692 29
        if ($recursive === true) {
2693 4
            $array = $this->array_keys_recursive(
2694 4
                null,
2695 4
                $search_values,
2696 4
                $strict
2697
            );
2698
2699 4
            return static::create(
2700 4
                $array,
2701 4
                $this->iteratorClass,
2702 4
                false
2703
            );
2704
        }
2705
2706
        // non recursive
2707
2708 28
        if ($search_values === null) {
2709
            $arrayFunction = function (): \Generator {
2710 28
                foreach ($this->getGenerator() as $key => $value) {
2711 26
                    yield $key;
2712
                }
2713 28
            };
2714
        } else {
2715
            $arrayFunction = function () use ($search_values, $strict): \Generator {
2716 1
                $is_array_tmp = \is_array($search_values);
2717
2718 1
                foreach ($this->getGenerator() as $key => $value) {
2719 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...
2720
                        (
2721 1
                            $is_array_tmp === false
2722
                            &&
2723 1
                            $strict === true
2724
                            &&
2725 1
                            $search_values === $value
2726
                        )
2727
                        ||
2728
                        (
2729 1
                            $is_array_tmp === false
2730
                            &&
2731 1
                            $strict === false
2732
                            &&
2733 1
                            $search_values == $value
2734
                        )
2735
                        ||
2736
                        (
2737 1
                            $is_array_tmp === true
2738
                            &&
2739 1
                            \in_array($value, $search_values, $strict)
2740
                        )
2741
                    ) {
2742 1
                        yield $key;
2743
                    }
2744
                }
2745 1
            };
2746
        }
2747
2748 28
        return static::create(
2749 28
            $arrayFunction,
2750 28
            $this->iteratorClass,
2751 28
            false
2752
        );
2753
    }
2754
2755
    /**
2756
     * Sort an array by key in reverse order.
2757
     *
2758
     * @param int $sort_flags [optional] <p>
2759
     *                        You may modify the behavior of the sort using the optional
2760
     *                        parameter sort_flags, for details
2761
     *                        see sort.
2762
     *                        </p>
2763
     *
2764
     * @return static
2765
     *                <p>(Mutable) Return this Arrayy object.</p>
2766
     */
2767 4
    public function krsort(int $sort_flags = 0): self
2768
    {
2769 4
        $this->generatorToArray();
2770
2771 4
        \krsort($this->array, $sort_flags);
2772
2773 4
        return $this;
2774
    }
2775
2776
    /**
2777
     * Get the last value from the current array.
2778
     *
2779
     * @return mixed
2780
     *               <p>Return null if there wasn't a element.</p>
2781
     */
2782 17
    public function last()
2783
    {
2784 17
        $key_last = $this->lastKey();
2785 17
        if ($key_last === null) {
2786 2
            return null;
2787
        }
2788
2789 15
        return $this->get($key_last);
2790
    }
2791
2792
    /**
2793
     * Get the last key from the current array.
2794
     *
2795
     * @return mixed
2796
     *               <p>Return null if there wasn't a element.</p>
2797
     */
2798 21
    public function lastKey()
2799
    {
2800 21
        $this->generatorToArray();
2801
2802 21
        return \array_key_last($this->array);
2803
    }
2804
2805
    /**
2806
     * Get the last value(s) from the current array.
2807
     *
2808
     * @param int|null $number
2809
     *
2810
     * @return static
2811
     *                <p>(Immutable)</p>
2812
     */
2813 13
    public function lastsImmutable(int $number = null): self
2814
    {
2815 13
        if ($this->isEmpty()) {
2816 1
            return static::create(
2817 1
                [],
2818 1
                $this->iteratorClass,
2819 1
                false
2820
            );
2821
        }
2822
2823 12
        if ($number === null) {
2824 8
            $poppedValue = $this->last();
2825
2826 8
            if ($poppedValue === null) {
2827 1
                $poppedValue = [$poppedValue];
2828
            } else {
2829 7
                $poppedValue = (array) $poppedValue;
2830
            }
2831
2832 8
            $arrayy = static::create(
2833 8
                $poppedValue,
2834 8
                $this->iteratorClass,
2835 8
                false
2836
            );
2837
        } else {
2838 4
            $number = (int) $number;
2839 4
            $arrayy = $this->rest(-$number);
2840
        }
2841
2842 12
        return $arrayy;
2843
    }
2844
2845
    /**
2846
     * Get the last value(s) from the current array.
2847
     *
2848
     * @param int|null $number
2849
     *
2850
     * @return static
2851
     *                <p>(Mutable)</p>
2852
     */
2853 13
    public function lastsMutable(int $number = null): self
2854
    {
2855 13
        if ($this->isEmpty()) {
2856 1
            return $this;
2857
        }
2858
2859 12
        if ($number === null) {
2860 8
            $poppedValue = $this->last();
2861
2862 8
            if ($poppedValue === null) {
2863 1
                $poppedValue = [$poppedValue];
2864
            } else {
2865 7
                $poppedValue = (array) $poppedValue;
2866
            }
2867
2868 8
            $this->array = static::create(
2869 8
                $poppedValue,
2870 8
                $this->iteratorClass,
2871 8
                false
2872 8
            )->getArray();
2873
        } else {
2874 4
            $number = (int) $number;
2875 4
            $this->array = $this->rest(-$number)->getArray();
2876
        }
2877
2878 12
        $this->generator = null;
2879
2880 12
        return $this;
2881
    }
2882
2883
    /**
2884
     * Count the values from the current array.
2885
     *
2886
     * alias: for "Arrayy->count()"
2887
     *
2888
     * @param int $mode
2889
     *
2890
     * @return int
2891
     *
2892
     * @see Arrayy::count()
2893
     */
2894 20
    public function length(int $mode = \COUNT_NORMAL): int
2895
    {
2896 20
        return $this->count($mode);
2897
    }
2898
2899
    /**
2900
     * Apply the given function to the every element of the array,
2901
     * collecting the results.
2902
     *
2903
     * @param callable $callable
2904
     * @param bool     $useKeyAsSecondParameter
2905
     * @param mixed    ...$arguments
2906
     *
2907
     * @return static
2908
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2909
     */
2910 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments)
2911
    {
2912 5
        $useArguments = \func_num_args() > 2;
2913
2914 5
        return static::create(
2915
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2916 5
                foreach ($this->getGenerator() as $key => $value) {
2917 4
                    if ($useArguments) {
2918 3
                        if ($useKeyAsSecondParameter) {
2919
                            yield $key => $callable($value, $key, ...$arguments);
2920
                        } else {
2921 3
                            yield $key => $callable($value, ...$arguments);
2922
                        }
2923
                    } else {
2924
                        /** @noinspection NestedPositiveIfStatementsInspection */
2925 4
                        if ($useKeyAsSecondParameter) {
2926
                            yield $key => $callable($value, $key);
2927
                        } else {
2928 4
                            yield $key => $callable($value);
2929
                        }
2930
                    }
2931
                }
2932 5
            },
2933 5
            $this->iteratorClass,
2934 5
            false
2935
        );
2936
    }
2937
2938
    /**
2939
     * Check if all items in current array match a truth test.
2940
     *
2941
     * @param \Closure $closure
2942
     *
2943
     * @return bool
2944
     */
2945 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...
2946
    {
2947 15
        if ($this->count() === 0) {
2948 2
            return false;
2949
        }
2950
2951 13
        foreach ($this->getGenerator() as $key => $value) {
2952 13
            $value = $closure($value, $key);
2953
2954 13
            if ($value === false) {
2955 7
                return false;
2956
            }
2957
        }
2958
2959 7
        return true;
2960
    }
2961
2962
    /**
2963
     * Check if any item in the current array matches a truth test.
2964
     *
2965
     * @param \Closure $closure
2966
     *
2967
     * @return bool
2968
     */
2969 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...
2970
    {
2971 14
        if ($this->count() === 0) {
2972 2
            return false;
2973
        }
2974
2975 12
        foreach ($this->getGenerator() as $key => $value) {
2976 12
            $value = $closure($value, $key);
2977
2978 12
            if ($value === true) {
2979 9
                return true;
2980
            }
2981
        }
2982
2983 4
        return false;
2984
    }
2985
2986
    /**
2987
     * Get the max value from an array.
2988
     *
2989
     * @return mixed
2990
     */
2991 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...
2992
    {
2993 10
        if ($this->count() === 0) {
2994 1
            return false;
2995
        }
2996
2997 9
        $max = false;
2998 9
        foreach ($this->getGenerator() as $value) {
2999
            if (
3000 9
                $max === false
3001
                ||
3002 9
                $value > $max
3003
            ) {
3004 9
                $max = $value;
3005
            }
3006
        }
3007
3008 9
        return $max;
3009
    }
3010
3011
    /**
3012
     * Merge the new $array into the current array.
3013
     *
3014
     * - keep key,value from the current array, also if the index is in the new $array
3015
     *
3016
     * @param array $array
3017
     * @param bool  $recursive
3018
     *
3019
     * @return static
3020
     *                <p>(Immutable)</p>
3021
     *
3022
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
3023
     */
3024 25 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...
3025
    {
3026 25
        if ($recursive === true) {
3027 4
            $result = \array_replace_recursive($this->getArray(), $array);
3028
        } else {
3029 21
            $result = \array_replace($this->getArray(), $array);
3030
        }
3031
3032 25
        return static::create(
3033 25
            $result,
3034 25
            $this->iteratorClass,
3035 25
            false
3036
        );
3037
    }
3038
3039
    /**
3040
     * Merge the new $array into the current array.
3041
     *
3042
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
3043
     * - create new indexes
3044
     *
3045
     * @param array $array
3046
     * @param bool  $recursive
3047
     *
3048
     * @return static
3049
     *                <p>(Immutable)</p>
3050
     *
3051
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
3052
     */
3053 16 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...
3054
    {
3055 16
        if ($recursive === true) {
3056 4
            $result = \array_merge_recursive($this->getArray(), $array);
3057
        } else {
3058 12
            $result = \array_merge($this->getArray(), $array);
3059
        }
3060
3061 16
        return static::create(
3062 16
            $result,
3063 16
            $this->iteratorClass,
3064 16
            false
3065
        );
3066
    }
3067
3068
    /**
3069
     * Merge the the current array into the $array.
3070
     *
3071
     * - use key,value from the new $array, also if the index is in the current array
3072
     *
3073
     * @param array $array
3074
     * @param bool  $recursive
3075
     *
3076
     * @return static
3077
     *                <p>(Immutable)</p>
3078
     *
3079
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
3080
     */
3081 16 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...
3082
    {
3083 16
        if ($recursive === true) {
3084 4
            $result = \array_replace_recursive($array, $this->getArray());
3085
        } else {
3086 12
            $result = \array_replace($array, $this->getArray());
3087
        }
3088
3089 16
        return static::create(
3090 16
            $result,
3091 16
            $this->iteratorClass,
3092 16
            false
3093
        );
3094
    }
3095
3096
    /**
3097
     * Merge the current array into the new $array.
3098
     *
3099
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3100
     * - create new indexes
3101
     *
3102
     * @param array $array
3103
     * @param bool  $recursive
3104
     *
3105
     * @return static
3106
     *                <p>(Immutable)</p>
3107
     *
3108
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
3109
     */
3110 17 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...
3111
    {
3112 17
        if ($recursive === true) {
3113 4
            $result = \array_merge_recursive($array, $this->getArray());
3114
        } else {
3115 13
            $result = \array_merge($array, $this->getArray());
3116
        }
3117
3118 17
        return static::create(
3119 17
            $result,
3120 17
            $this->iteratorClass,
3121 17
            false
3122
        );
3123
    }
3124
3125
    /**
3126
     * @return ArrayyMeta|static
3127
     */
3128 15
    public static function meta()
3129
    {
3130 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3131
    }
3132
3133
    /**
3134
     * Get the min value from an array.
3135
     *
3136
     * @return mixed
3137
     */
3138 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...
3139
    {
3140 10
        if ($this->count() === 0) {
3141 1
            return false;
3142
        }
3143
3144 9
        $min = false;
3145 9
        foreach ($this->getGenerator() as $value) {
3146
            if (
3147 9
                $min === false
3148
                ||
3149 9
                $value < $min
3150
            ) {
3151 9
                $min = $value;
3152
            }
3153
        }
3154
3155 9
        return $min;
3156
    }
3157
3158
    /**
3159
     * Get the most used value from the array.
3160
     *
3161
     * @return mixed
3162
     *               <p>Return null if there wasn't a element.</p>
3163
     */
3164 3
    public function mostUsedValue()
3165
    {
3166 3
        return $this->countValues()->arsort()->firstKey();
3167
    }
3168
3169
    /**
3170
     * Get the most used value from the array.
3171
     *
3172
     * @param int|null $number <p>How many values you will take?</p>
3173
     *
3174
     * @return static
3175
     *                <p>(Immutable)</p>
3176
     */
3177 3
    public function mostUsedValues(int $number = null): self
3178
    {
3179 3
        return $this->countValues()->arsort()->firstsKeys($number);
3180
    }
3181
3182
    /**
3183
     * Move an array element to a new index.
3184
     *
3185
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3186
     *
3187
     * @param int|string $from
3188
     * @param int        $to
3189
     *
3190
     * @return static
3191
     *                <p>(Immutable)</p>
3192
     */
3193 1
    public function moveElement($from, $to): self
3194
    {
3195 1
        $array = $this->getArray();
3196
3197 1
        if ((int) $from === $from) {
3198 1
            $tmp = \array_splice($array, $from, 1);
3199 1
            \array_splice($array, (int) $to, 0, $tmp);
3200 1
            $output = $array;
3201 1
        } elseif ((string) $from === $from) {
3202 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3203 1
            $itemToMove = $array[$from];
3204 1
            if ($indexToMove !== false) {
3205 1
                \array_splice($array, $indexToMove, 1);
3206
            }
3207 1
            $i = 0;
3208 1
            $output = [];
3209 1
            foreach ($array as $key => $item) {
3210 1
                if ($i === $to) {
3211 1
                    $output[$from] = $itemToMove;
3212
                }
3213 1
                $output[$key] = $item;
3214 1
                ++$i;
3215
            }
3216
        } else {
3217
            $output = [];
3218
        }
3219
3220 1
        return static::create(
3221 1
            $output,
3222 1
            $this->iteratorClass,
3223 1
            false
3224
        );
3225
    }
3226
3227
    /**
3228
     * Move an array element to the first place.
3229
     *
3230
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3231
     *       loss the keys of an indexed array.
3232
     *
3233
     * @param int|string $key
3234
     *
3235
     * @return static
3236
     *                <p>(Immutable)</p>
3237
     */
3238 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...
3239
    {
3240 1
        $array = $this->getArray();
3241
3242 1
        if ($this->offsetExists($key)) {
3243 1
            $tmpValue = $this->get($key);
3244 1
            unset($array[$key]);
3245 1
            $array = [$key => $tmpValue] + $array;
3246
        }
3247
3248 1
        return static::create(
3249 1
            $array,
3250 1
            $this->iteratorClass,
3251 1
            false
3252
        );
3253
    }
3254
3255
    /**
3256
     * Move an array element to the last place.
3257
     *
3258
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3259
     *       loss the keys of an indexed array.
3260
     *
3261
     * @param int|string $key
3262
     *
3263
     * @return static
3264
     *                <p>(Immutable)</p>
3265
     */
3266 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...
3267
    {
3268 1
        $array = $this->getArray();
3269
3270 1
        if ($this->offsetExists($key)) {
3271 1
            $tmpValue = $this->get($key);
3272 1
            unset($array[$key]);
3273 1
            $array += [$key => $tmpValue];
3274
        }
3275
3276 1
        return static::create(
3277 1
            $array,
3278 1
            $this->iteratorClass,
3279 1
            false
3280
        );
3281
    }
3282
3283
    /**
3284
     * Moves the internal iterator position to the next element and returns this element.
3285
     *
3286
     * @return mixed
3287
     */
3288
    public function next()
3289
    {
3290
        return \next($this->array);
3291
    }
3292
3293
    /**
3294
     * Get the next nth keys and values from the array.
3295
     *
3296
     * @param int $step
3297
     * @param int $offset
3298
     *
3299
     * @return static
3300
     */
3301 1
    public function nth(int $step, int $offset = 0): self
3302
    {
3303
        $arrayFunction = function () use ($step, $offset): \Generator {
3304 1
            $position = 0;
3305 1
            foreach ($this->getGenerator() as $key => $value) {
3306 1
                if ($position++ % $step !== $offset) {
3307 1
                    continue;
3308
                }
3309
3310 1
                yield $key => $value;
3311
            }
3312 1
        };
3313
3314 1
        return static::create(
3315 1
            $arrayFunction,
3316 1
            $this->iteratorClass,
3317 1
            false
3318
        );
3319
    }
3320
3321
    /**
3322
     * Get a subset of the items from the given array.
3323
     *
3324
     * @param mixed[] $keys
3325
     *
3326
     * @return static
3327
     *                <p>(Immutable)</p>
3328
     */
3329 1
    public function only(array $keys): self
3330
    {
3331 1
        $array = $this->getArray();
3332
3333 1
        return static::create(
3334 1
            \array_intersect_key($array, \array_flip($keys)),
3335 1
            $this->iteratorClass,
3336 1
            false
3337
        );
3338
    }
3339
3340
    /**
3341
     * Pad array to the specified size with a given value.
3342
     *
3343
     * @param int   $size  <p>Size of the result array.</p>
3344
     * @param mixed $value <p>Empty value by default.</p>
3345
     *
3346
     * @return static
3347
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3348
     */
3349 5
    public function pad(int $size, $value): self
3350
    {
3351 5
        return static::create(
3352 5
            \array_pad($this->getArray(), $size, $value),
3353 5
            $this->iteratorClass,
3354 5
            false
3355
        );
3356
    }
3357
3358
    /**
3359
     * Partitions this array in two array according to a predicate.
3360
     * Keys are preserved in the resulting array.
3361
     *
3362
     * @param \Closure $closure
3363
     *                          <p>The predicate on which to partition.</p>
3364
     *
3365
     * @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...
3366
     *                    <p>An array with two elements. The first element contains the array
3367
     *                    of elements where the predicate returned TRUE, the second element
3368
     *                    contains the array of elements where the predicate returned FALSE.</p>
3369
     */
3370 1
    public function partition(\Closure $closure): array
3371
    {
3372
        // init
3373 1
        $matches = [];
3374 1
        $noMatches = [];
3375
3376 1
        foreach ($this->array as $key => $value) {
3377 1
            if ($closure($value, $key)) {
3378 1
                $matches[$key] = $value;
3379
            } else {
3380 1
                $noMatches[$key] = $value;
3381
            }
3382
        }
3383
3384 1
        return [self::create($matches), self::create($noMatches)];
3385
    }
3386
3387
    /**
3388
     * Pop a specified value off the end of the current array.
3389
     *
3390
     * @return mixed
3391
     *               <p>(Mutable) The popped element from the current array.</p>
3392
     */
3393 5
    public function pop()
3394
    {
3395 5
        $this->generatorToArray();
3396
3397 5
        return \array_pop($this->array);
3398
    }
3399
3400
    /**
3401
     * Prepend a (key) + value to the current array.
3402
     *
3403
     * @param mixed $value
3404
     * @param mixed $key
3405
     *
3406
     * @return static
3407
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3408
     */
3409 11
    public function prepend($value, $key = null)
3410
    {
3411 11
        $this->generatorToArray();
3412
3413 11
        if ($this->properties !== []) {
3414 3
            $this->checkType($key, $value);
3415
        }
3416
3417 9
        if ($key === null) {
3418 8
            \array_unshift($this->array, $value);
3419
        } else {
3420 2
            $this->array = [$key => $value] + $this->array;
3421
        }
3422
3423 9
        return $this;
3424
    }
3425
3426
    /**
3427
     * Add a suffix to each key.
3428
     *
3429
     * @param mixed $suffix
3430
     *
3431
     * @return static
3432
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3433
     */
3434 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...
3435
    {
3436
        // init
3437 10
        $result = [];
3438
3439 10
        foreach ($this->getGenerator() as $key => $item) {
3440 9
            if ($item instanceof self) {
3441
                $result[$key] = $item->prependToEachKey($suffix);
3442 9
            } elseif (\is_array($item) === true) {
3443
                $result[$key] = self::create(
3444
                    $item,
3445
                    $this->iteratorClass,
3446
                    false
3447
                )->prependToEachKey($suffix)
3448
                    ->toArray();
3449
            } else {
3450 9
                $result[$key . $suffix] = $item;
3451
            }
3452
        }
3453
3454 10
        return self::create(
3455 10
            $result,
3456 10
            $this->iteratorClass,
3457 10
            false
3458
        );
3459
    }
3460
3461
    /**
3462
     * Add a suffix to each value.
3463
     *
3464
     * @param mixed $suffix
3465
     *
3466
     * @return static
3467
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3468
     */
3469 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...
3470
    {
3471
        // init
3472 10
        $result = [];
3473
3474 10
        foreach ($this->getGenerator() as $key => $item) {
3475 9
            if ($item instanceof self) {
3476
                $result[$key] = $item->prependToEachValue($suffix);
3477 9
            } elseif (\is_array($item) === true) {
3478
                $result[$key] = self::create(
3479
                    $item,
3480
                    $this->iteratorClass,
3481
                    false
3482
                )->prependToEachValue($suffix)
3483
                    ->toArray();
3484 9
            } elseif (\is_object($item) === true) {
3485 1
                $result[$key] = $item;
3486
            } else {
3487 8
                $result[$key] = $item . $suffix;
3488
            }
3489
        }
3490
3491 10
        return self::create(
3492 10
            $result,
3493 10
            $this->iteratorClass,
3494 10
            false
3495
        );
3496
    }
3497
3498
    /**
3499
     * Return the value of a given key and
3500
     * delete the key.
3501
     *
3502
     * @param int|int[]|string|string[]|null $keyOrKeys
3503
     * @param mixed                          $fallback
3504
     *
3505
     * @return mixed
3506
     */
3507 1
    public function pull($keyOrKeys = null, $fallback = null)
3508
    {
3509 1
        if ($keyOrKeys === null) {
3510
            $array = $this->getArray();
3511
            $this->clear();
3512
3513
            return $array;
3514
        }
3515
3516 1
        if (\is_array($keyOrKeys) === true) {
3517 1
            $valueOrValues = [];
3518 1
            foreach ($keyOrKeys as $key) {
3519 1
                $valueOrValues[] = $this->get($key, $fallback);
3520 1
                $this->offsetUnset($key);
3521
            }
3522
        } else {
3523 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3524 1
            $this->offsetUnset($keyOrKeys);
3525
        }
3526
3527 1
        return $valueOrValues;
3528
    }
3529
3530
    /**
3531
     * Push one or more values onto the end of array at once.
3532
     *
3533
     * @param array ...$args
3534
     *
3535
     * @return static
3536
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3537
     *
3538
     * @noinspection ReturnTypeCanBeDeclaredInspection
3539
     *
3540
     * @psalm-param  array<mixed,mixed>|array<TKey,T> ...$args
3541
     */
3542 5
    public function push(...$args)
3543
    {
3544 5
        $this->generatorToArray();
3545
3546
        if (
3547 5
            $this->checkPropertyTypes
3548
            &&
3549 5
            $this->properties !== []
3550
        ) {
3551 1
            foreach ($args as $key => $value) {
3552 1
                $this->checkType($key, $value);
3553
            }
3554
        }
3555
3556 5
        \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3557
3558 5
        return $this;
3559
    }
3560
3561
    /**
3562
     * Get a random value from the current array.
3563
     *
3564
     * @param int|null $number <p>How many values you will take?</p>
3565
     *
3566
     * @return static
3567
     *                <p>(Immutable)</p>
3568
     */
3569 19
    public function randomImmutable(int $number = null): self
3570
    {
3571 19
        $this->generatorToArray();
3572
3573 19
        if ($this->count() === 0) {
3574 1
            return static::create(
3575 1
                [],
3576 1
                $this->iteratorClass,
3577 1
                false
3578
            );
3579
        }
3580
3581 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...
3582
            /** @noinspection NonSecureArrayRandUsageInspection */
3583 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3584
3585 13
            return static::create(
3586 13
                $arrayRandValue,
3587 13
                $this->iteratorClass,
3588 13
                false
3589
            );
3590
        }
3591
3592 6
        $arrayTmp = $this->array;
3593
        /** @noinspection NonSecureShuffleUsageInspection */
3594 6
        \shuffle($arrayTmp);
3595
3596 6
        return static::create(
3597 6
            $arrayTmp,
3598 6
            $this->iteratorClass,
3599 6
            false
3600 6
        )->firstsImmutable($number);
3601
    }
3602
3603
    /**
3604
     * Pick a random key/index from the keys of this array.
3605
     *
3606
     * @throws \RangeException If array is empty
3607
     *
3608
     * @return mixed
3609
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3610
     */
3611 4
    public function randomKey()
3612
    {
3613 4
        $result = $this->randomKeys(1);
3614
3615 4
        if (!isset($result[0])) {
3616
            $result[0] = null;
3617
        }
3618
3619 4
        return $result[0];
3620
    }
3621
3622
    /**
3623
     * Pick a given number of random keys/indexes out of this array.
3624
     *
3625
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3626
     *
3627
     * @throws \RangeException If array is empty
3628
     *
3629
     * @return static
3630
     *                <p>(Immutable)</p>
3631
     */
3632 13
    public function randomKeys(int $number): self
3633
    {
3634 13
        $this->generatorToArray();
3635
3636 13
        $count = $this->count();
3637
3638
        if (
3639 13
            $number === 0
3640
            ||
3641 13
            $number > $count
3642
        ) {
3643 2
            throw new \RangeException(
3644 2
                \sprintf(
3645 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3646 2
                    $number,
3647 2
                    $count
3648
                )
3649
            );
3650
        }
3651
3652 11
        $result = (array) \array_rand($this->array, $number);
3653
3654 11
        return static::create(
3655 11
            $result,
3656 11
            $this->iteratorClass,
3657 11
            false
3658
        );
3659
    }
3660
3661
    /**
3662
     * Get a random value from the current array.
3663
     *
3664
     * @param int|null $number <p>How many values you will take?</p>
3665
     *
3666
     * @return static
3667
     *                <p>(Mutable)</p>
3668
     */
3669 17
    public function randomMutable(int $number = null): self
3670
    {
3671 17
        $this->generatorToArray();
3672
3673 17
        if ($this->count() === 0) {
3674
            return static::create(
3675
                [],
3676
                $this->iteratorClass,
3677
                false
3678
            );
3679
        }
3680
3681 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...
3682
            /** @noinspection NonSecureArrayRandUsageInspection */
3683 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3684 7
            $this->array = $arrayRandValue;
3685
3686 7
            return $this;
3687
        }
3688
3689
        /** @noinspection NonSecureShuffleUsageInspection */
3690 11
        \shuffle($this->array);
3691
3692 11
        return $this->firstsMutable($number);
3693
    }
3694
3695
    /**
3696
     * Pick a random value from the values of this array.
3697
     *
3698
     * @return mixed
3699
     *               <p>Get a random value or null if there wasn't a value.</p>
3700
     */
3701 4
    public function randomValue()
3702
    {
3703 4
        $result = $this->randomImmutable();
3704
3705 4
        if (!isset($result[0])) {
3706
            $result[0] = null;
3707
        }
3708
3709 4
        return $result[0];
3710
    }
3711
3712
    /**
3713
     * Pick a given number of random values out of this array.
3714
     *
3715
     * @param int $number
3716
     *
3717
     * @return static
3718
     *                <p>(Mutable)</p>
3719
     */
3720 7
    public function randomValues(int $number): self
3721
    {
3722 7
        return $this->randomMutable($number);
3723
    }
3724
3725
    /**
3726
     * Get a random value from an array, with the ability to skew the results.
3727
     *
3728
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3729
     *
3730
     * @param array    $array
3731
     * @param int|null $number <p>How many values you will take?</p>
3732
     *
3733
     * @return static
3734
     *                <p>(Immutable)</p>
3735
     *
3736
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
3737
     */
3738 9
    public function randomWeighted(array $array, int $number = null): self
3739
    {
3740
        // init
3741 9
        $options = [];
3742
3743 9
        foreach ($array as $option => $weight) {
3744 9
            if ($this->searchIndex($option) !== false) {
3745 2
                for ($i = 0; $i < $weight; ++$i) {
3746 1
                    $options[] = $option;
3747
                }
3748
            }
3749
        }
3750
3751 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3752
    }
3753
3754
    /**
3755
     * Reduce the current array via callable e.g. anonymous-function.
3756
     *
3757
     * @param callable $callable
3758
     * @param mixed    $init
3759
     *
3760
     * @return static
3761
     *                <p>(Immutable)</p>
3762
     */
3763 18
    public function reduce($callable, $init = []): self
3764
    {
3765 18
        if ($this->generator) {
3766 1
            $result = $init;
3767
3768 1
            foreach ($this->getGenerator() as $value) {
3769 1
                $result = $callable($result, $value);
3770
            }
3771
3772 1
            return static::create(
3773 1
                $result,
3774 1
                $this->iteratorClass,
3775 1
                false
3776
            );
3777
        }
3778
3779 18
        $result = \array_reduce($this->array, $callable, $init);
3780
3781 18
        if ($result === null) {
3782
            $this->array = [];
3783
        } else {
3784 18
            $this->array = (array) $result;
3785
        }
3786
3787 18
        return static::create(
3788 18
            $this->array,
3789 18
            $this->iteratorClass,
3790 18
            false
3791
        );
3792
    }
3793
3794
    /**
3795
     * @param bool $unique
3796
     *
3797
     * @return static
3798
     *                <p>(Immutable)</p>
3799
     */
3800 14
    public function reduce_dimension(bool $unique = true): self
3801
    {
3802
        // init
3803 14
        $result = [];
3804
3805 14
        foreach ($this->getGenerator() as $val) {
3806 12
            if (\is_array($val) === true) {
3807 5
                $result[] = (new static($val))->reduce_dimension($unique)->getArray();
3808
            } else {
3809 12
                $result[] = [$val];
3810
            }
3811
        }
3812
3813 14
        $result = $result === [] ? [] : \array_merge(...$result);
3814
3815 14
        $resultArrayy = new static($result);
3816
3817 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3818
    }
3819
3820
    /**
3821
     * Create a numerically re-indexed Arrayy object.
3822
     *
3823
     * @return static
3824
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3825
     */
3826 9
    public function reindex(): self
3827
    {
3828 9
        $this->generatorToArray();
3829
3830 9
        $this->array = \array_values($this->array);
3831
3832 9
        return $this;
3833
    }
3834
3835
    /**
3836
     * Return all items that fail the truth test.
3837
     *
3838
     * @param \Closure $closure
3839
     *
3840
     * @return static
3841
     *                <p>(Immutable)</p>
3842
     */
3843 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...
3844
    {
3845
        // init
3846 1
        $filtered = [];
3847
3848 1
        foreach ($this->getGenerator() as $key => $value) {
3849 1
            if (!$closure($value, $key)) {
3850 1
                $filtered[$key] = $value;
3851
            }
3852
        }
3853
3854 1
        return static::create(
3855 1
            $filtered,
3856 1
            $this->iteratorClass,
3857 1
            false
3858
        );
3859
    }
3860
3861
    /**
3862
     * Remove a value from the current array (optional using dot-notation).
3863
     *
3864
     * @param mixed $key
3865
     *
3866
     * @return static
3867
     *                <p>(Mutable)</p>
3868
     */
3869 18
    public function remove($key)
3870
    {
3871
        // recursive call
3872 18
        if (\is_array($key) === true) {
3873
            foreach ($key as $k) {
3874
                $this->internalRemove($k);
3875
            }
3876
3877
            return static::create(
3878
                $this->getArray(),
3879
                $this->iteratorClass,
3880
                false
3881
            );
3882
        }
3883
3884 18
        $this->internalRemove($key);
3885
3886 18
        return static::create(
3887 18
            $this->getArray(),
3888 18
            $this->iteratorClass,
3889 18
            false
3890
        );
3891
    }
3892
3893
    /**
3894
     * alias: for "Arrayy->removeValue()"
3895
     *
3896
     * @param mixed $element
3897
     *
3898
     * @return static
3899
     */
3900 8
    public function removeElement($element)
3901
    {
3902 8
        return $this->removeValue($element);
3903
    }
3904
3905
    /**
3906
     * Remove the first value from the current array.
3907
     *
3908
     * @return static
3909
     *                <p>(Immutable)</p>
3910
     */
3911 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...
3912
    {
3913 7
        $tmpArray = $this->getArray();
3914
3915 7
        \array_shift($tmpArray);
3916
3917 7
        return static::create(
3918 7
            $tmpArray,
3919 7
            $this->iteratorClass,
3920 7
            false
3921
        );
3922
    }
3923
3924
    /**
3925
     * Remove the last value from the current array.
3926
     *
3927
     * @return static
3928
     *                <p>(Immutable)</p>
3929
     */
3930 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...
3931
    {
3932 7
        $tmpArray = $this->getArray();
3933
3934 7
        \array_pop($tmpArray);
3935
3936 7
        return static::create(
3937 7
            $tmpArray,
3938 7
            $this->iteratorClass,
3939 7
            false
3940
        );
3941
    }
3942
3943
    /**
3944
     * Removes a particular value from an array (numeric or associative).
3945
     *
3946
     * @param mixed $value
3947
     *
3948
     * @return static
3949
     *                <p>(Immutable)</p>
3950
     */
3951 8
    public function removeValue($value): self
3952
    {
3953 8
        $this->generatorToArray();
3954
3955
        // init
3956 8
        $isSequentialArray = $this->isSequential();
3957
3958 8
        foreach ($this->array as $key => $item) {
3959 7
            if ($item === $value) {
3960 7
                unset($this->array[$key]);
3961
            }
3962
        }
3963
3964 8
        if ($isSequentialArray) {
3965 6
            $this->array = \array_values($this->array);
3966
        }
3967
3968 8
        return static::create(
3969 8
            $this->array,
3970 8
            $this->iteratorClass,
3971 8
            false
3972
        );
3973
    }
3974
3975
    /**
3976
     * Generate array of repeated arrays.
3977
     *
3978
     * @param int $times <p>How many times has to be repeated.</p>
3979
     *
3980
     * @return static
3981
     *                <p>(Immutable)</p>
3982
     */
3983 1
    public function repeat($times): self
3984
    {
3985 1
        if ($times === 0) {
3986 1
            return static::create([], $this->iteratorClass);
3987
        }
3988
3989 1
        return static::create(
3990 1
            \array_fill(0, (int) $times, $this->getArray()),
3991 1
            $this->iteratorClass,
3992 1
            false
3993
        );
3994
    }
3995
3996
    /**
3997
     * Replace a key with a new key/value pair.
3998
     *
3999
     * @param mixed $replace
4000
     * @param mixed $key
4001
     * @param mixed $value
4002
     *
4003
     * @return static
4004
     *                <p>(Immutable)</p>
4005
     */
4006 2
    public function replace($replace, $key, $value): self
4007
    {
4008 2
        $that = clone $this;
4009
4010 2
        return $that->remove($replace)
4011 2
            ->set($key, $value);
4012
    }
4013
4014
    /**
4015
     * Create an array using the current array as values and the other array as keys.
4016
     *
4017
     * @param array $keys <p>An array of keys.</p>
4018
     *
4019
     * @return static
4020
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
4021
     *
4022
     * @psalm-param array<mixed,mixed>|array<mixed,TKey> $keys
4023
     */
4024 2
    public function replaceAllKeys(array $keys): self
4025
    {
4026 2
        return static::create(
4027 2
            \array_combine($keys, $this->getArray()),
4028 2
            $this->iteratorClass,
4029 2
            false
4030
        );
4031
    }
4032
4033
    /**
4034
     * Create an array using the current array as keys and the other array as values.
4035
     *
4036
     * @param array $array <p>An array o values.</p>
4037
     *
4038
     * @return static
4039
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
4040
     *
4041
     * @psalm-param array<mixed,T> $array
4042
     */
4043 2
    public function replaceAllValues(array $array): self
4044
    {
4045 2
        return static::create(
4046 2
            \array_combine($this->array, $array),
4047 2
            $this->iteratorClass,
4048 2
            false
4049
        );
4050
    }
4051
4052
    /**
4053
     * Replace the keys in an array with another set.
4054
     *
4055
     * @param array $keys <p>An array of keys matching the array's size</p>
4056
     *
4057
     * @return static
4058
     *                <p>(Immutable)</p>
4059
     *
4060
     * @psalm-param array<mixed,mixed>|array<mixed,TKey> $keys
4061
     */
4062 1
    public function replaceKeys(array $keys): self
4063
    {
4064 1
        $values = \array_values($this->getArray());
4065 1
        $result = \array_combine($keys, $values);
4066
4067 1
        return static::create(
4068 1
            $result,
4069 1
            $this->iteratorClass,
4070 1
            false
4071
        );
4072
    }
4073
4074
    /**
4075
     * Replace the first matched value in an array.
4076
     *
4077
     * @param mixed $search      <p>The value to replace.</p>
4078
     * @param mixed $replacement <p>The value to replace.</p>
4079
     *
4080
     * @return static
4081
     *                <p>(Immutable)</p>
4082
     */
4083 3
    public function replaceOneValue($search, $replacement = ''): self
4084
    {
4085 3
        $array = $this->getArray();
4086 3
        $key = \array_search($search, $array, true);
4087
4088 3
        if ($key !== false) {
4089 3
            $array[$key] = $replacement;
4090
        }
4091
4092 3
        return static::create(
4093 3
            $array,
4094 3
            $this->iteratorClass,
4095 3
            false
4096
        );
4097
    }
4098
4099
    /**
4100
     * Replace values in the current array.
4101
     *
4102
     * @param mixed $search      <p>The value to replace.</p>
4103
     * @param mixed $replacement <p>What to replace it with.</p>
4104
     *
4105
     * @return static
4106
     *                <p>(Immutable)</p>
4107
     */
4108 1
    public function replaceValues($search, $replacement = ''): self
4109
    {
4110 1
        return $this->each(
4111
            static function ($value) use ($search, $replacement) {
4112 1
                return \str_replace($search, $replacement, $value);
4113 1
            }
4114
        );
4115
    }
4116
4117
    /**
4118
     * Get the last elements from index $from until the end of this array.
4119
     *
4120
     * @param int $from
4121
     *
4122
     * @return static
4123
     *                <p>(Immutable)</p>
4124
     */
4125 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...
4126
    {
4127 15
        $tmpArray = $this->getArray();
4128
4129 15
        return static::create(
4130 15
            \array_splice($tmpArray, $from),
4131 15
            $this->iteratorClass,
4132 15
            false
4133
        );
4134
    }
4135
4136
    /**
4137
     * Return the array in the reverse order.
4138
     *
4139
     * @return static
4140
     *                <p>(Mutable) Return this Arrayy object.</p>
4141
     */
4142 9
    public function reverse(): self
4143
    {
4144 9
        $this->generatorToArray();
4145
4146 9
        $this->array = \array_reverse($this->array);
4147
4148 9
        return $this;
4149
    }
4150
4151
    /**
4152
     * Sort an array in reverse order.
4153
     *
4154
     * @param int $sort_flags [optional] <p>
4155
     *                        You may modify the behavior of the sort using the optional
4156
     *                        parameter sort_flags, for details
4157
     *                        see sort.
4158
     *                        </p>
4159
     *
4160
     * @return static
4161
     *                <p>(Mutable) Return this Arrayy object.</p>
4162
     */
4163 4
    public function rsort(int $sort_flags = 0): self
4164
    {
4165 4
        $this->generatorToArray();
4166
4167 4
        \rsort($this->array, $sort_flags);
4168
4169 4
        return $this;
4170
    }
4171
4172
    /**
4173
     * Search for the first index of the current array via $value.
4174
     *
4175
     * @param mixed $value
4176
     *
4177
     * @return false|float|int|string
4178
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4179
     */
4180 21
    public function searchIndex($value)
4181
    {
4182 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4183 20
            if ($value === $valueFromArray) {
4184 10
                return $keyFromArray;
4185
            }
4186
        }
4187
4188 11
        return false;
4189
    }
4190
4191
    /**
4192
     * Search for the value of the current array via $index.
4193
     *
4194
     * @param mixed $index
4195
     *
4196
     * @return static
4197
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4198
     */
4199 9
    public function searchValue($index): self
4200
    {
4201 9
        $this->generatorToArray();
4202
4203
        // init
4204 9
        $return = [];
4205
4206 9
        if ($this->array === []) {
4207
            return static::create(
4208
                [],
4209
                $this->iteratorClass,
4210
                false
4211
            );
4212
        }
4213
4214
        // php cast "bool"-index into "int"-index
4215 9
        if ((bool) $index === $index) {
4216 1
            $index = (int) $index;
4217
        }
4218
4219 9
        if ($this->offsetExists($index)) {
4220 7
            $return = [$this->array[$index]];
4221
        }
4222
4223 9
        return static::create(
4224 9
            $return,
4225 9
            $this->iteratorClass,
4226 9
            false
4227
        );
4228
    }
4229
4230
    /**
4231
     * Set a value for the current array (optional using dot-notation).
4232
     *
4233
     * @param string $key   <p>The key to set.</p>
4234
     * @param mixed  $value <p>Its value.</p>
4235
     *
4236
     * @return static
4237
     *                <p>(Mutable)</p>
4238
     */
4239 18
    public function set($key, $value): self
4240
    {
4241 18
        $this->generatorToArray();
4242
4243 18
        $this->internalSet($key, $value);
4244
4245 18
        return $this;
4246
    }
4247
4248
    /**
4249
     * Get a value from a array and set it if it was not.
4250
     *
4251
     * WARNING: this method only set the value, if the $key is not already set
4252
     *
4253
     * @param mixed $key      <p>The key</p>
4254
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4255
     *
4256
     * @return mixed
4257
     *               <p>(Mutable)</p>
4258
     */
4259 11
    public function setAndGet($key, $fallback = null)
4260
    {
4261 11
        $this->generatorToArray();
4262
4263
        // If the key doesn't exist, set it.
4264 11
        if (!$this->has($key)) {
4265 4
            $this->array = $this->set($key, $fallback)->getArray();
4266
        }
4267
4268 11
        return $this->get($key);
4269
    }
4270
4271
    /**
4272
     * Shifts a specified value off the beginning of array.
4273
     *
4274
     * @return mixed
4275
     *               <p>(Mutable) A shifted element from the current array.</p>
4276
     */
4277 5
    public function shift()
4278
    {
4279 5
        $this->generatorToArray();
4280
4281 5
        return \array_shift($this->array);
4282
    }
4283
4284
    /**
4285
     * Shuffle the current array.
4286
     *
4287
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4288
     * @param array $array  [optional]
4289
     *
4290
     * @return static
4291
     *                <p>(Immutable)</p>
4292
     *
4293
     * @psalm-param array<mixed,mixed>|array<TKey,T> $array
4294
     */
4295 2
    public function shuffle(bool $secure = false, array $array = null): self
4296
    {
4297 2
        if ($array === null) {
4298 2
            $array = $this->getArray();
4299
        }
4300
4301 2
        if ($secure !== true) {
4302
            /** @noinspection NonSecureShuffleUsageInspection */
4303 2
            \shuffle($array);
4304
        } else {
4305 1
            $size = \count($array, \COUNT_NORMAL);
4306 1
            $keys = \array_keys($array);
4307 1
            for ($i = $size - 1; $i > 0; --$i) {
4308
                try {
4309 1
                    $r = \random_int(0, $i);
4310
                } catch (\Exception $e) {
4311
                    /** @noinspection RandomApiMigrationInspection */
4312
                    $r = \mt_rand(0, $i);
4313
                }
4314 1
                if ($r !== $i) {
4315 1
                    $temp = $array[$keys[$r]];
4316 1
                    $array[$keys[$r]] = $array[$keys[$i]];
4317 1
                    $array[$keys[$i]] = $temp;
4318
                }
4319
            }
4320
4321
            // reset indices
4322 1
            $array = \array_values($array);
4323
        }
4324
4325 2
        foreach ($array as $key => $value) {
4326
            // check if recursive is needed
4327 2
            if (\is_array($value) === true) {
4328
                $array[$key] = $this->shuffle($secure, $value);
4329
            }
4330
        }
4331
4332 2
        return static::create(
4333 2
            $array,
4334 2
            $this->iteratorClass,
4335 2
            false
4336
        );
4337
    }
4338
4339
    /**
4340
     * Count the values from the current array.
4341
     *
4342
     * alias: for "Arrayy->count()"
4343
     *
4344
     * @param int $mode
4345
     *
4346
     * @return int
4347
     */
4348 20
    public function size(int $mode = \COUNT_NORMAL): int
4349
    {
4350 20
        return $this->count($mode);
4351
    }
4352
4353
    /**
4354
     * Checks whether array has exactly $size items.
4355
     *
4356
     * @param int $size
4357
     *
4358
     * @return bool
4359
     */
4360 1 View Code Duplication
    public function sizeIs(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...
4361
    {
4362
        // init
4363 1
        $itemsTempCount = 0;
4364
4365 1
        foreach ($this->getGenerator() as $key => $value) {
4366 1
            ++$itemsTempCount;
4367 1
            if ($itemsTempCount > $size) {
4368 1
                return false;
4369
            }
4370
        }
4371
4372 1
        return $itemsTempCount === $size;
4373
    }
4374
4375
    /**
4376
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4377
     * smaller than $fromSize.
4378
     *
4379
     * @param int $fromSize
4380
     * @param int $toSize
4381
     *
4382
     * @return bool
4383
     */
4384 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4385
    {
4386 1
        if ($fromSize > $toSize) {
4387 1
            $tmp = $toSize;
4388 1
            $toSize = $fromSize;
4389 1
            $fromSize = $tmp;
4390
        }
4391
4392
        // init
4393 1
        $itemsTempCount = 0;
4394
4395 1
        foreach ($this->getGenerator() as $key => $value) {
4396 1
            ++$itemsTempCount;
4397 1
            if ($itemsTempCount > $toSize) {
4398 1
                return false;
4399
            }
4400
        }
4401
4402 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4403
    }
4404
4405
    /**
4406
     * Checks whether array has more than $size items.
4407
     *
4408
     * @param int $size
4409
     *
4410
     * @return bool
4411
     */
4412 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...
4413
    {
4414
        // init
4415 1
        $itemsTempCount = 0;
4416
4417 1
        foreach ($this->getGenerator() as $key => $value) {
4418 1
            ++$itemsTempCount;
4419 1
            if ($itemsTempCount > $size) {
4420 1
                return true;
4421
            }
4422
        }
4423
4424 1
        return $itemsTempCount > $size;
4425
    }
4426
4427
    /**
4428
     * Checks whether array has less than $size items.
4429
     *
4430
     * @param int $size
4431
     *
4432
     * @return bool
4433
     */
4434 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...
4435
    {
4436
        // init
4437 1
        $itemsTempCount = 0;
4438
4439 1
        foreach ($this->getGenerator() as $key => $value) {
4440 1
            ++$itemsTempCount;
4441 1
            if ($itemsTempCount > $size) {
4442 1
                return false;
4443
            }
4444
        }
4445
4446 1
        return $itemsTempCount < $size;
4447
    }
4448
4449
    /**
4450
     * Counts all elements in an array, or something in an object.
4451
     *
4452
     * <p>
4453
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4454
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4455
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4456
     * implemented and used in PHP.
4457
     * </p>
4458
     *
4459
     * @return int
4460
     *             <p>
4461
     *             The number of elements in var, which is
4462
     *             typically an array, since anything else will have one
4463
     *             element.
4464
     *             </p>
4465
     *             <p>
4466
     *             If var is not an array or an object with
4467
     *             implemented Countable interface,
4468
     *             1 will be returned.
4469
     *             There is one exception, if var is &null;,
4470
     *             0 will be returned.
4471
     *             </p>
4472
     *             <p>
4473
     *             Caution: count may return 0 for a variable that isn't set,
4474
     *             but it may also return 0 for a variable that has been initialized with an
4475
     *             empty array. Use isset to test if a variable is set.
4476
     *             </p>
4477
     */
4478 10
    public function sizeRecursive(): int
4479
    {
4480 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4481
    }
4482
4483
    /**
4484
     * Extract a slice of the array.
4485
     *
4486
     * @param int      $offset       <p>Slice begin index.</p>
4487
     * @param int|null $length       <p>Length of the slice.</p>
4488
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4489
     *
4490
     * @return static
4491
     *                <p>A slice of the original array with length $length.</p>
4492
     */
4493 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
4494
    {
4495 5
        return static::create(
4496 5
            \array_slice(
4497 5
                $this->getArray(),
4498 5
                $offset,
4499 5
                $length,
4500 5
                $preserveKeys
4501
            ),
4502 5
            $this->iteratorClass,
4503 5
            false
4504
        );
4505
    }
4506
4507
    /**
4508
     * Sort the current array and optional you can keep the keys.
4509
     *
4510
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4511
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4512
     *                              <strong>SORT_NATURAL</strong></p>
4513
     * @param bool       $keepKeys
4514
     *
4515
     * @return static
4516
     *                <p>(Mutable) Return this Arrayy object.</p>
4517
     */
4518 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4519
    {
4520 20
        $this->generatorToArray();
4521
4522 20
        return $this->sorting(
4523 20
            $this->array,
4524 20
            $direction,
4525 20
            $strategy,
4526 20
            $keepKeys
4527
        );
4528
    }
4529
4530
    /**
4531
     * Sort the current array by key.
4532
     *
4533
     * @see http://php.net/manual/en/function.ksort.php
4534
     * @see http://php.net/manual/en/function.krsort.php
4535
     *
4536
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4537
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4538
     *                              <strong>SORT_NATURAL</strong></p>
4539
     *
4540
     * @return static
4541
     *                <p>(Mutable) Return this Arrayy object.</p>
4542
     */
4543 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4544
    {
4545 18
        $this->generatorToArray();
4546
4547 18
        $this->sorterKeys($this->array, $direction, $strategy);
4548
4549 18
        return $this;
4550
    }
4551
4552
    /**
4553
     * Sort the current array by value.
4554
     *
4555
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4556
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4557
     *                              <strong>SORT_NATURAL</strong></p>
4558
     *
4559
     * @return static
4560
     *                <p>(Mutable)</p>
4561
     */
4562 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4563
    {
4564 1
        return $this->sort($direction, $strategy, true);
4565
    }
4566
4567
    /**
4568
     * Sort the current array by value.
4569
     *
4570
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4571
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4572
     *                              <strong>SORT_NATURAL</strong></p>
4573
     *
4574
     * @return static
4575
     *                <p>(Mutable)</p>
4576
     */
4577 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4578
    {
4579 1
        return $this->sort($direction, $strategy, false);
4580
    }
4581
4582
    /**
4583
     * Sort a array by value, by a closure or by a property.
4584
     *
4585
     * - If the sorter is null, the array is sorted naturally.
4586
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4587
     *
4588
     * @param callable|string|null $sorter
4589
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4590
     *                                        <strong>SORT_DESC</strong></p>
4591
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4592
     *                                        <strong>SORT_NATURAL</strong></p>
4593
     *
4594
     * @return static
4595
     *                <p>(Immutable)</p>
4596
     */
4597 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4598
    {
4599 1
        $array = $this->getArray();
4600 1
        $direction = $this->getDirection($direction);
4601
4602
        // Transform all values into their results.
4603 1
        if ($sorter) {
4604 1
            $arrayy = static::create(
4605 1
                $array,
4606 1
                $this->iteratorClass,
4607 1
                false
4608
            );
4609
4610 1
            $results = $arrayy->each(
4611
                function ($value) use ($sorter) {
4612 1
                    if (\is_callable($sorter) === true) {
4613 1
                        return $sorter($value);
4614
                    }
4615
4616 1
                    return $this->get($sorter);
4617 1
                }
4618
            );
4619
4620 1
            $results = $results->getArray();
4621
        } else {
4622 1
            $results = $array;
4623
        }
4624
4625
        // Sort by the results and replace by original values
4626 1
        \array_multisort($results, $direction, $strategy, $array);
4627
4628 1
        return static::create(
4629 1
            $array,
4630 1
            $this->iteratorClass,
4631 1
            false
4632
        );
4633
    }
4634
4635
    /**
4636
     * @param int      $offset
4637
     * @param int|null $length
4638
     * @param array    $replacement
4639
     *
4640
     * @return static
4641
     *                <p>(Immutable)</p>
4642
     *
4643
     * @psalm-param array<mixed,mixed>|array<mixed,T> $replacement
4644
     */
4645 1
    public function splice(int $offset, int $length = null, $replacement = []): self
4646
    {
4647 1
        $tmpArray = $this->getArray();
4648
4649 1
        \array_splice(
4650 1
            $tmpArray,
4651 1
            $offset,
4652 1
            $length ?? $this->count(),
4653 1
            $replacement
4654
        );
4655
4656 1
        return static::create(
4657 1
            $tmpArray,
4658 1
            $this->iteratorClass,
4659 1
            false
4660
        );
4661
    }
4662
4663
    /**
4664
     * Split an array in the given amount of pieces.
4665
     *
4666
     * @param int  $numberOfPieces
4667
     * @param bool $keepKeys
4668
     *
4669
     * @return static
4670
     *                <p>(Immutable)</p>
4671
     */
4672 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4673
    {
4674 1
        $this->generatorToArray();
4675
4676 1
        $count = $this->count();
4677
4678 1
        if ($count === 0) {
4679 1
            $result = [];
4680
        } else {
4681 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
4682 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4683
        }
4684
4685 1
        return static::create(
4686 1
            $result,
4687 1
            $this->iteratorClass,
4688 1
            false
4689
        );
4690
    }
4691
4692
    /**
4693
     * Stripe all empty items.
4694
     *
4695
     * @return static
4696
     *                <p>(Immutable)</p>
4697
     */
4698 1
    public function stripEmpty(): self
4699
    {
4700 1
        return $this->filter(
4701
            static function ($item) {
4702 1
                if ($item === null) {
4703 1
                    return false;
4704
                }
4705
4706 1
                return (bool) \trim((string) $item);
4707 1
            }
4708
        );
4709
    }
4710
4711
    /**
4712
     * Swap two values between positions by key.
4713
     *
4714
     * @param int|string $swapA <p>a key in the array</p>
4715
     * @param int|string $swapB <p>a key in the array</p>
4716
     *
4717
     * @return static
4718
     *                <p>(Immutable)</p>
4719
     */
4720 1
    public function swap($swapA, $swapB): self
4721
    {
4722 1
        $array = $this->getArray();
4723
4724 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4725
4726 1
        return static::create(
4727 1
            $array,
4728 1
            $this->iteratorClass,
4729 1
            false
4730
        );
4731
    }
4732
4733
    /**
4734
     * alias: for "Arrayy->getArray()"
4735
     *
4736
     * @param bool $convertAllArrayyElements
4737
     *
4738
     * @return array
4739
     *
4740
     * @see          Arrayy::getArray()
4741
     *
4742
     * @psalm-return array<array-key,mixed>
4743
     */
4744 241
    public function toArray(bool $convertAllArrayyElements = false): array
4745
    {
4746 241
        return $this->getArray($convertAllArrayyElements);
4747
    }
4748
4749
    /**
4750
     * Convert the current array to JSON.
4751
     *
4752
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4753
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4754
     *
4755
     * @return string
4756
     */
4757 7
    public function toJson(int $options = 0, int $depth = 512): string
4758
    {
4759 7
        $return = \json_encode($this->getArray(), $options, $depth);
4760 7
        if ($return === false) {
4761
            return '';
4762
        }
4763
4764 7
        return $return;
4765
    }
4766
4767
    /**
4768
     * @param string[]|null $items
4769
     * @param string[]      $helper
4770
     *
4771
     * @return static
4772
     */
4773 1
    public function toPermutation(array $items = null, array $helper = []): self
4774
    {
4775
        // init
4776 1
        $return = [];
4777
4778 1
        if ($items === null) {
4779 1
            $items = $this->getArray();
4780
        }
4781
4782 1
        if (empty($items)) {
4783 1
            $return[] = $helper;
4784
        } else {
4785 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
4786 1
                $new_items = $items;
4787 1
                $new_helper = $helper;
4788 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
4789
                /** @noinspection PhpSillyAssignmentInspection */
4790
                /** @var string[] $new_items */
4791 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...
4792 1
                \array_unshift($new_helper, $tmp_helper);
4793
                /** @noinspection SlowArrayOperationsInLoopInspection */
4794 1
                $return = \array_merge(
4795 1
                    $return,
4796 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
4797
                );
4798
            }
4799
        }
4800
4801 1
        return static::create(
4802 1
            $return,
4803 1
            $this->iteratorClass,
4804 1
            false
4805
        );
4806
    }
4807
4808
    /**
4809
     * Implodes array to a string with specified separator.
4810
     *
4811
     * @param string $separator [optional] <p>The element's separator.</p>
4812
     *
4813
     * @return string
4814
     *                <p>The string representation of array, separated by ",".</p>
4815
     */
4816 19
    public function toString(string $separator = ','): string
4817
    {
4818 19
        return $this->implode($separator);
4819
    }
4820
4821
    /**
4822
     * Return a duplicate free copy of the current array.
4823
     *
4824
     * @return static
4825
     *                <p>(Mutable)</p>
4826
     */
4827 13
    public function unique(): self
4828
    {
4829
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4830
4831 13
        $this->array = $this->reduce(
4832
            static function ($resultArray, $value) {
4833 12
                if (!\in_array($value, $resultArray, true)) {
4834 12
                    $resultArray[] = $value;
4835
                }
4836
4837 12
                return $resultArray;
4838 13
            },
4839 13
            []
4840 13
        )->getArray();
4841 13
        $this->generator = null;
4842
4843 13
        return $this;
4844
    }
4845
4846
    /**
4847
     * Return a duplicate free copy of the current array. (with the old keys)
4848
     *
4849
     * @return static
4850
     *                <p>(Mutable)</p>
4851
     */
4852 11
    public function uniqueKeepIndex(): self
4853
    {
4854
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4855
4856
        // init
4857 11
        $array = $this->getArray();
4858
4859 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...
4860 11
            \array_keys($array),
4861
            static function ($resultArray, $key) use ($array) {
4862 10
                if (!\in_array($array[$key], $resultArray, true)) {
4863 10
                    $resultArray[$key] = $array[$key];
4864
                }
4865
4866 10
                return $resultArray;
4867 11
            },
4868 11
            []
4869
        );
4870 11
        $this->generator = null;
4871
4872 11
        if ($this->array === null) {
4873
            $this->array = [];
4874
        } else {
4875 11
            $this->array = (array) $this->array;
4876
        }
4877
4878 11
        return $this;
4879
    }
4880
4881
    /**
4882
     * alias: for "Arrayy->unique()"
4883
     *
4884
     * @return static
4885
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4886
     *
4887
     * @see Arrayy::unique()
4888
     */
4889 10
    public function uniqueNewIndex(): self
4890
    {
4891 10
        return $this->unique();
4892
    }
4893
4894
    /**
4895
     * Prepends one or more values to the beginning of array at once.
4896
     *
4897
     * @param array ...$args
4898
     *
4899
     * @return static
4900
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4901
     *
4902
     * @psalm-param array<mixed,mixed>|array<TKey,T> ...$args
4903
     */
4904 4
    public function unshift(...$args): self
4905
    {
4906 4
        $this->generatorToArray();
4907
4908 4
        \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4909
4910 4
        return $this;
4911
    }
4912
4913
    /**
4914
     * Tests whether the given closure retrun something valid for all elements of this array.
4915
     *
4916
     * @param \Closure $closure the predicate
4917
     *
4918
     * @return bool
4919
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
4920
     */
4921 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...
4922
    {
4923 1
        foreach ($this->getGenerator() as $key => $value) {
4924 1
            if (!$closure($value, $key)) {
4925 1
                return false;
4926
            }
4927
        }
4928
4929 1
        return true;
4930
    }
4931
4932
    /**
4933
     * Get all values from a array.
4934
     *
4935
     * @return static
4936
     *                <p>(Immutable)</p>
4937
     */
4938 2
    public function values(): self
4939
    {
4940 2
        return static::create(
4941
            function () {
4942
                /** @noinspection YieldFromCanBeUsedInspection */
4943 2
                foreach ($this->getGenerator() as $value) {
4944 2
                    yield $value;
4945
                }
4946 2
            },
4947 2
            $this->iteratorClass,
4948 2
            false
4949
        );
4950
    }
4951
4952
    /**
4953
     * Apply the given function to every element in the array, discarding the results.
4954
     *
4955
     * @param callable $callable
4956
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4957
     *
4958
     * @return static
4959
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4960
     */
4961 12
    public function walk($callable, bool $recursive = false): self
4962
    {
4963 12
        $this->generatorToArray();
4964
4965 12
        if ($recursive === true) {
4966 6
            \array_walk_recursive($this->array, $callable);
4967
        } else {
4968 6
            \array_walk($this->array, $callable);
4969
        }
4970
4971 12
        return $this;
4972
    }
4973
4974
    /**
4975
     * Returns a collection of matching items.
4976
     *
4977
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
4978
     * @param mixed  $value                 the value to match
4979
     *
4980
     * @throws \InvalidArgumentException if property or method is not defined
4981
     *
4982
     * @return static
4983
     *
4984
     * @psalm-return static<T>
4985
     */
4986 1
    public function where(string $keyOrPropertyOrMethod, $value): self
4987
    {
4988 1
        return $this->filter(
4989
            function ($item) use ($keyOrPropertyOrMethod, $value) {
4990 1
                $accessorValue = $this->extractValue(
4991 1
                    $item,
4992 1
                    $keyOrPropertyOrMethod
4993
                );
4994
4995 1
                return $accessorValue === $value;
4996 1
            }
4997
        );
4998
    }
4999
5000
    /**
5001
     * Convert an array into a object.
5002
     *
5003
     * @param array $array
5004
     *
5005
     * @return \stdClass
5006
     *
5007
     * @psalm-param array<mixed,mixed> $array
5008
     */
5009 4
    final protected static function arrayToObject(array $array = []): \stdClass
5010
    {
5011
        // init
5012 4
        $object = new \stdClass();
5013
5014 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
5015 1
            return $object;
5016
        }
5017
5018 3
        foreach ($array as $name => $value) {
5019 3
            if (\is_array($value) === true) {
5020 1
                $object->{$name} = static::arrayToObject($value);
5021
            } else {
5022 3
                $object->{$name} = $value;
5023
            }
5024
        }
5025
5026 3
        return $object;
5027
    }
5028
5029
    /**
5030
     * @param array|\Generator|null $input         <p>
5031
     *                                             An array containing keys to return.
5032
     *                                             </p>
5033
     * @param mixed|null            $search_values [optional] <p>
5034
     *                                             If specified, then only keys containing these values are returned.
5035
     *                                             </p>
5036
     * @param bool                  $strict        [optional] <p>
5037
     *                                             Determines if strict comparison (===) should be used during the
5038
     *                                             search.
5039
     *                                             </p>
5040
     *
5041
     * @return array
5042
     *               <p>an array of all the keys in input</p>
5043
     *
5044
     * @psalm-param  array<mixed,mixed>|\Generator<TKey,T>|null $input
5045
     * @psalm-return array<TKey|mixed>
5046
     */
5047 11
    protected function array_keys_recursive(
5048
        $input = null,
5049
        $search_values = null,
5050
        bool $strict = true
5051
    ): array {
5052
        // init
5053 11
        $keys = [];
5054 11
        $keysTmp = [];
5055
5056 11
        if ($input === null) {
5057 4
            $input = $this->getGenerator();
5058
        }
5059
5060 11
        if ($search_values === null) {
5061 11
            foreach ($input as $key => $value) {
5062 11
                $keys[] = $key;
5063
5064
                // check if recursive is needed
5065 11
                if (\is_array($value) === true) {
5066 4
                    $keysTmp[] = $this->array_keys_recursive($value);
5067
                }
5068
            }
5069
        } else {
5070 1
            $is_array_tmp = \is_array($search_values);
5071
5072 1
            foreach ($input as $key => $value) {
5073 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...
5074
                    (
5075 1
                        $is_array_tmp === false
5076
                        &&
5077 1
                        $strict === true
5078
                        &&
5079 1
                        $search_values === $value
5080
                    )
5081
                    ||
5082
                    (
5083 1
                        $is_array_tmp === false
5084
                        &&
5085 1
                        $strict === false
5086
                        &&
5087 1
                        $search_values == $value
5088
                    )
5089
                    ||
5090
                    (
5091 1
                        $is_array_tmp === true
5092
                        &&
5093 1
                        \in_array($value, $search_values, $strict)
5094
                    )
5095
                ) {
5096 1
                    $keys[] = $key;
5097
                }
5098
5099
                // check if recursive is needed
5100 1
                if (\is_array($value) === true) {
5101 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5102
                }
5103
            }
5104
        }
5105
5106 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5107
    }
5108
5109
    /**
5110
     * @param mixed      $path
5111
     * @param callable   $callable
5112
     * @param array|null $currentOffset
5113
     *
5114
     * @return void
5115
     *
5116
     * @psalm-param array<mixed,mixed>|array<TKey,T>|null $currentOffset
5117
     */
5118 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5119
    {
5120 4
        $this->generatorToArray();
5121
5122 4
        if ($currentOffset === null) {
5123 4
            $currentOffset = &$this->array;
5124
        }
5125
5126 4
        $explodedPath = \explode($this->pathSeparator, $path);
5127 4
        if ($explodedPath === false) {
5128
            return;
5129
        }
5130
5131 4
        $nextPath = \array_shift($explodedPath);
5132
5133 4
        if (!isset($currentOffset[$nextPath])) {
5134
            return;
5135
        }
5136
5137 4
        if (!empty($explodedPath)) {
5138 1
            $this->callAtPath(
5139 1
                \implode($this->pathSeparator, $explodedPath),
5140 1
                $callable,
5141 1
                $currentOffset[$nextPath]
5142
            );
5143
        } else {
5144 4
            $callable($currentOffset[$nextPath]);
5145
        }
5146 4
    }
5147
5148
    /**
5149
     * Extracts the value of the given property or method from the object.
5150
     *
5151
     * @param static $object                <p>The object to extract the value from.</p>
5152
     * @param string $keyOrPropertyOrMethod <p>The property or method for which the
5153
     *                                      value should be extracted.</p>
5154
     *
5155
     * @throws \InvalidArgumentException if the method or property is not defined
5156
     *
5157
     * @return mixed
5158
     *               <p>The value extracted from the specified property or method.</p>
5159
     *
5160
     * @psalm-param self<TKey,T> $object
5161
     */
5162 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5163
    {
5164 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5165 2
            $return = $object->get($keyOrPropertyOrMethod);
5166
5167 2
            if ($return instanceof self) {
5168 1
                return $return->getArray();
5169
            }
5170
5171 1
            return $return;
5172
        }
5173
5174
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5175
            return $object->{$keyOrPropertyOrMethod};
5176
        }
5177
5178
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5179
            return $object->{$keyOrPropertyOrMethod}();
5180
        }
5181
5182
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
5183
    }
5184
5185
    /**
5186
     * create a fallback for array
5187
     *
5188
     * 1. use the current array, if it's a array
5189
     * 2. fallback to empty array, if there is nothing
5190
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
5191
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
5192
     * 5. call "__toArray()" on object, if the method exists
5193
     * 6. cast a string or object with "__toString()" into an array
5194
     * 7. throw a "InvalidArgumentException"-Exception
5195
     *
5196
     * @param mixed $data
5197
     *
5198
     * @throws \InvalidArgumentException
5199
     *
5200
     * @return array
5201
     *
5202
     * @psalm-return array<mixed,mixed>|array<TKey,T>
5203
     */
5204 1066
    protected function fallbackForArray(&$data): array
5205
    {
5206 1066
        $data = $this->internalGetArray($data);
5207
5208 1066
        if ($data === null) {
5209 2
            throw new \InvalidArgumentException('Passed value should be a array');
5210
        }
5211
5212 1064
        return $data;
5213
    }
5214
5215
    /**
5216
     * @return bool
5217
     *
5218
     * @noinspection ReturnTypeCanBeDeclaredInspection
5219
     */
5220 982
    protected function generatorToArray()
5221
    {
5222 982
        if ($this->generator) {
5223 2
            $this->array = $this->getArray();
5224 2
            $this->generator = null;
5225
5226 2
            return true;
5227
        }
5228
5229 982
        return false;
5230
    }
5231
5232
    /**
5233
     * Get correct PHP constant for direction.
5234
     *
5235
     * @param int|string $direction
5236
     *
5237
     * @return int
5238
     */
5239 39
    protected function getDirection($direction): int
5240
    {
5241 39
        if ((string) $direction === $direction) {
5242 10
            $direction = \strtolower($direction);
5243
5244 10
            if ($direction === 'desc') {
5245 2
                $direction = \SORT_DESC;
5246
            } else {
5247 8
                $direction = \SORT_ASC;
5248
            }
5249
        }
5250
5251
        if (
5252 39
            $direction !== \SORT_DESC
5253
            &&
5254 39
            $direction !== \SORT_ASC
5255
        ) {
5256
            $direction = \SORT_ASC;
5257
        }
5258
5259 39
        return $direction;
5260
    }
5261
5262
    /**
5263
     * @return TypeCheckPhpDoc[]
5264
     *
5265
     * @noinspection ReturnTypeCanBeDeclaredInspection
5266
     */
5267 16
    protected function getPropertiesFromPhpDoc()
5268
    {
5269 16
        static $PROPERTY_CACHE = [];
5270 16
        $cacheKey = 'Class::' . static::class;
5271
5272 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5273 15
            return $PROPERTY_CACHE[$cacheKey];
5274
        }
5275
5276
        // init
5277 2
        $properties = [];
5278
5279 2
        $reflector = new \ReflectionClass($this);
5280 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5281 2
        $docComment = $reflector->getDocComment();
5282 2
        if ($docComment) {
5283 2
            $docblock = $factory->create($docComment);
5284
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5285 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5286 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
5287
            }
5288
        }
5289
5290 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5291
    }
5292
5293
    /**
5294
     * @param mixed $glue
5295
     * @param mixed $pieces
5296
     * @param bool  $useKeys
5297
     *
5298
     * @return string
5299
     */
5300 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
5301
    {
5302 36
        if ($pieces instanceof self) {
5303 1
            $pieces = $pieces->getArray();
5304
        }
5305
5306 36
        if (\is_array($pieces) === true) {
5307 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
5308 36
            $pieces_count_not_zero = $pieces_count > 0;
5309
5310 36
            return \implode(
5311 36
                $glue,
5312 36
                \array_map(
5313 36
                    [$this, 'implode_recursive'],
5314 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
5315 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
5316
                )
5317
            );
5318
        }
5319
5320
        if (
5321 36
            \is_scalar($pieces) === true
5322
            ||
5323 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
5324
        ) {
5325 32
            return (string) $pieces;
5326
        }
5327
5328 8
        return '';
5329
    }
5330
5331
    /**
5332
     * @param mixed                 $needle   <p>
5333
     *                                        The searched value.
5334
     *                                        </p>
5335
     *                                        <p>
5336
     *                                        If needle is a string, the comparison is done
5337
     *                                        in a case-sensitive manner.
5338
     *                                        </p>
5339
     * @param array|\Generator|null $haystack <p>
5340
     *                                        The array.
5341
     *                                        </p>
5342
     * @param bool                  $strict   [optional] <p>
5343
     *                                        If the third parameter strict is set to true
5344
     *                                        then the in_array function will also check the
5345
     *                                        types of the
5346
     *                                        needle in the haystack.
5347
     *                                        </p>
5348
     *
5349
     * @return bool
5350
     *              <p>true if needle is found in the array, false otherwise</p>
5351
     *
5352
     * @psalm-param array<mixed,mixed>|\Generator<TKey,T>|null $haystack
5353
     */
5354 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
5355
    {
5356 18
        if ($haystack === null) {
5357
            $haystack = $this->getGenerator();
5358
        }
5359
5360 18
        foreach ($haystack as $item) {
5361 14
            if (\is_array($item) === true) {
5362 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
5363
            } else {
5364
                /** @noinspection NestedPositiveIfStatementsInspection */
5365 14
                if ($strict === true) {
5366 14
                    $returnTmp = $item === $needle;
5367
                } else {
5368
                    $returnTmp = $item == $needle;
5369
                }
5370
            }
5371
5372 14
            if ($returnTmp === true) {
5373 10
                return true;
5374
            }
5375
        }
5376
5377 8
        return false;
5378
    }
5379
5380
    /**
5381
     * @param mixed $data
5382
     *
5383
     * @return array|null
5384
     *
5385
     * @psalm-return array<mixed,mixed>|array<TKey,T>|null
5386
     */
5387 1066
    protected function internalGetArray(&$data)
5388
    {
5389 1066
        if (\is_array($data) === true) {
5390 1063
            return $data;
5391
        }
5392
5393 52
        if (!$data) {
5394 6
            return [];
5395
        }
5396
5397 51
        if (\is_object($data) === true) {
5398 46
            if ($data instanceof \ArrayObject) {
5399 4
                return $data->getArrayCopy();
5400
            }
5401
5402 43
            if ($data instanceof \Generator) {
5403
                return static::createFromGeneratorImmutable($data)->getArray();
5404
            }
5405
5406 43
            if ($data instanceof \Traversable) {
5407
                return static::createFromObject($data)->getArray();
5408
            }
5409
5410 43
            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...
5411
                return (array) $data->jsonSerialize();
5412
            }
5413
5414 43
            if (\method_exists($data, '__toArray')) {
5415
                return (array) $data->__toArray();
5416
            }
5417
5418 43
            if (\method_exists($data, '__toString')) {
5419
                return [(string) $data];
5420
            }
5421
        }
5422
5423 48
        if (\is_callable($data)) {
5424 41
            $this->generator = new ArrayyRewindableGenerator($data);
5425
5426 41
            return [];
5427
        }
5428
5429 9
        if (\is_scalar($data)) {
5430 7
            return [$data];
5431
        }
5432
5433 2
        return null;
5434
    }
5435
5436
    /**
5437
     * Internal mechanics of remove method.
5438
     *
5439
     * @param mixed $key
5440
     *
5441
     * @return bool
5442
     */
5443 18
    protected function internalRemove($key): bool
5444
    {
5445 18
        $this->generatorToArray();
5446
5447
        if (
5448 18
            $this->pathSeparator
5449
            &&
5450 18
            (string) $key === $key
5451
            &&
5452 18
            \strpos($key, $this->pathSeparator) !== false
5453
        ) {
5454
            $path = \explode($this->pathSeparator, (string) $key);
5455
5456
            if ($path !== false) {
5457
                // crawl though the keys
5458
                while (\count($path, \COUNT_NORMAL) > 1) {
5459
                    $key = \array_shift($path);
5460
5461
                    if (!$this->has($key)) {
5462
                        return false;
5463
                    }
5464
5465
                    $this->array = &$this->array[$key];
5466
                }
5467
5468
                $key = \array_shift($path);
5469
            }
5470
        }
5471
5472 18
        unset($this->array[$key]);
5473
5474 18
        return true;
5475
    }
5476
5477
    /**
5478
     * Internal mechanic of set method.
5479
     *
5480
     * @param int|string|null $key
5481
     * @param mixed           $value
5482
     * @param bool            $checkProperties
5483
     *
5484
     * @return bool
5485
     */
5486 938
    protected function internalSet(
5487
        $key,
5488
        &$value,
5489
        bool $checkProperties = true
5490
    ): bool {
5491
        if (
5492 938
            $checkProperties === true
5493
            &&
5494 938
            $this->properties !== []
5495
        ) {
5496 87
            $this->checkType($key, $value);
5497
        }
5498
5499 936
        if ($key === null) {
5500
            return false;
5501
        }
5502
5503 936
        $this->generatorToArray();
5504
5505 936
        $array = &$this->array;
5506
5507
        if (
5508 936
            $this->pathSeparator
5509
            &&
5510 936
            (string) $key === $key
5511
            &&
5512 936
            \strpos($key, $this->pathSeparator) !== false
5513
        ) {
5514 3
            $path = \explode($this->pathSeparator, (string) $key);
5515
5516 3
            if ($path !== false) {
5517
                // crawl through the keys
5518 3
                while (\count($path, \COUNT_NORMAL) > 1) {
5519 3
                    $key = \array_shift($path);
5520
5521 3
                    $array = &$array[$key];
5522
                }
5523
5524 3
                $key = \array_shift($path);
5525
            }
5526
        }
5527
5528 936
        $array[$key] = $value;
5529
5530 936
        return true;
5531
    }
5532
5533
    /**
5534
     * Convert a object into an array.
5535
     *
5536
     * @param object $object
5537
     *
5538
     * @return mixed
5539
     */
5540 5
    protected static function objectToArray($object)
5541
    {
5542 5
        if (!\is_object($object)) {
5543 4
            return $object;
5544
        }
5545
5546 5
        if (\is_object($object)) {
5547 5
            $object = \get_object_vars($object);
5548
        }
5549
5550 5
        return \array_map(['static', 'objectToArray'], $object);
5551
    }
5552
5553
    /**
5554
     * @param array $data
5555
     * @param bool  $checkPropertiesInConstructor
5556
     *
5557
     * @return void
5558
     *
5559
     * @psalm-param array<mixed,T> $data
5560
     */
5561 1064
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
5562
    {
5563 1064
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
5564
                                        &&
5565 1064
                                        $checkPropertiesInConstructor === true;
5566
5567 1064
        if ($this->properties !== []) {
5568 78
            foreach ($data as $key => &$valueInner) {
5569 78
                $this->internalSet(
5570 78
                    $key,
5571 78
                    $valueInner,
5572 78
                    $checkPropertiesInConstructor
5573
                );
5574
            }
5575
        } else {
5576
            if (
5577 997
                $this->checkPropertyTypes === true
5578
                ||
5579 997
                $checkPropertiesInConstructor === true
5580
            ) {
5581 16
                $this->properties = $this->getPropertiesFromPhpDoc();
5582
            }
5583
5584
            if (
5585 997
                $this->checkPropertiesMismatchInConstructor === true
5586
                &&
5587 997
                \count($data) !== 0
5588
                &&
5589 997
                \count(\array_diff_key($this->properties, $data)) > 0
5590
            ) {
5591 1
                throw new \TypeError('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'Property mismatch - inp...his->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...
5592
            }
5593
5594 996
            foreach ($data as $key => &$valueInner) {
5595 864
                $this->internalSet(
5596 864
                    $key,
5597 864
                    $valueInner,
5598 864
                    $checkPropertiesInConstructor
5599
                );
5600
            }
5601
        }
5602 1057
    }
5603
5604
    /**
5605
     * sorting keys
5606
     *
5607
     * @param array      $elements
5608
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5609
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5610
     *                              <strong>SORT_NATURAL</strong></p>
5611
     *
5612
     * @return static
5613
     *                <p>(Mutable) Return this Arrayy object.</p>
5614
     *
5615
     * @psalm-param array<mixed,mixed>|array<mixed|TKey,T> $elements
5616
     */
5617 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5618
    {
5619 18
        $direction = $this->getDirection($direction);
5620
5621 18
        switch ($direction) {
5622 18
            case 'desc':
5623
            case \SORT_DESC:
5624 6
                \krsort($elements, $strategy);
5625
5626 6
                break;
5627 13
            case 'asc':
5628 13
            case \SORT_ASC:
5629
            default:
5630 13
                \ksort($elements, $strategy);
5631
        }
5632
5633 18
        return $this;
5634
    }
5635
5636
    /**
5637
     * @param array      $elements  <p>Warning: used as reference</p>
5638
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5639
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5640
     *                              <strong>SORT_NATURAL</strong></p>
5641
     * @param bool       $keepKeys
5642
     *
5643
     * @return static
5644
     *                <p>(Mutable) Return this Arrayy object.</p>
5645
     *
5646
     * @psalm-param array<mixed> $elements
5647
     */
5648 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5649
    {
5650 20
        $direction = $this->getDirection($direction);
5651
5652 20
        if (!$strategy) {
5653 20
            $strategy = \SORT_REGULAR;
5654
        }
5655
5656 20
        switch ($direction) {
5657 20
            case 'desc':
5658
            case \SORT_DESC:
5659 9
                if ($keepKeys) {
5660 5
                    \arsort($elements, $strategy);
5661
                } else {
5662 4
                    \rsort($elements, $strategy);
5663
                }
5664
5665 9
                break;
5666 11
            case 'asc':
5667 11
            case \SORT_ASC:
5668
            default:
5669 11
                if ($keepKeys) {
5670 4
                    \asort($elements, $strategy);
5671
                } else {
5672 7
                    \sort($elements, $strategy);
5673
                }
5674
        }
5675
5676 20
        return $this;
5677
    }
5678
5679
    /**
5680
     * @param int|string|null $key
5681
     * @param mixed           $value
5682
     *
5683
     * @return void
5684
     */
5685 87
    private function checkType($key, $value)
5686
    {
5687
        if (
5688 87
            $key !== null
5689
            &&
5690 87
            isset($this->properties[$key]) === false
5691
            &&
5692 87
            $this->checkPropertiesMismatch === true
5693
        ) {
5694
            throw new \TypeError('The key ' . $key . ' does not exists in "properties". Maybe because @property was not used for the class (' . \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 . ' do...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...
5695
        }
5696
5697 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
5698 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
5699 17
        } elseif ($key !== null && isset($this->properties[$key])) {
5700 17
            $this->properties[$key]->checkType($value);
5701
        }
5702 85
    }
5703
}
5704