Completed
Push — master ( 095ad5...549151 )
by Lars
01:40
created

Arrayy::nth()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 1
nop 2
dl 0
loc 19
ccs 11
cts 11
cp 1
crap 3
rs 9.6333
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 T
21
 */
22
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
23
{
24
    const ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES = '!!!!Arrayy_Helper_Types_For_All_Properties!!!!';
25
26
    /**
27
     * @var array
28
     */
29
    protected $array = [];
30
31
    /**
32
     * @var \Arrayy\ArrayyRewindableGenerator|null
33
     */
34
    protected $generator;
35
36
    /**
37
     * @var string
38
     *
39
     * @psalm-var class-string<\Arrayy\ArrayyIterator>
40
     */
41
    protected $iteratorClass = ArrayyIterator::class;
42
43
    /**
44
     * @var string
45
     */
46
    protected $pathSeparator = '.';
47
48
    /**
49
     * @var bool
50
     */
51
    protected $checkPropertyTypes = false;
52
53
    /**
54
     * @var bool
55
     */
56
    protected $checkForMissingPropertiesInConstructor = false;
57
58
    /**
59
     * @var bool
60
     */
61
    protected $checkPropertiesMismatchInConstructor = false;
62
63
    /**
64
     * @var bool
65
     */
66
    protected $checkPropertiesMismatch = true;
67
68
    /**
69
     * @var array<TypeCheckInterface>|\Arrayy\Type\TypeInterface|TypeCheckArray<TypeCheckInterface>
70
     */
71
    protected $properties = [];
72
73
    /**
74
     * Initializes
75
     *
76
     * @param mixed  $data                         <p>
77
     *                                             Should be an array or a generator, otherwise it will try
78
     *                                             to convert it into an array.
79
     *                                             </p>
80
     * @param string $iteratorClass                optional <p>
81
     *                                             You can overwrite the ArrayyIterator, but mostly you don't
82
     *                                             need this option.
83
     *                                             </p>
84
     * @param bool   $checkPropertiesInConstructor optional <p>
85
     *                                             You need to extend the "Arrayy"-class and you need to set
86
     *                                             the $checkPropertiesMismatchInConstructor class property
87
     *                                             to
88
     *                                             true, otherwise this option didn't not work anyway.
89
     *                                             </p>
90
     *
91
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
92
     */
93 1066
    public function __construct(
94
        $data = [],
95
        string $iteratorClass = ArrayyIterator::class,
96
        bool $checkPropertiesInConstructor = true
97
    ) {
98 1066
        $data = $this->fallbackForArray($data);
99
100
        // used only for serialize + unserialize, all other methods are overwritten
101 1064
        parent::__construct([], 0, $iteratorClass);
102
103 1064
        $this->setInitialValuesAndProperties($data, $checkPropertiesInConstructor);
104
105 1057
        $this->setIteratorClass($iteratorClass);
106 1057
    }
107
108
    /**
109
     * Call object as function.
110
     *
111
     * @param mixed $key
112
     *
113
     * @return mixed
114
     */
115 1
    public function __invoke($key = null)
116
    {
117 1
        if ($key !== null) {
118 1
            $this->generatorToArray();
119
120 1
            return $this->array[$key] ?? false;
121
        }
122
123
        return $this->getArray();
124
    }
125
126
    /**
127
     * Whether or not an element exists by key.
128
     *
129
     * @param mixed $key
130
     *
131
     * @return bool
132
     *              <p>True is the key/index exists, otherwise false.</p>
133
     */
134
    public function __isset($key): bool
135
    {
136
        return $this->offsetExists($key);
137
    }
138
139
    /**
140
     * Assigns a value to the specified element.
141
     *
142
     * @param mixed $key
143
     * @param mixed $value
144
     *
145
     * @return void
146
     */
147 2
    public function __set($key, $value)
148
    {
149 2
        $this->internalSet($key, $value);
150 2
    }
151
152
    /**
153
     * magic to string
154
     *
155
     * @return string
156
     */
157 15
    public function __toString(): string
158
    {
159 15
        return $this->toString();
160
    }
161
162
    /**
163
     * Unset element by key.
164
     *
165
     * @param mixed $key
166
     */
167
    public function __unset($key)
168
    {
169
        $this->internalRemove($key);
170
    }
171
172
    /**
173
     * Get a value by key.
174
     *
175
     * @param mixed $key
176
     *
177
     * @return mixed
178
     *               <p>Get a Value from the current array.</p>
179
     */
180 4
    public function &__get($key)
181
    {
182 4
        $return = $this->get($key);
183
184 4
        if (\is_array($return) === true) {
185
            return static::create($return, $this->iteratorClass, false);
186
        }
187
188 4
        return $return;
189
    }
190
191
    /**
192
     * alias: for "Arrayy->append()"
193
     *
194
     * @param mixed $value
195
     *
196
     * @return static
197
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
198
     *
199
     * @see Arrayy::append()
200
     */
201 3
    public function add($value)
202
    {
203 3
        return $this->append($value);
204
    }
205
206
    /**
207
     * Append a (key) + value to the current array.
208
     *
209
     * @param mixed $value
210
     * @param mixed $key
211
     *
212
     * @return static
213
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
214
     */
215 13
    public function append($value, $key = null): self
216
    {
217 13
        $this->generatorToArray();
218
219 13
        if ($this->properties !== []) {
220 4
            $this->checkType($key, $value);
221
        }
222
223 12
        if ($key !== null) {
224
            if (
225
                isset($this->array[$key])
226
                &&
227
                \is_array($this->array[$key]) === true
228
            ) {
229
                $this->array[$key][] = $value;
230
            } else {
231
                $this->array[$key] = $value;
232
            }
233
        } else {
234 12
            $this->array[] = $value;
235
        }
236
237 12
        return $this;
238
    }
239
240
    /**
241
     * Sort the entries by value.
242
     *
243
     * @param int $sort_flags [optional] <p>
244
     *                        You may modify the behavior of the sort using the optional
245
     *                        parameter sort_flags, for details
246
     *                        see sort.
247
     *                        </p>
248
     *
249
     * @return static
250
     *                <p>(Mutable) Return this Arrayy object.</p>
251
     */
252 4
    public function asort(int $sort_flags = 0): self
253
    {
254 4
        $this->generatorToArray();
255
256 4
        \asort($this->array, $sort_flags);
257
258 4
        return $this;
259
    }
260
261
    /**
262
     * Counts all elements in an array, or something in an object.
263
     *
264
     * <p>
265
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
266
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
267
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
268
     * implemented and used in PHP.
269
     * </p>
270
     *
271
     * @see http://php.net/manual/en/function.count.php
272
     *
273
     * @param int $mode [optional] If the optional mode parameter is set to
274
     *                  COUNT_RECURSIVE (or 1), count
275
     *                  will recursively count the array. This is particularly useful for
276
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
277
     *
278
     * @return int
279
     *             <p>
280
     *             The number of elements in var, which is
281
     *             typically an array, since anything else will have one
282
     *             element.
283
     *             </p>
284
     *             <p>
285
     *             If var is not an array or an object with
286
     *             implemented Countable interface,
287
     *             1 will be returned.
288
     *             There is one exception, if var is &null;,
289
     *             0 will be returned.
290
     *             </p>
291
     *             <p>
292
     *             Caution: count may return 0 for a variable that isn't set,
293
     *             but it may also return 0 for a variable that has been initialized with an
294
     *             empty array. Use isset to test if a variable is set.
295
     *             </p>
296
     */
297 145
    public function count(int $mode = \COUNT_NORMAL): int
298
    {
299
        if (
300 145
            $this->generator
301
            &&
302 145
            $mode === \COUNT_NORMAL
303
        ) {
304 4
            return \iterator_count($this->generator);
305
        }
306
307 141
        return \count($this->getArray(), $mode);
308
    }
309
310
    /**
311
     * Exchange the array for another one.
312
     *
313
     * @param array|static $data
314
     *
315
     * @return array
316
     */
317 1
    public function exchangeArray($data): array
318
    {
319 1
        $this->array = $this->fallbackForArray($data);
320
321 1
        return $this->array;
322
    }
323
324
    /**
325
     * Creates a copy of the ArrayyObject.
326
     *
327
     * @return array
328
     */
329 5
    public function getArrayCopy(): array
330
    {
331 5
        $this->generatorToArray();
332
333 5
        return $this->array;
334
    }
335
336
    /**
337
     * Returns a new iterator, thus implementing the \Iterator interface.
338
     *
339
     * @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...
340
     *                          <p>An iterator for the values in the array.</p>
341
     */
342 24
    public function getIterator(): \Iterator
343
    {
344 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
345 1
            return $this->generator;
346
        }
347
348 23
        $iterator = $this->getIteratorClass();
349
350 23
        if ($iterator === ArrayyIterator::class) {
351 23
            return new $iterator($this->getArray(), 0, static::class);
352
        }
353
354
        return new $iterator($this->getArray());
355
    }
356
357
    /**
358
     * Gets the iterator classname for the ArrayObject.
359
     *
360
     * @return string
361
     */
362 23
    public function getIteratorClass(): string
363
    {
364 23
        return $this->iteratorClass;
365
    }
366
367
    /**
368
     * Sort the entries by key
369
     *
370
     * @param int $sort_flags [optional] <p>
371
     *                        You may modify the behavior of the sort using the optional
372
     *                        parameter sort_flags, for details
373
     *                        see sort.
374
     *                        </p>
375
     *
376
     * @return static
377
     *                <p>(Mutable) Return this Arrayy object.</p>
378
     */
379 4
    public function ksort(int $sort_flags = 0): self
380
    {
381 4
        $this->generatorToArray();
382
383 4
        \ksort($this->array, $sort_flags);
384
385 4
        return $this;
386
    }
387
388
    /**
389
     * Sort an array using a case insensitive "natural order" algorithm
390
     *
391
     * @return static
392
     *                <p>(Mutable) Return this Arrayy object.</p>
393
     */
394
    public function natcasesort(): self
395
    {
396
        $this->generatorToArray();
397
398
        \natcasesort($this->array);
399
400
        return $this;
401
    }
402
403
    /**
404
     * Sort entries using a "natural order" algorithm
405
     *
406
     * @return static
407
     *                <p>(Mutable) Return this Arrayy object.</p>
408
     */
409 1
    public function natsort(): self
410
    {
411 1
        $this->generatorToArray();
412
413 1
        \natsort($this->array);
414
415 1
        return $this;
416
    }
417
418
    /**
419
     * Whether or not an offset exists.
420
     *
421
     * @param bool|int|string $offset
422
     *
423
     * @return bool
424
     *
425
     * @noinspection PhpSillyAssignmentInspection
426
     */
427 130
    public function offsetExists($offset): bool
428
    {
429 130
        $this->generatorToArray();
430
431 130
        if ($this->array === []) {
432 5
            return false;
433
        }
434
435
        // php cast "bool"-index into "int"-index
436 125
        if ((bool) $offset === $offset) {
437 1
            $offset = (int) $offset;
438
        }
439
440
        /** @var int|string $offset - hint for phpstan */
441 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...
442
443 125
        $tmpReturn = $this->keyExists($offset);
444
445
        if (
446 125
            $tmpReturn === true
447
            ||
448
            (
449 92
                $tmpReturn === false
450
                &&
451 125
                \strpos((string) $offset, $this->pathSeparator) === false
452
            )
453
        ) {
454 123
            return $tmpReturn;
455
        }
456
457 3
        $offsetExists = false;
458
459
        if (
460 3
            $this->pathSeparator
461
            &&
462 3
            (string) $offset === $offset
463
            &&
464 3
            \strpos($offset, $this->pathSeparator) !== false
465
        ) {
466 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
467 3
            if ($explodedPath !== false) {
468 3
                $lastOffset = \array_pop($explodedPath);
469 3
                if ($lastOffset !== null) {
470 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
471
472 3
                    $this->callAtPath(
473 3
                        $containerPath,
474
                        static function ($container) use ($lastOffset, &$offsetExists) {
475 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
476 3
                        }
477
                    );
478
                }
479
            }
480
        }
481
482 3
        return $offsetExists;
483
    }
484
485
    /**
486
     * Returns the value at specified offset.
487
     *
488
     * @param int|string $offset
489
     *
490
     * @return mixed
491
     *               <p>Will return null if the offset did not exists.</p>
492
     */
493 101
    public function offsetGet($offset)
494
    {
495 101
        return $this->offsetExists($offset) ? $this->get($offset) : null;
496
    }
497
498
    /**
499
     * Assigns a value to the specified offset + check the type.
500
     *
501
     * @param int|string|null $offset
502
     * @param mixed           $value
503
     *
504
     * @return void
505
     */
506 21
    public function offsetSet($offset, $value)
507
    {
508 21
        $this->generatorToArray();
509
510 21
        if ($offset === null) {
511 5
            if ($this->properties !== []) {
512 1
                $this->checkType(null, $value);
513
            }
514
515 4
            $this->array[] = $value;
516
        } else {
517 16
            $this->internalSet(
518 16
                $offset,
519 16
                $value,
520 16
                true
521
            );
522
        }
523 20
    }
524
525
    /**
526
     * Unset an offset.
527
     *
528
     * @param int|string $offset
529
     *
530
     * @return void
531
     */
532 12
    public function offsetUnset($offset)
533
    {
534 12
        $this->generatorToArray();
535
536 12
        if ($this->array === []) {
537 3
            return;
538
        }
539
540 10
        if ($this->keyExists($offset)) {
541 7
            unset($this->array[$offset]);
542
543 7
            return;
544
        }
545
546
        if (
547 5
            $this->pathSeparator
548
            &&
549 5
            (string) $offset === $offset
550
            &&
551 5
            \strpos($offset, $this->pathSeparator) !== false
552
        ) {
553 2
            $path = \explode($this->pathSeparator, (string) $offset);
554
555 2
            if ($path !== false) {
556 2
                $pathToUnset = \array_pop($path);
557
558 2
                $this->callAtPath(
559 2
                    \implode($this->pathSeparator, $path),
560
                    static function (&$offset) use ($pathToUnset) {
561 2
                        unset($offset[$pathToUnset]);
562 2
                    }
563
                );
564
            }
565
        }
566
567 5
        unset($this->array[$offset]);
568 5
    }
569
570
    /**
571
     * Serialize the current "Arrayy"-object.
572
     *
573
     * @return string
574
     */
575 2
    public function serialize(): string
576
    {
577 2
        $this->generatorToArray();
578
579 2
        if (\PHP_VERSION_ID < 70400) {
580 2
            return parent::serialize();
581
        }
582
583
        return \serialize($this);
584
    }
585
586
    /**
587
     * Sets the iterator classname for the current "Arrayy"-object.
588
     *
589
     * @param string $iteratorClass
590
     *
591
     * @throws \InvalidArgumentException
592
     *
593
     * @return void
594
     *
595
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
596
     */
597 1057
    public function setIteratorClass($iteratorClass)
598
    {
599 1057
        if (\class_exists($iteratorClass)) {
600 1057
            $this->iteratorClass = $iteratorClass;
601
602 1057
            return;
603
        }
604
605
        if (\strpos($iteratorClass, '\\') === 0) {
606
            $iteratorClass = '\\' . $iteratorClass;
607
            if (\class_exists($iteratorClass)) {
608
                $this->iteratorClass = $iteratorClass;
609
610
                return;
611
            }
612
        }
613
614
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $iteratorClass);
615
    }
616
617
    /**
618
     * Sort the entries with a user-defined comparison function and maintain key association.
619
     *
620
     * @param callable $function
621
     *
622
     * @throws \InvalidArgumentException
623
     *
624
     * @return static
625
     *                <p>(Mutable) Return this Arrayy object.</p>
626
     */
627 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...
628
    {
629
        if (!\is_callable($function)) {
630
            throw new \InvalidArgumentException('Passed function must be callable');
631
        }
632
633
        $this->generatorToArray();
634
635
        \uasort($this->array, $function);
636
637
        return $this;
638
    }
639
640
    /**
641
     * Sort the entries by keys using a user-defined comparison function.
642
     *
643
     * @param callable $function
644
     *
645
     * @throws \InvalidArgumentException
646
     *
647
     * @return static
648
     *                <p>(Mutable) Return this Arrayy object.</p>
649
     */
650 5
    public function uksort($function): self
651
    {
652 5
        return $this->customSortKeys($function);
653
    }
654
655
    /**
656
     * Unserialize an string and return the instance of the "Arrayy"-class.
657
     *
658
     * @param string $string
659
     *
660
     * @return static
661
     */
662 2
    public function unserialize($string): self
663
    {
664 2
        if (\PHP_VERSION_ID < 70400) {
665 2
            parent::unserialize($string);
666
667 2
            return $this;
668
        }
669
670
        return \unserialize($string, ['allowed_classes' => [__CLASS__, TypeCheckPhpDoc::class]]);
671
    }
672
673
    /**
674
     * Append a (key) + values to the current array.
675
     *
676
     * @param array $values
677
     * @param mixed $key
678
     *
679
     * @return static
680
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
681
     */
682 1
    public function appendArrayValues(array $values, $key = null)
683
    {
684 1
        $this->generatorToArray();
685
686 1
        if ($key !== null) {
687
            if (
688 1
                isset($this->array[$key])
689
                &&
690 1
                \is_array($this->array[$key]) === true
691
            ) {
692 1
                foreach ($values as $value) {
693 1
                    $this->array[$key][] = $value;
694
                }
695
            } else {
696 1
                foreach ($values as $value) {
697
                    $this->array[$key] = $value;
698
                }
699
            }
700
        } else {
701
            foreach ($values as $value) {
702
                $this->array[] = $value;
703
            }
704
        }
705
706 1
        return $this;
707
    }
708
709
    /**
710
     * Add a suffix to each key.
711
     *
712
     * @param mixed $prefix
713
     *
714
     * @return static
715
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
716
     */
717 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...
718
    {
719
        // init
720 10
        $result = [];
721
722 10
        foreach ($this->getGenerator() as $key => $item) {
723 9
            if ($item instanceof self) {
724
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
725 9
            } elseif (\is_array($item) === true) {
726
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
727
                    ->appendToEachKey($prefix)
728
                    ->toArray();
729
            } else {
730 9
                $result[$prefix . $key] = $item;
731
            }
732
        }
733
734 10
        return self::create($result, $this->iteratorClass, false);
735
    }
736
737
    /**
738
     * Add a prefix to each value.
739
     *
740
     * @param mixed $prefix
741
     *
742
     * @return static
743
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
744
     */
745 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...
746
    {
747
        // init
748 10
        $result = [];
749
750 10
        foreach ($this->getGenerator() as $key => $item) {
751 9
            if ($item instanceof self) {
752
                $result[$key] = $item->appendToEachValue($prefix);
753 9
            } elseif (\is_array($item) === true) {
754
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
755 9
            } elseif (\is_object($item) === true) {
756 1
                $result[$key] = $item;
757
            } else {
758 8
                $result[$key] = $prefix . $item;
759
            }
760
        }
761
762 10
        return self::create($result, $this->iteratorClass, false);
763
    }
764
765
    /**
766
     * Sort an array in reverse order and maintain index association.
767
     *
768
     * @return static
769
     *                <p>(Mutable) Return this Arrayy object.</p>
770
     */
771 10
    public function arsort(): self
772
    {
773 10
        $this->generatorToArray();
774
775 10
        \arsort($this->array);
776
777 10
        return $this;
778
    }
779
780
    /**
781
     * Iterate over the current array and execute a callback for each loop.
782
     *
783
     * @param \Closure $closure
784
     *
785
     * @return static
786
     *                <p>(Immutable)</p>
787
     */
788 2
    public function at(\Closure $closure): self
789
    {
790 2
        $arrayy = clone $this;
791
792 2
        foreach ($arrayy->getGenerator() as $key => $value) {
793 2
            $closure($value, $key);
794
        }
795
796 2
        return static::create(
797 2
            $arrayy->toArray(),
798 2
            $this->iteratorClass,
799 2
            false
800
        );
801
    }
802
803
    /**
804
     * Returns the average value of the current array.
805
     *
806
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
807
     *
808
     * @return float|int
809
     *                   <p>The average value.</p>
810
     */
811 10
    public function average($decimals = 0)
812
    {
813 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
814
815 10
        if (!$count) {
816 2
            return 0;
817
        }
818
819 8
        if ((int) $decimals !== $decimals) {
820 3
            $decimals = 0;
821
        }
822
823 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
824
    }
825
826
    /**
827
     * Changes all keys in an array.
828
     *
829
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
830
     *                  or <strong>CASE_LOWER</strong> (default)</p>
831
     *
832
     * @return static
833
     *                <p>(Immutable)</p>
834
     */
835 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
836
    {
837
        if (
838 1
            $case !== \CASE_LOWER
839
            &&
840 1
            $case !== \CASE_UPPER
841
        ) {
842
            $case = \CASE_LOWER;
843
        }
844
845 1
        $return = [];
846 1
        foreach ($this->getGenerator() as $key => $value) {
847 1
            if ($case === \CASE_LOWER) {
848 1
                $key = \mb_strtolower((string) $key);
849
            } else {
850 1
                $key = \mb_strtoupper((string) $key);
851
            }
852
853 1
            $return[$key] = $value;
854
        }
855
856 1
        return static::create(
857 1
            $return,
858 1
            $this->iteratorClass,
859 1
            false
860
        );
861
    }
862
863
    /**
864
     * Change the path separator of the array wrapper.
865
     *
866
     * By default, the separator is: "."
867
     *
868
     * @param string $separator <p>Separator to set.</p>
869
     *
870
     * @return static
871
     *                <p>Mutable</p>
872
     */
873 11
    public function changeSeparator($separator): self
874
    {
875 11
        $this->pathSeparator = $separator;
876
877 11
        return $this;
878
    }
879
880
    /**
881
     * Create a chunked version of the current array.
882
     *
883
     * @param int  $size         <p>Size of each chunk.</p>
884
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
885
     *
886
     * @return static
887
     *                <p>(Immutable) A new array of chunks from the original array.</p>
888
     */
889 5
    public function chunk($size, $preserveKeys = false): self
890
    {
891 5
        return static::create(
892 5
            \array_chunk($this->getArray(), $size, $preserveKeys),
893 5
            $this->iteratorClass,
894 5
            false
895
        );
896
    }
897
898
    /**
899
     * Clean all falsy values from the current array.
900
     *
901
     * @return static
902
     *                <p>(Immutable)</p>
903
     */
904 8
    public function clean(): self
905
    {
906 8
        return $this->filter(
907
            static function ($value) {
908 7
                return (bool) $value;
909 8
            }
910
        );
911
    }
912
913
    /**
914
     * WARNING!!! -> Clear the current array.
915
     *
916
     * @return static
917
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
918
     */
919 5
    public function clear(): self
920
    {
921 5
        $this->array = [];
922 5
        $this->generator = null;
923
924 5
        return $this;
925
    }
926
927
    /**
928
     * Check if an item is in the current array.
929
     *
930
     * @param float|int|string $value
931
     * @param bool             $recursive
932
     * @param bool             $strict
933
     *
934
     * @return bool
935
     */
936 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
937
    {
938 23
        if ($recursive === true) {
939 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
940
        }
941
942 14
        foreach ($this->getGenerator() as $valueFromArray) {
943 11
            if ($strict) {
944 11
                if ($value === $valueFromArray) {
945 11
                    return true;
946
                }
947
            } else {
948
                /** @noinspection NestedPositiveIfStatementsInspection */
949
                if ($value == $valueFromArray) {
950
                    return true;
951
                }
952
            }
953
        }
954
955 7
        return false;
956
    }
957
958
    /**
959
     * Check if an (case-insensitive) string is in the current array.
960
     *
961
     * @param string $value
962
     * @param bool   $recursive
963
     *
964
     * @return bool
965
     */
966 26
    public function containsCaseInsensitive($value, $recursive = false): bool
967
    {
968 26
        if ($recursive === true) {
969 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
970 22
                if (\is_array($valueTmp) === true) {
971 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
972 5
                    if ($return === true) {
973 5
                        return $return;
974
                    }
975 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
976 16
                    return true;
977
                }
978
            }
979
980 10
            return false;
981
        }
982
983 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
984 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
985 8
                return true;
986
            }
987
        }
988
989 5
        return false;
990
    }
991
992
    /**
993
     * Check if the given key/index exists in the array.
994
     *
995
     * @param int|string $key <p>key/index to search for</p>
996
     *
997
     * @return bool
998
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
999
     */
1000 4
    public function containsKey($key): bool
1001
    {
1002 4
        return $this->offsetExists($key);
1003
    }
1004
1005
    /**
1006
     * Check if all given needles are present in the array as key/index.
1007
     *
1008
     * @param array $needles   <p>The keys you are searching for.</p>
1009
     * @param bool  $recursive
1010
     *
1011
     * @return bool
1012
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1013
     */
1014 2
    public function containsKeys(array $needles, $recursive = false): bool
1015
    {
1016 2
        if ($recursive === true) {
1017
            return
1018 2
                \count(
1019 2
                    \array_intersect(
1020 2
                        $needles,
1021 2
                        $this->keys(true)->getArray()
1022
                    ),
1023 2
                    \COUNT_RECURSIVE
1024
                )
1025
                ===
1026 2
                \count(
1027 2
                    $needles,
1028 2
                    \COUNT_RECURSIVE
1029
                );
1030
        }
1031
1032 1
        return \count(
1033 1
            \array_intersect($needles, $this->keys()->getArray()),
1034 1
            \COUNT_NORMAL
1035
        )
1036
               ===
1037 1
               \count(
1038 1
                   $needles,
1039 1
                   \COUNT_NORMAL
1040
               );
1041
    }
1042
1043
    /**
1044
     * Check if all given needles are present in the array as key/index.
1045
     *
1046
     * @param array $needles <p>The keys you are searching for.</p>
1047
     *
1048
     * @return bool
1049
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1050
     */
1051 1
    public function containsKeysRecursive(array $needles): bool
1052
    {
1053 1
        return $this->containsKeys($needles, true);
1054
    }
1055
1056
    /**
1057
     * alias: for "Arrayy->contains()"
1058
     *
1059
     * @param float|int|string $value
1060
     *
1061
     * @return bool
1062
     *
1063
     * @see Arrayy::contains()
1064
     */
1065 9
    public function containsValue($value): bool
1066
    {
1067 9
        return $this->contains($value);
1068
    }
1069
1070
    /**
1071
     * alias: for "Arrayy->contains($value, true)"
1072
     *
1073
     * @param float|int|string $value
1074
     *
1075
     * @return bool
1076
     *
1077
     * @see Arrayy::contains()
1078
     */
1079 18
    public function containsValueRecursive($value): bool
1080
    {
1081 18
        return $this->contains($value, true);
1082
    }
1083
1084
    /**
1085
     * Check if all given needles are present in the array.
1086
     *
1087
     * @param array $needles
1088
     *
1089
     * @return bool
1090
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1091
     */
1092 1
    public function containsValues(array $needles): bool
1093
    {
1094 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1095
               ===
1096 1
               \count($needles, \COUNT_NORMAL);
1097
    }
1098
1099
    /**
1100
     * Counts all the values of an array
1101
     *
1102
     * @see http://php.net/manual/en/function.array-count-values.php
1103
     *
1104
     * @return static
1105
     *                <p>
1106
     *                (Immutable)
1107
     *                An associative Arrayy-object of values from input as
1108
     *                keys and their count as value.
1109
     *                </p>
1110
     */
1111 7
    public function countValues(): self
1112
    {
1113 7
        return self::create(\array_count_values($this->getArray()), $this->iteratorClass);
1114
    }
1115
1116
    /**
1117
     * Creates an Arrayy object.
1118
     *
1119
     * @param mixed  $data
1120
     * @param string $iteratorClass
1121
     * @param bool   $checkPropertiesInConstructor
1122
     *
1123
     * @return static
1124
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1125
     *
1126
     * @psalm-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
1127
     */
1128 669
    public static function create(
1129
        $data = [],
1130
        string $iteratorClass = ArrayyIterator::class,
1131
        bool $checkPropertiesInConstructor = true
1132
    ) {
1133 669
        return new static(
1134 669
            $data,
1135 669
            $iteratorClass,
1136 669
            $checkPropertiesInConstructor
1137
        );
1138
    }
1139
1140
    /**
1141
     * WARNING: Creates an Arrayy object by reference.
1142
     *
1143
     * @param array $array
1144
     *
1145
     * @return static
1146
     *                <p>(Mutable) Return this Arrayy object.</p>
1147
     */
1148 1
    public function createByReference(array &$array = []): self
1149
    {
1150 1
        $array = $this->fallbackForArray($array);
1151
1152 1
        $this->array = &$array;
1153
1154 1
        return $this;
1155
    }
1156
1157
    /**
1158
     * Create an new instance from a callable function which will return an Generator.
1159
     *
1160
     * @param callable():Generator $generatorFunction
0 ignored issues
show
Documentation introduced by
The doc-type callable():Generator could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (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...
1161
     *
1162
     * @return static
1163
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1164
     */
1165 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1166
    {
1167 5
        return self::create($generatorFunction);
1168
    }
1169
1170
    /**
1171
     * Create an new instance filled with a copy of values from a "Generator"-object.
1172
     *
1173
     * @param \Generator $generator
1174
     *
1175
     * @return static
1176
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1177
     */
1178 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1179
    {
1180 4
        return self::create(\iterator_to_array($generator, true));
1181
    }
1182
1183
    /**
1184
     * Create an new Arrayy object via JSON.
1185
     *
1186
     * @param string $json
1187
     *
1188
     * @return static
1189
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1190
     */
1191 5
    public static function createFromJson(string $json): self
1192
    {
1193 5
        return static::create(\json_decode($json, true));
1194
    }
1195
1196
    /**
1197
     * Create an new instance filled with values from an object that is iterable.
1198
     *
1199
     * @param \Traversable $object <p>iterable object</p>
1200
     *
1201
     * @return static
1202
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1203
     */
1204 4
    public static function createFromObject(\Traversable $object): self
1205
    {
1206
        // init
1207 4
        $array = self::create();
1208
1209 4
        if ($object instanceof self) {
1210 4
            $objectArray = $object->getGenerator();
1211
        } else {
1212
            $objectArray = $object;
1213
        }
1214
1215 4
        foreach ($objectArray as $key => $value) {
1216 3
            $array[$key] = $value;
1217
        }
1218
1219 4
        return $array;
1220
    }
1221
1222
    /**
1223
     * Create an new instance filled with values from an object.
1224
     *
1225
     * @param object $object
1226
     *
1227
     * @return static
1228
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1229
     */
1230 5
    public static function createFromObjectVars($object): self
1231
    {
1232 5
        return self::create(self::objectToArray($object));
1233
    }
1234
1235
    /**
1236
     * Create an new Arrayy object via string.
1237
     *
1238
     * @param string      $str       <p>The input string.</p>
1239
     * @param string|null $delimiter <p>The boundary string.</p>
1240
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1241
     *                               used.</p>
1242
     *
1243
     * @return static
1244
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1245
     */
1246 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1247
    {
1248 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...
1249 1
            \preg_match_all($regEx, $str, $array);
1250
1251 1
            if (!empty($array)) {
1252 1
                $array = $array[0];
1253
            }
1254
        } else {
1255
            /** @noinspection NestedPositiveIfStatementsInspection */
1256 9
            if ($delimiter !== null) {
1257 7
                $array = \explode($delimiter, $str);
1258
            } else {
1259 2
                $array = [$str];
1260
            }
1261
        }
1262
1263
        // trim all string in the array
1264 10
        \array_walk(
1265 10
            $array,
1266
            static function (&$val) {
1267 10
                if ((string) $val === $val) {
1268 10
                    $val = \trim($val);
1269
                }
1270 10
            }
1271
        );
1272
1273 10
        return static::create($array);
1274
    }
1275
1276
    /**
1277
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1278
     *
1279
     * @param \Traversable $traversable
1280
     *
1281
     * @return static
1282
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1283
     */
1284 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1285
    {
1286 1
        return self::create(\iterator_to_array($traversable, true));
1287
    }
1288
1289
    /**
1290
     * Create an new instance containing a range of elements.
1291
     *
1292
     * @param mixed $low  <p>First value of the sequence.</p>
1293
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1294
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1295
     *
1296
     * @return static
1297
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1298
     */
1299 2
    public static function createWithRange($low, $high, int $step = 1): self
1300
    {
1301 2
        return static::create(\range($low, $high, $step));
1302
    }
1303
1304
    /**
1305
     * Gets the element of the array at the current internal iterator position.
1306
     *
1307
     * @return false|mixed
1308
     */
1309
    public function current()
1310
    {
1311
        return \current($this->array);
1312
    }
1313
1314
    /**
1315
     * Custom sort by index via "uksort".
1316
     *
1317
     * @see http://php.net/manual/en/function.uksort.php
1318
     *
1319
     * @param callable $function
1320
     *
1321
     * @throws \InvalidArgumentException
1322
     *
1323
     * @return static
1324
     *                <p>(Mutable) Return this Arrayy object.</p>
1325
     */
1326 5 View Code Duplication
    public function customSortKeys($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...
1327
    {
1328 5
        if (\is_callable($function) === false) {
1329
            throw new \InvalidArgumentException('Passed function must be callable');
1330
        }
1331
1332 5
        $this->generatorToArray();
1333
1334 5
        \uksort($this->array, $function);
1335
1336 5
        return $this;
1337
    }
1338
1339
    /**
1340
     * Custom sort by value via "usort".
1341
     *
1342
     * @see http://php.net/manual/en/function.usort.php
1343
     *
1344
     * @param callable $function
1345
     *
1346
     * @throws \InvalidArgumentException
1347
     *
1348
     * @return static
1349
     *                <p>(Mutable) Return this Arrayy object.</p>
1350
     */
1351 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...
1352
    {
1353 6
        if (\is_callable($function) === false) {
1354
            throw new \InvalidArgumentException('Passed function must be callable');
1355
        }
1356
1357 6
        $this->generatorToArray();
1358
1359 6
        \usort($this->array, $function);
1360
1361 6
        return $this;
1362
    }
1363
1364
    /**
1365
     * Delete the given key or keys.
1366
     *
1367
     * @param int|int[]|string|string[] $keyOrKeys
1368
     *
1369
     * @return void
1370
     */
1371 4
    public function delete($keyOrKeys)
1372
    {
1373 4
        $keyOrKeys = (array) $keyOrKeys;
1374
1375 4
        foreach ($keyOrKeys as $key) {
1376 4
            $this->offsetUnset($key);
1377
        }
1378 4
    }
1379
1380
    /**
1381
     * Return values that are only in the current array.
1382
     *
1383
     * @param array ...$array
1384
     *
1385
     * @return static
1386
     *                <p>(Immutable)</p>
1387
     */
1388 13
    public function diff(...$array): self
1389
    {
1390 13
        return static::create(
1391 13
            \array_diff($this->getArray(), ...$array),
1392 13
            $this->iteratorClass,
1393 13
            false
1394
        );
1395
    }
1396
1397
    /**
1398
     * Return values that are only in the current array.
1399
     *
1400
     * @param array ...$array
1401
     *
1402
     * @return static
1403
     *                <p>(Immutable)</p>
1404
     */
1405 8
    public function diffKey(...$array): self
1406
    {
1407 8
        return static::create(
1408 8
            \array_diff_key($this->getArray(), ...$array),
1409 8
            $this->iteratorClass,
1410 8
            false
1411
        );
1412
    }
1413
1414
    /**
1415
     * Return values and Keys that are only in the current array.
1416
     *
1417
     * @param array $array
1418
     *
1419
     * @return static
1420
     *                <p>(Immutable)</p>
1421
     */
1422 8
    public function diffKeyAndValue(array $array = []): self
1423
    {
1424 8
        return static::create(
1425 8
            \array_diff_assoc($this->getArray(), $array),
1426 8
            $this->iteratorClass,
1427 8
            false
1428
        );
1429
    }
1430
1431
    /**
1432
     * Return values that are only in the current multi-dimensional array.
1433
     *
1434
     * @param array      $array
1435
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1436
     *
1437
     * @return static
1438
     *                <p>(Immutable)</p>
1439
     */
1440 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1441
    {
1442
        // init
1443 1
        $result = [];
1444
1445
        if (
1446 1
            $helperVariableForRecursion !== null
1447
            &&
1448 1
            \is_array($helperVariableForRecursion) === true
1449
        ) {
1450
            $arrayForTheLoop = $helperVariableForRecursion;
1451
        } else {
1452 1
            $arrayForTheLoop = $this->getGenerator();
1453
        }
1454
1455 1
        foreach ($arrayForTheLoop as $key => $value) {
1456 1
            if ($value instanceof self) {
1457
                $value = $value->getArray();
1458
            }
1459
1460 1
            if (\array_key_exists($key, $array)) {
1461 1
                if ($value !== $array[$key]) {
1462 1
                    $result[$key] = $value;
1463
                }
1464
            } else {
1465 1
                $result[$key] = $value;
1466
            }
1467
        }
1468
1469 1
        return static::create(
1470 1
            $result,
1471 1
            $this->iteratorClass,
1472 1
            false
1473
        );
1474
    }
1475
1476
    /**
1477
     * Return values that are only in the new $array.
1478
     *
1479
     * @param array $array
1480
     *
1481
     * @return static
1482
     *                <p>(Immutable)</p>
1483
     */
1484 8
    public function diffReverse(array $array = []): self
1485
    {
1486 8
        return static::create(
1487 8
            \array_diff($array, $this->getArray()),
1488 8
            $this->iteratorClass,
1489 8
            false
1490
        );
1491
    }
1492
1493
    /**
1494
     * Divide an array into two arrays. One with keys and the other with values.
1495
     *
1496
     * @return static
1497
     *                <p>(Immutable)</p>
1498
     */
1499 1
    public function divide(): self
1500
    {
1501 1
        return static::create(
1502
            [
1503 1
                $this->keys(),
1504 1
                $this->values(),
1505
            ],
1506 1
            $this->iteratorClass,
1507 1
            false
1508
        );
1509
    }
1510
1511
    /**
1512
     * Iterate over the current array and modify the array's value.
1513
     *
1514
     * @param \Closure $closure
1515
     *
1516
     * @return static
1517
     *                <p>(Immutable)</p>
1518
     */
1519 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...
1520
    {
1521
        // init
1522 5
        $array = [];
1523
1524 5
        foreach ($this->getGenerator() as $key => $value) {
1525 5
            $array[$key] = $closure($value, $key);
1526
        }
1527
1528 5
        return static::create(
1529 5
            $array,
1530 5
            $this->iteratorClass,
1531 5
            false
1532
        );
1533
    }
1534
1535
    /**
1536
     * Sets the internal iterator to the last element in the array and returns this element.
1537
     *
1538
     * @return mixed
1539
     */
1540
    public function end()
1541
    {
1542
        return \end($this->array);
1543
    }
1544
1545
    /**
1546
     * Check if a value is in the current array using a closure.
1547
     *
1548
     * @param \Closure $closure
1549
     *
1550
     * @return bool
1551
     *              <p>Returns true if the given value is found, false otherwise.</p>
1552
     */
1553 4
    public function exists(\Closure $closure): bool
1554
    {
1555
        // init
1556 4
        $isExists = false;
1557
1558 4
        foreach ($this->getGenerator() as $key => $value) {
1559 3
            if ($closure($value, $key)) {
1560 1
                $isExists = true;
1561
1562 1
                break;
1563
            }
1564
        }
1565
1566 4
        return $isExists;
1567
    }
1568
1569
    /**
1570
     * Fill the array until "$num" with "$default" values.
1571
     *
1572
     * @param int   $num
1573
     * @param mixed $default
1574
     *
1575
     * @return static
1576
     *                <p>(Immutable)</p>
1577
     */
1578 8
    public function fillWithDefaults(int $num, $default = null): self
1579
    {
1580 8
        if ($num < 0) {
1581 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1582
        }
1583
1584 7
        $this->generatorToArray();
1585
1586 7
        $tmpArray = $this->array;
1587
1588 7
        $count = \count($tmpArray);
1589
1590 7
        while ($count < $num) {
1591 4
            $tmpArray[] = $default;
1592 4
            ++$count;
1593
        }
1594
1595 7
        return static::create(
1596 7
            $tmpArray,
1597 7
            $this->iteratorClass,
1598 7
            false
1599
        );
1600
    }
1601
1602
    /**
1603
     * Find all items in an array that pass the truth test.
1604
     *
1605
     * @param \Closure|null $closure [optional] <p>
1606
     *                               The callback function to use
1607
     *                               </p>
1608
     *                               <p>
1609
     *                               If no callback is supplied, all entries of
1610
     *                               input equal to false (see
1611
     *                               converting to
1612
     *                               boolean) will be removed.
1613
     *                               </p>
1614
     * @param int           $flag    [optional] <p>
1615
     *                               Flag determining what arguments are sent to <i>callback</i>:
1616
     *                               </p><ul>
1617
     *                               <li>
1618
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1619
     *                               to <i>callback</i> instead of the value</span>
1620
     *                               </li>
1621
     *                               <li>
1622
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1623
     *                               arguments to <i>callback</i> instead of the value</span>
1624
     *                               </li>
1625
     *                               </ul>
1626
     *
1627
     * @return static
1628
     *                <p>(Immutable)</p>
1629
     */
1630 12
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1631
    {
1632 12
        if (!$closure) {
1633 1
            return $this->clean();
1634
        }
1635
1636 12
        return static::create(
1637 12
            \array_filter($this->getArray(), $closure, $flag),
1638 12
            $this->iteratorClass,
1639 12
            false
1640
        );
1641
    }
1642
1643
    /**
1644
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1645
     * property within that.
1646
     *
1647
     * @param string          $property
1648
     * @param string|string[] $value
1649
     * @param string          $comparisonOp
1650
     *                                      <p>
1651
     *                                      'eq' (equals),<br />
1652
     *                                      'gt' (greater),<br />
1653
     *                                      'gte' || 'ge' (greater or equals),<br />
1654
     *                                      'lt' (less),<br />
1655
     *                                      'lte' || 'le' (less or equals),<br />
1656
     *                                      'ne' (not equals),<br />
1657
     *                                      'contains',<br />
1658
     *                                      'notContains',<br />
1659
     *                                      'newer' (via strtotime),<br />
1660
     *                                      'older' (via strtotime),<br />
1661
     *                                      </p>
1662
     *
1663
     * @return static
1664
     *                <p>(Immutable)</p>
1665
     */
1666 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1667
    {
1668 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...
1669 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1670
        }
1671
1672
        $ops = [
1673
            'eq' => static function ($item, $prop, $value): bool {
1674 1
                return $item[$prop] === $value;
1675 1
            },
1676
            'gt' => static function ($item, $prop, $value): bool {
1677
                return $item[$prop] > $value;
1678 1
            },
1679
            'ge' => static function ($item, $prop, $value): bool {
1680
                return $item[$prop] >= $value;
1681 1
            },
1682
            'gte' => static function ($item, $prop, $value): bool {
1683
                return $item[$prop] >= $value;
1684 1
            },
1685
            'lt' => static function ($item, $prop, $value): bool {
1686 1
                return $item[$prop] < $value;
1687 1
            },
1688
            'le' => static function ($item, $prop, $value): bool {
1689
                return $item[$prop] <= $value;
1690 1
            },
1691
            'lte' => static function ($item, $prop, $value): bool {
1692
                return $item[$prop] <= $value;
1693 1
            },
1694
            'ne' => static function ($item, $prop, $value): bool {
1695
                return $item[$prop] !== $value;
1696 1
            },
1697
            'contains' => static function ($item, $prop, $value): bool {
1698 1
                return \in_array($item[$prop], (array) $value, true);
1699 1
            },
1700
            'notContains' => static function ($item, $prop, $value): bool {
1701
                return !\in_array($item[$prop], (array) $value, true);
1702 1
            },
1703
            'newer' => static function ($item, $prop, $value): bool {
1704
                return \strtotime($item[$prop]) > \strtotime($value);
1705 1
            },
1706
            'older' => static function ($item, $prop, $value): bool {
1707
                return \strtotime($item[$prop]) < \strtotime($value);
1708 1
            },
1709
        ];
1710
1711 1
        $result = \array_values(
1712 1
            \array_filter(
1713 1
                $this->getArray(),
1714
                static function ($item) use (
1715 1
                    $property,
1716 1
                    $value,
1717 1
                    $ops,
1718 1
                    $comparisonOp
1719
                ) {
1720 1
                    $item = (array) $item;
1721 1
                    $itemArrayy = static::create($item);
1722 1
                    $item[$property] = $itemArrayy->get($property, []);
1723
1724 1
                    return $ops[$comparisonOp]($item, $property, $value);
1725 1
                }
1726
            )
1727
        );
1728
1729 1
        return static::create(
1730 1
            $result,
1731 1
            $this->iteratorClass,
1732 1
            false
1733
        );
1734
    }
1735
1736
    /**
1737
     * Find the first item in an array that passes the truth test,
1738
     *  otherwise return false
1739
     *
1740
     * @param \Closure $closure
1741
     *
1742
     * @return false|mixed
1743
     *                     <p>Return false if we did not find the value.</p>
1744
     */
1745 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...
1746
    {
1747 8
        foreach ($this->getGenerator() as $key => $value) {
1748 6
            if ($closure($value, $key)) {
1749 5
                return $value;
1750
            }
1751
        }
1752
1753 3
        return false;
1754
    }
1755
1756
    /**
1757
     * find by ...
1758
     *
1759
     * @param string          $property
1760
     * @param string|string[] $value
1761
     * @param string          $comparisonOp
1762
     *
1763
     * @return static
1764
     *                <p>(Immutable)</p>
1765
     */
1766 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1767
    {
1768 1
        return $this->filterBy($property, $value, $comparisonOp);
1769
    }
1770
1771
    /**
1772
     * Get the first value from the current array.
1773
     *
1774
     * @return mixed
1775
     *               <p>Return null if there wasn't a element.</p>
1776
     */
1777 21
    public function first()
1778
    {
1779 21
        $key_first = $this->firstKey();
1780 21
        if ($key_first === null) {
1781 3
            return null;
1782
        }
1783
1784 18
        return $this->get($key_first);
1785
    }
1786
1787
    /**
1788
     * Get the first key from the current array.
1789
     *
1790
     * @return mixed
1791
     *               <p>Return null if there wasn't a element.</p>
1792
     */
1793 28
    public function firstKey()
1794
    {
1795 28
        $this->generatorToArray();
1796
1797 28
        return \array_key_first($this->array);
1798
    }
1799
1800
    /**
1801
     * Get the first value(s) from the current array.
1802
     * And will return an empty array if there was no first entry.
1803
     *
1804
     * @param int|null $number <p>How many values you will take?</p>
1805
     *
1806
     * @return static
1807
     *                <p>(Immutable)</p>
1808
     */
1809 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...
1810
    {
1811 37
        $arrayTmp = $this->getArray();
1812
1813 37
        if ($number === null) {
1814 14
            $array = (array) \array_shift($arrayTmp);
1815
        } else {
1816 23
            $number = (int) $number;
1817 23
            $array = \array_splice($arrayTmp, 0, $number);
1818
        }
1819
1820 37
        return static::create(
1821 37
            $array,
1822 37
            $this->iteratorClass,
1823 37
            false
1824
        );
1825
    }
1826
1827
    /**
1828
     * Get the first value(s) from the current array.
1829
     * And will return an empty array if there was no first entry.
1830
     *
1831
     * @param int|null $number <p>How many values you will take?</p>
1832
     *
1833
     * @return static
1834
     *                <p>(Immutable)</p>
1835
     */
1836 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...
1837
    {
1838 3
        $arrayTmp = $this->keys()->getArray();
1839
1840 3
        if ($number === null) {
1841
            $array = (array) \array_shift($arrayTmp);
1842
        } else {
1843 3
            $number = (int) $number;
1844 3
            $array = \array_splice($arrayTmp, 0, $number);
1845
        }
1846
1847 3
        return static::create(
1848 3
            $array,
1849 3
            $this->iteratorClass,
1850 3
            false
1851
        );
1852
    }
1853
1854
    /**
1855
     * Get and rmove the first value(s) from the current array.
1856
     * And will return an empty array if there was no first entry.
1857
     *
1858
     * @param int|null $number <p>How many values you will take?</p>
1859
     *
1860
     * @return static
1861
     *                <p>(Mutable)</p>
1862
     */
1863 34
    public function firstsMutable(int $number = null): self
1864
    {
1865 34
        $this->generatorToArray();
1866
1867 34
        if ($number === null) {
1868 19
            $this->array = (array) \array_shift($this->array);
1869
        } else {
1870 15
            $number = (int) $number;
1871 15
            $this->array = \array_splice($this->array, 0, $number);
1872
        }
1873
1874 34
        return $this;
1875
    }
1876
1877
    /**
1878
     * Exchanges all keys with their associated values in an array.
1879
     *
1880
     * @return static
1881
     *                <p>(Immutable)</p>
1882
     */
1883 1
    public function flip(): self
1884
    {
1885 1
        return static::create(
1886 1
            \array_flip($this->getArray()),
1887 1
            $this->iteratorClass,
1888 1
            false
1889
        );
1890
    }
1891
1892
    /**
1893
     * Get a value from an array (optional using dot-notation).
1894
     *
1895
     * @param mixed $key      <p>The key to look for.</p>
1896
     * @param mixed $fallback <p>Value to fallback to.</p>
1897
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1898
     *                        class.</p>
1899
     *
1900
     * @return mixed|static
1901
     */
1902 172
    public function get($key, $fallback = null, array $array = null)
1903
    {
1904 172
        if ($array !== null) {
1905 4
            $usedArray = $array;
1906
        } else {
1907 169
            $this->generatorToArray();
1908
1909 169
            $usedArray = $this->array;
1910
        }
1911
1912 172
        if ($key === null) {
1913 1
            return static::create(
1914 1
                $usedArray,
1915 1
                $this->iteratorClass,
1916 1
                false
1917
            );
1918
        }
1919
1920
        // php cast "bool"-index into "int"-index
1921 172
        if ((bool) $key === $key) {
1922 3
            $key = (int) $key;
1923
        }
1924
1925 172
        if (\array_key_exists($key, $usedArray) === true) {
1926 162
            if (\is_array($usedArray[$key]) === true) {
1927 11
                return static::create(
1928 11
                    $usedArray[$key],
1929 11
                    $this->iteratorClass,
1930 11
                    false
1931
                );
1932
            }
1933
1934 154
            return $usedArray[$key];
1935
        }
1936
1937
        // crawl through array, get key according to object or not
1938 24
        $usePath = false;
1939
        if (
1940 24
            $this->pathSeparator
1941
            &&
1942 24
            (string) $key === $key
1943
            &&
1944 24
            \strpos($key, $this->pathSeparator) !== false
1945
        ) {
1946 7
            $segments = \explode($this->pathSeparator, (string) $key);
1947 7
            if ($segments !== false) {
1948 7
                $usePath = true;
1949
1950 7
                foreach ($segments as $segment) {
1951
                    if (
1952
                        (
1953 7
                            \is_array($usedArray) === true
1954
                            ||
1955 7
                            $usedArray instanceof \ArrayAccess
1956
                        )
1957
                        &&
1958 7
                        isset($usedArray[$segment])
1959
                    ) {
1960 7
                        $usedArray = $usedArray[$segment];
1961
1962 7
                        continue;
1963
                    }
1964
1965
                    if (
1966 6
                        \is_object($usedArray) === true
1967
                        &&
1968 6
                        \property_exists($usedArray, $segment)
1969
                    ) {
1970 1
                        $usedArray = $usedArray->{$segment};
1971
1972 1
                        continue;
1973
                    }
1974
1975 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1976
                }
1977
            }
1978
        }
1979
1980 24
        if (!$usePath && !isset($usedArray[$key])) {
1981 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1982
        }
1983
1984 7
        if (\is_array($usedArray) === true) {
1985 1
            return static::create(
1986 1
                $usedArray,
1987 1
                $this->iteratorClass,
1988 1
                false
1989
            );
1990
        }
1991
1992 7
        return $usedArray;
1993
    }
1994
1995
    /**
1996
     * alias: for "Arrayy->getArray()"
1997
     *
1998
     * @return array
1999
     *
2000
     * @see Arrayy::getArray()
2001
     */
2002 1
    public function getAll(): array
2003
    {
2004 1
        return $this->getArray();
2005
    }
2006
2007
    /**
2008
     * Get the current array from the "Arrayy"-object.
2009
     *
2010
     * @param bool $convertAllArrayyElements
2011
     *
2012
     * @return array
2013
     */
2014 859
    public function getArray($convertAllArrayyElements = false): array
2015
    {
2016
        // init
2017 859
        $array = [];
2018
2019 859
        if ($convertAllArrayyElements) {
2020 1
            foreach ($this->getGenerator() as $key => $value) {
2021 1
                if ($value instanceof self) {
2022 1
                    $value = $value->getArray(true);
2023
                }
2024
2025 1
                $array[$key] = $value;
2026
            }
2027
        } else {
2028 859
            foreach ($this->getGenerator() as $key => $value) {
2029 752
                $array[$key] = $value;
2030
            }
2031
        }
2032
2033 859
        return $array;
2034
    }
2035
2036
    /**
2037
     * Returns the values from a single column of the input array, identified by
2038
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2039
     *
2040
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2041
     * array by the values from the $indexKey column in the input array.
2042
     *
2043
     * @param mixed $columnKey
2044
     * @param mixed $indexKey
2045
     *
2046
     * @return static
2047
     *                <p>(Immutable)</p>
2048
     */
2049 1
    public function getColumn($columnKey = null, $indexKey = null): self
2050
    {
2051 1
        return static::create(
2052 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2053 1
            $this->iteratorClass,
2054 1
            false
2055
        );
2056
    }
2057
2058
    /**
2059
     * Get the current array from the "Arrayy"-object as generator.
2060
     *
2061
     * @return \Generator
2062
     */
2063 942
    public function getGenerator(): \Generator
2064
    {
2065 942
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2066 40
            yield from $this->generator;
2067
        }
2068
2069 942
        yield from $this->array;
2070 898
    }
2071
2072
    /**
2073
     * alias: for "Arrayy->keys()"
2074
     *
2075
     * @return static
2076
     *                <p>(Immutable)</p>
2077
     *
2078
     * @see Arrayy::keys()
2079
     */
2080 2
    public function getKeys()
2081
    {
2082 2
        return $this->keys();
2083
    }
2084
2085
    /**
2086
     * Get the current array from the "Arrayy"-object as object.
2087
     *
2088
     * @return \stdClass
2089
     */
2090 4
    public function getObject(): \stdClass
2091
    {
2092 4
        return self::arrayToObject($this->getArray());
2093
    }
2094
2095
    /**
2096
     * alias: for "Arrayy->randomImmutable()"
2097
     *
2098
     * @return static
2099
     *                <p>(Immutable)</p>
2100
     *
2101
     * @see Arrayy::randomImmutable()
2102
     */
2103 4
    public function getRandom(): self
2104
    {
2105 4
        return $this->randomImmutable();
2106
    }
2107
2108
    /**
2109
     * alias: for "Arrayy->randomKey()"
2110
     *
2111
     * @return mixed
2112
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2113
     *
2114
     * @see Arrayy::randomKey()
2115
     */
2116 3
    public function getRandomKey()
2117
    {
2118 3
        return $this->randomKey();
2119
    }
2120
2121
    /**
2122
     * alias: for "Arrayy->randomKeys()"
2123
     *
2124
     * @param int $number
2125
     *
2126
     * @return static
2127
     *                <p>(Immutable)</p>
2128
     *
2129
     * @see Arrayy::randomKeys()
2130
     */
2131 8
    public function getRandomKeys(int $number): self
2132
    {
2133 8
        return $this->randomKeys($number);
2134
    }
2135
2136
    /**
2137
     * alias: for "Arrayy->randomValue()"
2138
     *
2139
     * @return mixed
2140
     *               <p>Get a random value or null if there wasn't a value.</p>
2141
     *
2142
     * @see Arrayy::randomValue()
2143
     */
2144 3
    public function getRandomValue()
2145
    {
2146 3
        return $this->randomValue();
2147
    }
2148
2149
    /**
2150
     * alias: for "Arrayy->randomValues()"
2151
     *
2152
     * @param int $number
2153
     *
2154
     * @return static
2155
     *                <p>(Immutable)</p>
2156
     *
2157
     * @see Arrayy::randomValues()
2158
     */
2159 6
    public function getRandomValues(int $number): self
2160
    {
2161 6
        return $this->randomValues($number);
2162
    }
2163
2164
    /**
2165
     * Gets all values.
2166
     *
2167
     * @return static
2168
     *                <p>The values of all elements in this array, in the order they
2169
     *                appear in the array.</p>
2170
     */
2171 4
    public function getValues()
2172
    {
2173 4
        $this->generatorToArray();
2174
2175 4
        return static::create(
2176 4
            \array_values($this->array),
2177 4
            $this->iteratorClass,
2178 4
            false
2179
        );
2180
    }
2181
2182
    /**
2183
     * Gets all values via Generator.
2184
     *
2185
     * @return \Generator
2186
     *                    <p>The values of all elements in this array, in the order they
2187
     *                    appear in the array as Generator.</p>
2188
     */
2189 4
    public function getValuesYield(): \Generator
2190
    {
2191 4
        yield from $this->getGenerator();
2192 4
    }
2193
2194
    /**
2195
     * Group values from a array according to the results of a closure.
2196
     *
2197
     * @param callable|string $grouper  <p>A callable function name.</p>
2198
     * @param bool            $saveKeys
2199
     *
2200
     * @return static
2201
     *                <p>(Immutable)</p>
2202
     */
2203 4
    public function group($grouper, bool $saveKeys = false): self
2204
    {
2205
        // init
2206 4
        $result = [];
2207
2208
        // Iterate over values, group by property/results from closure.
2209 4
        foreach ($this->getGenerator() as $key => $value) {
2210 4
            if (\is_callable($grouper) === true) {
2211 3
                $groupKey = $grouper($value, $key);
2212
            } else {
2213 1
                $groupKey = $this->get($grouper);
2214
            }
2215
2216 4
            $newValue = $this->get($groupKey, null, $result);
2217
2218 4
            if ($groupKey instanceof self) {
2219
                $groupKey = $groupKey->getArray();
2220
            }
2221
2222 4
            if ($newValue instanceof self) {
2223 4
                $newValue = $newValue->getArray();
2224
            }
2225
2226
            // Add to results.
2227 4
            if ($groupKey !== null) {
2228 3
                if ($saveKeys) {
2229 2
                    $result[$groupKey] = $newValue;
2230 2
                    $result[$groupKey][$key] = $value;
2231
                } else {
2232 1
                    $result[$groupKey] = $newValue;
2233 1
                    $result[$groupKey][] = $value;
2234
                }
2235
            }
2236
        }
2237
2238 4
        return static::create(
2239 4
            $result,
2240 4
            $this->iteratorClass,
2241 4
            false
2242
        );
2243
    }
2244
2245
    /**
2246
     * Check if an array has a given key.
2247
     *
2248
     * @param mixed $key
2249
     *
2250
     * @return bool
2251
     */
2252 23
    public function has($key): bool
2253
    {
2254 23
        static $UN_FOUND = null;
2255
2256 23
        if ($UN_FOUND === null) {
2257
            // Generate unique string to use as marker.
2258 1
            $UN_FOUND = \uniqid('arrayy', true);
2259
        }
2260
2261 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2262
    }
2263
2264
    /**
2265
     * Check if an array has a given value.
2266
     *
2267
     * INFO: if you need to search recursive please use ```contains()```
2268
     *
2269
     * @param mixed $value
2270
     *
2271
     * @return bool
2272
     */
2273 1
    public function hasValue($value): bool
2274
    {
2275 1
        return $this->contains($value);
2276
    }
2277
2278
    /**
2279
     * Implodes the values of this array.
2280
     *
2281
     * @param string $glue
2282
     *
2283
     * @return string
2284
     */
2285 28
    public function implode(string $glue = ''): string
2286
    {
2287 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2288
    }
2289
2290
    /**
2291
     * Implodes the keys of this array.
2292
     *
2293
     * @param string $glue
2294
     *
2295
     * @return string
2296
     */
2297 8
    public function implodeKeys(string $glue = ''): string
2298
    {
2299 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2300
    }
2301
2302
    /**
2303
     * Given a list and an iterate-function that returns
2304
     * a key for each element in the list (or a property name),
2305
     * returns an object with an index of each item.
2306
     *
2307
     * @param mixed $key
2308
     *
2309
     * @return static
2310
     *                <p>(Immutable)</p>
2311
     */
2312 4
    public function indexBy($key): self
2313
    {
2314
        // init
2315 4
        $results = [];
2316
2317 4
        foreach ($this->getGenerator() as $a) {
2318 4
            if (\array_key_exists($key, $a) === true) {
2319 3
                $results[$a[$key]] = $a;
2320
            }
2321
        }
2322
2323 4
        return static::create(
2324 4
            $results,
2325 4
            $this->iteratorClass,
2326 4
            false
2327
        );
2328
    }
2329
2330
    /**
2331
     * alias: for "Arrayy->searchIndex()"
2332
     *
2333
     * @param mixed $value <p>The value to search for.</p>
2334
     *
2335
     * @return false|mixed
2336
     *
2337
     * @see Arrayy::searchIndex()
2338
     */
2339 4
    public function indexOf($value)
2340
    {
2341 4
        return $this->searchIndex($value);
2342
    }
2343
2344
    /**
2345
     * Get everything but the last..$to items.
2346
     *
2347
     * @param int $to
2348
     *
2349
     * @return static
2350
     *                <p>(Immutable)</p>
2351
     */
2352 12
    public function initial(int $to = 1): self
2353
    {
2354 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2355
    }
2356
2357
    /**
2358
     * Return an array with all elements found in input array.
2359
     *
2360
     * @param array $search
2361
     * @param bool  $keepKeys
2362
     *
2363
     * @return static
2364
     *                <p>(Immutable)</p>
2365
     */
2366 4
    public function intersection(array $search, bool $keepKeys = false): self
2367
    {
2368 4
        if ($keepKeys) {
2369 1
            return static::create(
2370 1
                \array_uintersect(
2371 1
                    $this->getArray(),
2372 1
                    $search,
2373
                    static function ($a, $b) {
2374 1
                        return $a === $b ? 0 : -1;
2375 1
                    }
2376
                ),
2377 1
                $this->iteratorClass,
2378 1
                false
2379
            );
2380
        }
2381
2382 3
        return static::create(
2383 3
            \array_values(\array_intersect($this->getArray(), $search)),
2384 3
            $this->iteratorClass,
2385 3
            false
2386
        );
2387
    }
2388
2389
    /**
2390
     * Return an array with all elements found in input array.
2391
     *
2392
     * @param array ...$array
2393
     *
2394
     * @return static
2395
     *                <p>(Immutable)</p>
2396
     */
2397 1
    public function intersectionMulti(...$array): self
2398
    {
2399 1
        return static::create(
2400 1
            \array_values(\array_intersect($this->getArray(), ...$array)),
2401 1
            $this->iteratorClass,
2402 1
            false
2403
        );
2404
    }
2405
2406
    /**
2407
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2408
     *
2409
     * @param array $search
2410
     *
2411
     * @return bool
2412
     */
2413 1
    public function intersects(array $search): bool
2414
    {
2415 1
        return $this->intersection($search)->count() > 0;
2416
    }
2417
2418
    /**
2419
     * Invoke a function on all of an array's values.
2420
     *
2421
     * @param callable $callable
2422
     * @param mixed    $arguments
2423
     *
2424
     * @return static
2425
     *                <p>(Immutable)</p>
2426
     */
2427 1
    public function invoke($callable, $arguments = []): self
2428
    {
2429
        // If one argument given for each iteration, create an array for it.
2430 1
        if (\is_array($arguments) === false) {
2431 1
            $arguments = \array_fill(
2432 1
                0,
2433 1
                $this->count(),
2434 1
                $arguments
2435
            );
2436
        }
2437
2438
        // If the callable has arguments, pass them.
2439 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...
2440 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2441
        } else {
2442 1
            $array = $this->map($callable);
2443
        }
2444
2445 1
        return static::create(
2446 1
            $array,
2447 1
            $this->iteratorClass,
2448 1
            false
2449
        );
2450
    }
2451
2452
    /**
2453
     * Check whether array is associative or not.
2454
     *
2455
     * @param bool $recursive
2456
     *
2457
     * @return bool
2458
     *              <p>Returns true if associative, false otherwise.</p>
2459
     */
2460 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...
2461
    {
2462 15
        if ($this->isEmpty()) {
2463 3
            return false;
2464
        }
2465
2466 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2467 13
            if ((string) $key !== $key) {
2468 11
                return false;
2469
            }
2470
        }
2471
2472 3
        return true;
2473
    }
2474
2475
    /**
2476
     * Check if a given key or keys are empty.
2477
     *
2478
     * @param int|int[]|string|string[]|null $keys
2479
     *
2480
     * @return bool
2481
     *              <p>Returns true if empty, false otherwise.</p>
2482
     */
2483 38
    public function isEmpty($keys = null): bool
2484
    {
2485 38
        if ($this->generator) {
2486
            return $this->getArray() === [];
2487
        }
2488
2489 38
        if ($keys === null) {
2490 38
            return $this->array === [];
2491
        }
2492
2493
        foreach ((array) $keys as $key) {
2494
            if (!empty($this->get($key))) {
2495
                return false;
2496
            }
2497
        }
2498
2499
        return true;
2500
    }
2501
2502
    /**
2503
     * Check if the current array is equal to the given "$array" or not.
2504
     *
2505
     * @param array $array
2506
     *
2507
     * @return bool
2508
     */
2509 1
    public function isEqual(array $array): bool
2510
    {
2511 1
        return $this->getArray() === $array;
2512
    }
2513
2514
    /**
2515
     * Check if the current array is a multi-array.
2516
     *
2517
     * @return bool
2518
     */
2519 22
    public function isMultiArray(): bool
2520
    {
2521
        return !(
2522 22
            \count($this->getArray(), \COUNT_NORMAL)
2523
            ===
2524 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2525
        );
2526
    }
2527
2528
    /**
2529
     * Check whether array is numeric or not.
2530
     *
2531
     * @return bool
2532
     *              <p>Returns true if numeric, false otherwise.</p>
2533
     */
2534 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...
2535
    {
2536 5
        if ($this->isEmpty()) {
2537 2
            return false;
2538
        }
2539
2540 4
        foreach ($this->keys()->getGenerator() as $key) {
2541 4
            if ((int) $key !== $key) {
2542 2
                return false;
2543
            }
2544
        }
2545
2546 2
        return true;
2547
    }
2548
2549
    /**
2550
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2551
     *
2552
     * @param bool $recursive
2553
     *
2554
     * @return bool
2555
     */
2556 9
    public function isSequential(bool $recursive = false): bool
2557
    {
2558
2559
        // recursive
2560
2561 9
        if ($recursive === true) {
2562
            return $this->array_keys_recursive($this->getArray())
2563
                   ===
2564
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2565
        }
2566
2567
        // non recursive
2568
2569 9
        return \array_keys($this->getArray())
2570
               ===
2571 9
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2572
    }
2573
2574
    /**
2575
     * @return array
2576
     */
2577
    public function jsonSerialize(): array
2578
    {
2579
        return $this->getArray();
2580
    }
2581
2582
    /**
2583
     * Gets the key/index of the element at the current internal iterator position.
2584
     *
2585
     * @return int|string|null
2586
     */
2587
    public function key()
2588
    {
2589
        return \key($this->array);
2590
    }
2591
2592
    /**
2593
     * Checks if the given key exists in the provided array.
2594
     *
2595
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2596
     *       then you need to use "Arrayy->offsetExists()".
2597
     *
2598
     * @param int|string $key the key to look for
2599
     *
2600
     * @return bool
2601
     */
2602 127
    public function keyExists($key): bool
2603
    {
2604 127
        return \array_key_exists($key, $this->array);
2605
    }
2606
2607
    /**
2608
     * Get all keys from the current array.
2609
     *
2610
     * @param bool       $recursive     [optional] <p>
2611
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2612
     *                                  </p>
2613
     * @param mixed|null $search_values [optional] <p>
2614
     *                                  If specified, then only keys containing these values are returned.
2615
     *                                  </p>
2616
     * @param bool       $strict        [optional] <p>
2617
     *                                  Determines if strict comparison (===) should be used during the search.
2618
     *                                  </p>
2619
     *
2620
     * @return static
2621
     *                <p>(Immutable) An array of all the keys in input.</p>
2622
     */
2623 29
    public function keys(
2624
        bool $recursive = false,
2625
        $search_values = null,
2626
        bool $strict = true
2627
    ): self {
2628
2629
        // recursive
2630
2631 29
        if ($recursive === true) {
2632 4
            $array = $this->array_keys_recursive(
2633 4
                null,
2634 4
                $search_values,
2635 4
                $strict
2636
            );
2637
2638 4
            return static::create(
2639 4
                $array,
2640 4
                $this->iteratorClass,
2641 4
                false
2642
            );
2643
        }
2644
2645
        // non recursive
2646
2647 28
        if ($search_values === null) {
2648
            $arrayFunction = function (): \Generator {
2649 28
                foreach ($this->getGenerator() as $key => $value) {
2650 26
                    yield $key;
2651
                }
2652 28
            };
2653
        } else {
2654
            $arrayFunction = function () use ($search_values, $strict): \Generator {
2655 1
                $is_array_tmp = \is_array($search_values);
2656
2657 1
                foreach ($this->getGenerator() as $key => $value) {
2658 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...
2659
                        (
2660 1
                            $is_array_tmp === false
2661
                            &&
2662 1
                            $strict === true
2663
                            &&
2664 1
                            $search_values === $value
2665
                        )
2666
                        ||
2667
                        (
2668 1
                            $is_array_tmp === false
2669
                            &&
2670 1
                            $strict === false
2671
                            &&
2672 1
                            $search_values == $value
2673
                        )
2674
                        ||
2675
                        (
2676 1
                            $is_array_tmp === true
2677
                            &&
2678 1
                            \in_array($value, $search_values, $strict)
2679
                        )
2680
                    ) {
2681 1
                        yield $key;
2682
                    }
2683
                }
2684 1
            };
2685
        }
2686
2687 28
        return static::create(
2688 28
            $arrayFunction,
2689 28
            $this->iteratorClass,
2690 28
            false
2691
        );
2692
    }
2693
2694
    /**
2695
     * Sort an array by key in reverse order.
2696
     *
2697
     * @param int $sort_flags [optional] <p>
2698
     *                        You may modify the behavior of the sort using the optional
2699
     *                        parameter sort_flags, for details
2700
     *                        see sort.
2701
     *                        </p>
2702
     *
2703
     * @return static
2704
     *                <p>(Mutable) Return this Arrayy object.</p>
2705
     */
2706 4
    public function krsort(int $sort_flags = 0): self
2707
    {
2708 4
        $this->generatorToArray();
2709
2710 4
        \krsort($this->array, $sort_flags);
2711
2712 4
        return $this;
2713
    }
2714
2715
    /**
2716
     * Get the last value from the current array.
2717
     *
2718
     * @return mixed
2719
     *               <p>Return null if there wasn't a element.</p>
2720
     */
2721 17
    public function last()
2722
    {
2723 17
        $key_last = $this->lastKey();
2724 17
        if ($key_last === null) {
2725 2
            return null;
2726
        }
2727
2728 15
        return $this->get($key_last);
2729
    }
2730
2731
    /**
2732
     * Get the last key from the current array.
2733
     *
2734
     * @return mixed
2735
     *               <p>Return null if there wasn't a element.</p>
2736
     */
2737 21
    public function lastKey()
2738
    {
2739 21
        $this->generatorToArray();
2740
2741 21
        return \array_key_last($this->array);
2742
    }
2743
2744
    /**
2745
     * Get the last value(s) from the current array.
2746
     *
2747
     * @param int|null $number
2748
     *
2749
     * @return static
2750
     *                <p>(Immutable)</p>
2751
     */
2752 13
    public function lastsImmutable(int $number = null): self
2753
    {
2754 13
        if ($this->isEmpty()) {
2755 1
            return static::create(
2756 1
                [],
2757 1
                $this->iteratorClass,
2758 1
                false
2759
            );
2760
        }
2761
2762 12
        if ($number === null) {
2763 8
            $poppedValue = $this->last();
2764
2765 8
            if ($poppedValue === null) {
2766 1
                $poppedValue = [$poppedValue];
2767
            } else {
2768 7
                $poppedValue = (array) $poppedValue;
2769
            }
2770
2771 8
            $arrayy = static::create(
2772 8
                $poppedValue,
2773 8
                $this->iteratorClass,
2774 8
                false
2775
            );
2776
        } else {
2777 4
            $number = (int) $number;
2778 4
            $arrayy = $this->rest(-$number);
2779
        }
2780
2781 12
        return $arrayy;
2782
    }
2783
2784
    /**
2785
     * Get the last value(s) from the current array.
2786
     *
2787
     * @param int|null $number
2788
     *
2789
     * @return static
2790
     *                <p>(Mutable)</p>
2791
     */
2792 13
    public function lastsMutable(int $number = null): self
2793
    {
2794 13
        if ($this->isEmpty()) {
2795 1
            return $this;
2796
        }
2797
2798 12
        if ($number === null) {
2799 8
            $poppedValue = $this->last();
2800
2801 8
            if ($poppedValue === null) {
2802 1
                $poppedValue = [$poppedValue];
2803
            } else {
2804 7
                $poppedValue = (array) $poppedValue;
2805
            }
2806
2807 8
            $this->array = static::create(
2808 8
                $poppedValue,
2809 8
                $this->iteratorClass,
2810 8
                false
2811 8
            )->getArray();
2812
        } else {
2813 4
            $number = (int) $number;
2814 4
            $this->array = $this->rest(-$number)->getArray();
2815
        }
2816
2817 12
        $this->generator = null;
2818
2819 12
        return $this;
2820
    }
2821
2822
    /**
2823
     * Count the values from the current array.
2824
     *
2825
     * alias: for "Arrayy->count()"
2826
     *
2827
     * @param int $mode
2828
     *
2829
     * @return int
2830
     *
2831
     * @see Arrayy::count()
2832
     */
2833 20
    public function length(int $mode = \COUNT_NORMAL): int
2834
    {
2835 20
        return $this->count($mode);
2836
    }
2837
2838
    /**
2839
     * Apply the given function to the every element of the array,
2840
     * collecting the results.
2841
     *
2842
     * @param callable $callable
2843
     * @param bool     $useKeyAsSecondParameter
2844
     * @param mixed    ...$arguments
2845
     *
2846
     * @return static
2847
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2848
     */
2849 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments)
2850
    {
2851 5
        $useArguments = \func_num_args() > 2;
2852
2853 5
        return static::create(
2854
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2855 5
                foreach ($this->getGenerator() as $key => $value) {
2856 4
                    if ($useArguments) {
2857 3
                        if ($useKeyAsSecondParameter) {
2858
                            yield $key => $callable($value, $key, ...$arguments);
2859
                        } else {
2860 3
                            yield $key => $callable($value, ...$arguments);
2861
                        }
2862
                    } else {
2863
                        /** @noinspection NestedPositiveIfStatementsInspection */
2864 4
                        if ($useKeyAsSecondParameter) {
2865
                            yield $key => $callable($value, $key);
2866
                        } else {
2867 4
                            yield $key => $callable($value);
2868
                        }
2869
                    }
2870
                }
2871 5
            },
2872 5
            $this->iteratorClass,
2873 5
            false
2874
        );
2875
    }
2876
2877
    /**
2878
     * Check if all items in current array match a truth test.
2879
     *
2880
     * @param \Closure $closure
2881
     *
2882
     * @return bool
2883
     */
2884 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...
2885
    {
2886 15
        if ($this->count() === 0) {
2887 2
            return false;
2888
        }
2889
2890 13
        foreach ($this->getGenerator() as $key => $value) {
2891 13
            $value = $closure($value, $key);
2892
2893 13
            if ($value === false) {
2894 7
                return false;
2895
            }
2896
        }
2897
2898 7
        return true;
2899
    }
2900
2901
    /**
2902
     * Check if any item in the current array matches a truth test.
2903
     *
2904
     * @param \Closure $closure
2905
     *
2906
     * @return bool
2907
     */
2908 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...
2909
    {
2910 14
        if ($this->count() === 0) {
2911 2
            return false;
2912
        }
2913
2914 12
        foreach ($this->getGenerator() as $key => $value) {
2915 12
            $value = $closure($value, $key);
2916
2917 12
            if ($value === true) {
2918 9
                return true;
2919
            }
2920
        }
2921
2922 4
        return false;
2923
    }
2924
2925
    /**
2926
     * Get the max value from an array.
2927
     *
2928
     * @return mixed
2929
     */
2930 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...
2931
    {
2932 10
        if ($this->count() === 0) {
2933 1
            return false;
2934
        }
2935
2936 9
        $max = false;
2937 9
        foreach ($this->getGenerator() as $value) {
2938
            if (
2939 9
                $max === false
2940
                ||
2941 9
                $value > $max
2942
            ) {
2943 9
                $max = $value;
2944
            }
2945
        }
2946
2947 9
        return $max;
2948
    }
2949
2950
    /**
2951
     * Merge the new $array into the current array.
2952
     *
2953
     * - keep key,value from the current array, also if the index is in the new $array
2954
     *
2955
     * @param array $array
2956
     * @param bool  $recursive
2957
     *
2958
     * @return static
2959
     *                <p>(Immutable)</p>
2960
     */
2961 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...
2962
    {
2963 25
        if ($recursive === true) {
2964 4
            $result = \array_replace_recursive($this->getArray(), $array);
2965
        } else {
2966 21
            $result = \array_replace($this->getArray(), $array);
2967
        }
2968
2969 25
        return static::create(
2970 25
            $result,
2971 25
            $this->iteratorClass,
2972 25
            false
2973
        );
2974
    }
2975
2976
    /**
2977
     * Merge the new $array into the current array.
2978
     *
2979
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2980
     * - create new indexes
2981
     *
2982
     * @param array $array
2983
     * @param bool  $recursive
2984
     *
2985
     * @return static
2986
     *                <p>(Immutable)</p>
2987
     */
2988 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...
2989
    {
2990 16
        if ($recursive === true) {
2991 4
            $result = \array_merge_recursive($this->getArray(), $array);
2992
        } else {
2993 12
            $result = \array_merge($this->getArray(), $array);
2994
        }
2995
2996 16
        return static::create(
2997 16
            $result,
2998 16
            $this->iteratorClass,
2999 16
            false
3000
        );
3001
    }
3002
3003
    /**
3004
     * Merge the the current array into the $array.
3005
     *
3006
     * - use key,value from the new $array, also if the index is in the current array
3007
     *
3008
     * @param array $array
3009
     * @param bool  $recursive
3010
     *
3011
     * @return static
3012
     *                <p>(Immutable)</p>
3013
     */
3014 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...
3015
    {
3016 16
        if ($recursive === true) {
3017 4
            $result = \array_replace_recursive($array, $this->getArray());
3018
        } else {
3019 12
            $result = \array_replace($array, $this->getArray());
3020
        }
3021
3022 16
        return static::create(
3023 16
            $result,
3024 16
            $this->iteratorClass,
3025 16
            false
3026
        );
3027
    }
3028
3029
    /**
3030
     * Merge the current array into the new $array.
3031
     *
3032
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
3033
     * - create new indexes
3034
     *
3035
     * @param array $array
3036
     * @param bool  $recursive
3037
     *
3038
     * @return static
3039
     *                <p>(Immutable)</p>
3040
     */
3041 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...
3042
    {
3043 17
        if ($recursive === true) {
3044 4
            $result = \array_merge_recursive($array, $this->getArray());
3045
        } else {
3046 13
            $result = \array_merge($array, $this->getArray());
3047
        }
3048
3049 17
        return static::create(
3050 17
            $result,
3051 17
            $this->iteratorClass,
3052 17
            false
3053
        );
3054
    }
3055
3056
    /**
3057
     * @return ArrayyMeta|static
3058
     */
3059 15
    public static function meta()
3060
    {
3061 15
        return (new ArrayyMeta())->getMetaObject(static::class);
3062
    }
3063
3064
    /**
3065
     * Get the min value from an array.
3066
     *
3067
     * @return mixed
3068
     */
3069 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...
3070
    {
3071 10
        if ($this->count() === 0) {
3072 1
            return false;
3073
        }
3074
3075 9
        $min = false;
3076 9
        foreach ($this->getGenerator() as $value) {
3077
            if (
3078 9
                $min === false
3079
                ||
3080 9
                $value < $min
3081
            ) {
3082 9
                $min = $value;
3083
            }
3084
        }
3085
3086 9
        return $min;
3087
    }
3088
3089
    /**
3090
     * Get the most used value from the array.
3091
     *
3092
     * @return mixed
3093
     *               <p>Return null if there wasn't a element.</p>
3094
     */
3095 3
    public function mostUsedValue()
3096
    {
3097 3
        return $this->countValues()->arsort()->firstKey();
3098
    }
3099
3100
    /**
3101
     * Get the most used value from the array.
3102
     *
3103
     * @param int|null $number <p>How many values you will take?</p>
3104
     *
3105
     * @return static
3106
     *                <p>(Immutable)</p>
3107
     */
3108 3
    public function mostUsedValues(int $number = null): self
3109
    {
3110 3
        return $this->countValues()->arsort()->firstsKeys($number);
3111
    }
3112
3113
    /**
3114
     * Move an array element to a new index.
3115
     *
3116
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
3117
     *
3118
     * @param int|string $from
3119
     * @param int        $to
3120
     *
3121
     * @return static
3122
     *                <p>(Immutable)</p>
3123
     */
3124 1
    public function moveElement($from, $to): self
3125
    {
3126 1
        $array = $this->getArray();
3127
3128 1
        if ((int) $from === $from) {
3129 1
            $tmp = \array_splice($array, $from, 1);
3130 1
            \array_splice($array, (int) $to, 0, $tmp);
3131 1
            $output = $array;
3132 1
        } elseif ((string) $from === $from) {
3133 1
            $indexToMove = \array_search($from, \array_keys($array), true);
3134 1
            $itemToMove = $array[$from];
3135 1
            if ($indexToMove !== false) {
3136 1
                \array_splice($array, $indexToMove, 1);
3137
            }
3138 1
            $i = 0;
3139 1
            $output = [];
3140 1
            foreach ($array as $key => $item) {
3141 1
                if ($i === $to) {
3142 1
                    $output[$from] = $itemToMove;
3143
                }
3144 1
                $output[$key] = $item;
3145 1
                ++$i;
3146
            }
3147
        } else {
3148
            $output = [];
3149
        }
3150
3151 1
        return static::create(
3152 1
            $output,
3153 1
            $this->iteratorClass,
3154 1
            false
3155
        );
3156
    }
3157
3158
    /**
3159
     * Move an array element to the first place.
3160
     *
3161
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3162
     *       loss the keys of an indexed array.
3163
     *
3164
     * @param int|string $key
3165
     *
3166
     * @return static
3167
     *                <p>(Immutable)</p>
3168
     */
3169 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...
3170
    {
3171 1
        $array = $this->getArray();
3172
3173 1
        if ($this->offsetExists($key)) {
3174 1
            $tmpValue = $this->get($key);
3175 1
            unset($array[$key]);
3176 1
            $array = [$key => $tmpValue] + $array;
3177
        }
3178
3179 1
        return static::create(
3180 1
            $array,
3181 1
            $this->iteratorClass,
3182 1
            false
3183
        );
3184
    }
3185
3186
    /**
3187
     * Move an array element to the last place.
3188
     *
3189
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3190
     *       loss the keys of an indexed array.
3191
     *
3192
     * @param int|string $key
3193
     *
3194
     * @return static
3195
     *                <p>(Immutable)</p>
3196
     */
3197 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...
3198
    {
3199 1
        $array = $this->getArray();
3200
3201 1
        if ($this->offsetExists($key)) {
3202 1
            $tmpValue = $this->get($key);
3203 1
            unset($array[$key]);
3204 1
            $array += [$key => $tmpValue];
3205
        }
3206
3207 1
        return static::create(
3208 1
            $array,
3209 1
            $this->iteratorClass,
3210 1
            false
3211
        );
3212
    }
3213
3214
    /**
3215
     * Moves the internal iterator position to the next element and returns this element.
3216
     *
3217
     * @return mixed
3218
     */
3219
    public function next()
3220
    {
3221
        return \next($this->array);
3222
    }
3223
3224
    /**
3225
     * Get the next nth keys and values from the array.
3226
     *
3227
     * @param int $step
3228
     * @param int $offset
3229
     *
3230
     * @return static
3231
     */
3232 1
    public function nth(int $step, int $offset = 0): self
3233
    {
3234
        $arrayFunction = function () use ($step, $offset): \Generator {
3235 1
            $position = 0;
3236 1
            foreach ($this->getGenerator() as $key => $value) {
3237 1
                if ($position++ % $step !== $offset) {
3238 1
                    continue;
3239
                }
3240
3241 1
                yield $key => $value;
3242
            }
3243 1
        };
3244
3245 1
        return static::create(
3246 1
            $arrayFunction,
3247 1
            $this->iteratorClass,
3248 1
            false
3249
        );
3250
    }
3251
3252
    /**
3253
     * Get a subset of the items from the given array.
3254
     *
3255
     * @param mixed[] $keys
3256
     *
3257
     * @return static
3258
     *                <p>(Immutable)</p>
3259
     */
3260 1
    public function only(array $keys): self
3261
    {
3262 1
        $array = $this->getArray();
3263
3264 1
        return static::create(
3265 1
            \array_intersect_key($array, \array_flip($keys)),
3266 1
            $this->iteratorClass,
3267 1
            false
3268
        );
3269
    }
3270
3271
    /**
3272
     * Pad array to the specified size with a given value.
3273
     *
3274
     * @param int   $size  <p>Size of the result array.</p>
3275
     * @param mixed $value <p>Empty value by default.</p>
3276
     *
3277
     * @return static
3278
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3279
     */
3280 5
    public function pad(int $size, $value): self
3281
    {
3282 5
        return static::create(
3283 5
            \array_pad($this->getArray(), $size, $value),
3284 5
            $this->iteratorClass,
3285 5
            false
3286
        );
3287
    }
3288
3289
    /**
3290
     * Partitions this array in two array according to a predicate.
3291
     * Keys are preserved in the resulting array.
3292
     *
3293
     * @param \Closure $closure
3294
     *                          <p>The predicate on which to partition.</p>
3295
     *
3296
     * @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...
3297
     *                    <p>An array with two elements. The first element contains the array
3298
     *                    of elements where the predicate returned TRUE, the second element
3299
     *                    contains the array of elements where the predicate returned FALSE.</p>
3300
     */
3301 1
    public function partition(\Closure $closure): array
3302
    {
3303
        // init
3304 1
        $matches = [];
3305 1
        $noMatches = [];
3306
3307 1
        foreach ($this->array as $key => $value) {
3308 1
            if ($closure($value, $key)) {
3309 1
                $matches[$key] = $value;
3310
            } else {
3311 1
                $noMatches[$key] = $value;
3312
            }
3313
        }
3314
3315 1
        return [self::create($matches), self::create($noMatches)];
3316
    }
3317
3318
    /**
3319
     * Pop a specified value off the end of the current array.
3320
     *
3321
     * @return mixed
3322
     *               <p>(Mutable) The popped element from the current array.</p>
3323
     */
3324 5
    public function pop()
3325
    {
3326 5
        $this->generatorToArray();
3327
3328 5
        return \array_pop($this->array);
3329
    }
3330
3331
    /**
3332
     * Prepend a (key) + value to the current array.
3333
     *
3334
     * @param mixed $value
3335
     * @param mixed $key
3336
     *
3337
     * @return static
3338
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3339
     */
3340 11
    public function prepend($value, $key = null)
3341
    {
3342 11
        $this->generatorToArray();
3343
3344 11
        if ($this->properties !== []) {
3345 3
            $this->checkType($key, $value);
3346
        }
3347
3348 9
        if ($key === null) {
3349 8
            \array_unshift($this->array, $value);
3350
        } else {
3351 2
            $this->array = [$key => $value] + $this->array;
3352
        }
3353
3354 9
        return $this;
3355
    }
3356
3357
    /**
3358
     * Add a suffix to each key.
3359
     *
3360
     * @param mixed $suffix
3361
     *
3362
     * @return static
3363
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3364
     */
3365 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...
3366
    {
3367
        // init
3368 10
        $result = [];
3369
3370 10
        foreach ($this->getGenerator() as $key => $item) {
3371 9
            if ($item instanceof self) {
3372
                $result[$key] = $item->prependToEachKey($suffix);
3373 9
            } elseif (\is_array($item) === true) {
3374
                $result[$key] = self::create(
3375
                    $item,
3376
                    $this->iteratorClass,
3377
                    false
3378
                )->prependToEachKey($suffix)
3379
                    ->toArray();
3380
            } else {
3381 9
                $result[$key . $suffix] = $item;
3382
            }
3383
        }
3384
3385 10
        return self::create(
3386 10
            $result,
3387 10
            $this->iteratorClass,
3388 10
            false
3389
        );
3390
    }
3391
3392
    /**
3393
     * Add a suffix to each value.
3394
     *
3395
     * @param mixed $suffix
3396
     *
3397
     * @return static
3398
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3399
     */
3400 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...
3401
    {
3402
        // init
3403 10
        $result = [];
3404
3405 10
        foreach ($this->getGenerator() as $key => $item) {
3406 9
            if ($item instanceof self) {
3407
                $result[$key] = $item->prependToEachValue($suffix);
3408 9
            } elseif (\is_array($item) === true) {
3409
                $result[$key] = self::create(
3410
                    $item,
3411
                    $this->iteratorClass,
3412
                    false
3413
                )->prependToEachValue($suffix)
3414
                    ->toArray();
3415 9
            } elseif (\is_object($item) === true) {
3416 1
                $result[$key] = $item;
3417
            } else {
3418 8
                $result[$key] = $item . $suffix;
3419
            }
3420
        }
3421
3422 10
        return self::create(
3423 10
            $result,
3424 10
            $this->iteratorClass,
3425 10
            false
3426
        );
3427
    }
3428
3429
    /**
3430
     * Return the value of a given key and
3431
     * delete the key.
3432
     *
3433
     * @param int|int[]|string|string[]|null $keyOrKeys
3434
     * @param mixed                          $fallback
3435
     *
3436
     * @return mixed
3437
     */
3438 1
    public function pull($keyOrKeys = null, $fallback = null)
3439
    {
3440 1
        if ($keyOrKeys === null) {
3441
            $array = $this->getArray();
3442
            $this->clear();
3443
3444
            return $array;
3445
        }
3446
3447 1
        if (\is_array($keyOrKeys) === true) {
3448 1
            $valueOrValues = [];
3449 1
            foreach ($keyOrKeys as $key) {
3450 1
                $valueOrValues[] = $this->get($key, $fallback);
3451 1
                $this->offsetUnset($key);
3452
            }
3453
        } else {
3454 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3455 1
            $this->offsetUnset($keyOrKeys);
3456
        }
3457
3458 1
        return $valueOrValues;
3459
    }
3460
3461
    /**
3462
     * Push one or more values onto the end of array at once.
3463
     *
3464
     * @param array ...$args
3465
     *
3466
     * @return static
3467
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3468
     *
3469
     * @noinspection ReturnTypeCanBeDeclaredInspection
3470
     */
3471 5
    public function push(...$args)
3472
    {
3473 5
        $this->generatorToArray();
3474
3475
        if (
3476 5
            $this->checkPropertyTypes
3477
            &&
3478 5
            $this->properties !== []
3479
        ) {
3480 1
            foreach ($args as $key => $value) {
3481 1
                $this->checkType($key, $value);
3482
            }
3483
        }
3484
3485 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...
3486
3487 5
        return $this;
3488
    }
3489
3490
    /**
3491
     * Get a random value from the current array.
3492
     *
3493
     * @param int|null $number <p>How many values you will take?</p>
3494
     *
3495
     * @return static
3496
     *                <p>(Immutable)</p>
3497
     */
3498 19
    public function randomImmutable(int $number = null): self
3499
    {
3500 19
        $this->generatorToArray();
3501
3502 19
        if ($this->count() === 0) {
3503 1
            return static::create(
3504 1
                [],
3505 1
                $this->iteratorClass,
3506 1
                false
3507
            );
3508
        }
3509
3510 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...
3511
            /** @noinspection NonSecureArrayRandUsageInspection */
3512 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3513
3514 13
            return static::create(
3515 13
                $arrayRandValue,
3516 13
                $this->iteratorClass,
3517 13
                false
3518
            );
3519
        }
3520
3521 6
        $arrayTmp = $this->array;
3522
        /** @noinspection NonSecureShuffleUsageInspection */
3523 6
        \shuffle($arrayTmp);
3524
3525 6
        return static::create(
3526 6
            $arrayTmp,
3527 6
            $this->iteratorClass,
3528 6
            false
3529 6
        )->firstsImmutable($number);
3530
    }
3531
3532
    /**
3533
     * Pick a random key/index from the keys of this array.
3534
     *
3535
     * @throws \RangeException If array is empty
3536
     *
3537
     * @return mixed
3538
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3539
     */
3540 4
    public function randomKey()
3541
    {
3542 4
        $result = $this->randomKeys(1);
3543
3544 4
        if (!isset($result[0])) {
3545
            $result[0] = null;
3546
        }
3547
3548 4
        return $result[0];
3549
    }
3550
3551
    /**
3552
     * Pick a given number of random keys/indexes out of this array.
3553
     *
3554
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3555
     *
3556
     * @throws \RangeException If array is empty
3557
     *
3558
     * @return static
3559
     *                <p>(Immutable)</p>
3560
     */
3561 13
    public function randomKeys(int $number): self
3562
    {
3563 13
        $this->generatorToArray();
3564
3565 13
        $count = $this->count();
3566
3567
        if (
3568 13
            $number === 0
3569
            ||
3570 13
            $number > $count
3571
        ) {
3572 2
            throw new \RangeException(
3573 2
                \sprintf(
3574 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3575 2
                    $number,
3576 2
                    $count
3577
                )
3578
            );
3579
        }
3580
3581 11
        $result = (array) \array_rand($this->array, $number);
3582
3583 11
        return static::create(
3584 11
            $result,
3585 11
            $this->iteratorClass,
3586 11
            false
3587
        );
3588
    }
3589
3590
    /**
3591
     * Get a random value from the current array.
3592
     *
3593
     * @param int|null $number <p>How many values you will take?</p>
3594
     *
3595
     * @return static
3596
     *                <p>(Mutable)</p>
3597
     */
3598 17
    public function randomMutable(int $number = null): self
3599
    {
3600 17
        $this->generatorToArray();
3601
3602 17
        if ($this->count() === 0) {
3603
            return static::create(
3604
                [],
3605
                $this->iteratorClass,
3606
                false
3607
            );
3608
        }
3609
3610 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...
3611
            /** @noinspection NonSecureArrayRandUsageInspection */
3612 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3613 7
            $this->array = $arrayRandValue;
3614
3615 7
            return $this;
3616
        }
3617
3618
        /** @noinspection NonSecureShuffleUsageInspection */
3619 11
        \shuffle($this->array);
3620
3621 11
        return $this->firstsMutable($number);
3622
    }
3623
3624
    /**
3625
     * Pick a random value from the values of this array.
3626
     *
3627
     * @return mixed
3628
     *               <p>Get a random value or null if there wasn't a value.</p>
3629
     */
3630 4
    public function randomValue()
3631
    {
3632 4
        $result = $this->randomImmutable();
3633
3634 4
        if (!isset($result[0])) {
3635
            $result[0] = null;
3636
        }
3637
3638 4
        return $result[0];
3639
    }
3640
3641
    /**
3642
     * Pick a given number of random values out of this array.
3643
     *
3644
     * @param int $number
3645
     *
3646
     * @return static
3647
     *                <p>(Mutable)</p>
3648
     */
3649 7
    public function randomValues(int $number): self
3650
    {
3651 7
        return $this->randomMutable($number);
3652
    }
3653
3654
    /**
3655
     * Get a random value from an array, with the ability to skew the results.
3656
     *
3657
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3658
     *
3659
     * @param array    $array
3660
     * @param int|null $number <p>How many values you will take?</p>
3661
     *
3662
     * @return static
3663
     *                <p>(Immutable)</p>
3664
     */
3665 9
    public function randomWeighted(array $array, int $number = null): self
3666
    {
3667
        // init
3668 9
        $options = [];
3669
3670 9
        foreach ($array as $option => $weight) {
3671 9
            if ($this->searchIndex($option) !== false) {
3672 2
                for ($i = 0; $i < $weight; ++$i) {
3673 1
                    $options[] = $option;
3674
                }
3675
            }
3676
        }
3677
3678 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3679
    }
3680
3681
    /**
3682
     * Reduce the current array via callable e.g. anonymous-function.
3683
     *
3684
     * @param callable $callable
3685
     * @param mixed    $init
3686
     *
3687
     * @return static
3688
     *                <p>(Immutable)</p>
3689
     */
3690 18
    public function reduce($callable, $init = []): self
3691
    {
3692 18
        if ($this->generator) {
3693 1
            $result = $init;
3694
3695 1
            foreach ($this->getGenerator() as $value) {
3696 1
                $result = $callable($result, $value);
3697
            }
3698
3699 1
            return static::create(
3700 1
                $result,
3701 1
                $this->iteratorClass,
3702 1
                false
3703
            );
3704
        }
3705
3706 18
        $result = \array_reduce($this->array, $callable, $init);
3707
3708 18
        if ($result === null) {
3709
            $this->array = [];
3710
        } else {
3711 18
            $this->array = (array) $result;
3712
        }
3713
3714 18
        return static::create(
3715 18
            $this->array,
3716 18
            $this->iteratorClass,
3717 18
            false
3718
        );
3719
    }
3720
3721
    /**
3722
     * @param bool $unique
3723
     *
3724
     * @return static
3725
     *                <p>(Immutable)</p>
3726
     */
3727 14
    public function reduce_dimension(bool $unique = true): self
3728
    {
3729
        // init
3730 14
        $result = [];
3731
3732 14
        foreach ($this->getGenerator() as $val) {
3733 12
            if (\is_array($val) === true) {
3734 5
                $result[] = (new static($val))->reduce_dimension($unique)->getArray();
3735
            } else {
3736 12
                $result[] = [$val];
3737
            }
3738
        }
3739
3740 14
        $result = $result === [] ? [] : \array_merge(...$result);
3741
3742 14
        $resultArrayy = new static($result);
3743
3744 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3745
    }
3746
3747
    /**
3748
     * Create a numerically re-indexed Arrayy object.
3749
     *
3750
     * @return static
3751
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3752
     */
3753 9
    public function reindex(): self
3754
    {
3755 9
        $this->generatorToArray();
3756
3757 9
        $this->array = \array_values($this->array);
3758
3759 9
        return $this;
3760
    }
3761
3762
    /**
3763
     * Return all items that fail the truth test.
3764
     *
3765
     * @param \Closure $closure
3766
     *
3767
     * @return static
3768
     *                <p>(Immutable)</p>
3769
     */
3770 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...
3771
    {
3772
        // init
3773 1
        $filtered = [];
3774
3775 1
        foreach ($this->getGenerator() as $key => $value) {
3776 1
            if (!$closure($value, $key)) {
3777 1
                $filtered[$key] = $value;
3778
            }
3779
        }
3780
3781 1
        return static::create(
3782 1
            $filtered,
3783 1
            $this->iteratorClass,
3784 1
            false
3785
        );
3786
    }
3787
3788
    /**
3789
     * Remove a value from the current array (optional using dot-notation).
3790
     *
3791
     * @param mixed $key
3792
     *
3793
     * @return static
3794
     *                <p>(Mutable)</p>
3795
     */
3796 18
    public function remove($key)
3797
    {
3798
        // recursive call
3799 18
        if (\is_array($key) === true) {
3800
            foreach ($key as $k) {
3801
                $this->internalRemove($k);
3802
            }
3803
3804
            return static::create(
3805
                $this->getArray(),
3806
                $this->iteratorClass,
3807
                false
3808
            );
3809
        }
3810
3811 18
        $this->internalRemove($key);
3812
3813 18
        return static::create(
3814 18
            $this->getArray(),
3815 18
            $this->iteratorClass,
3816 18
            false
3817
        );
3818
    }
3819
3820
    /**
3821
     * alias: for "Arrayy->removeValue()"
3822
     *
3823
     * @param mixed $element
3824
     *
3825
     * @return static
3826
     */
3827 8
    public function removeElement($element)
3828
    {
3829 8
        return $this->removeValue($element);
3830
    }
3831
3832
    /**
3833
     * Remove the first value from the current array.
3834
     *
3835
     * @return static
3836
     *                <p>(Immutable)</p>
3837
     */
3838 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...
3839
    {
3840 7
        $tmpArray = $this->getArray();
3841
3842 7
        \array_shift($tmpArray);
3843
3844 7
        return static::create(
3845 7
            $tmpArray,
3846 7
            $this->iteratorClass,
3847 7
            false
3848
        );
3849
    }
3850
3851
    /**
3852
     * Remove the last value from the current array.
3853
     *
3854
     * @return static
3855
     *                <p>(Immutable)</p>
3856
     */
3857 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...
3858
    {
3859 7
        $tmpArray = $this->getArray();
3860
3861 7
        \array_pop($tmpArray);
3862
3863 7
        return static::create(
3864 7
            $tmpArray,
3865 7
            $this->iteratorClass,
3866 7
            false
3867
        );
3868
    }
3869
3870
    /**
3871
     * Removes a particular value from an array (numeric or associative).
3872
     *
3873
     * @param mixed $value
3874
     *
3875
     * @return static
3876
     *                <p>(Immutable)</p>
3877
     */
3878 8
    public function removeValue($value): self
3879
    {
3880 8
        $this->generatorToArray();
3881
3882
        // init
3883 8
        $isSequentialArray = $this->isSequential();
3884
3885 8
        foreach ($this->array as $key => $item) {
3886 7
            if ($item === $value) {
3887 7
                unset($this->array[$key]);
3888
            }
3889
        }
3890
3891 8
        if ($isSequentialArray) {
3892 6
            $this->array = \array_values($this->array);
3893
        }
3894
3895 8
        return static::create(
3896 8
            $this->array,
3897 8
            $this->iteratorClass,
3898 8
            false
3899
        );
3900
    }
3901
3902
    /**
3903
     * Generate array of repeated arrays.
3904
     *
3905
     * @param int $times <p>How many times has to be repeated.</p>
3906
     *
3907
     * @return static
3908
     *                <p>(Immutable)</p>
3909
     */
3910 1
    public function repeat($times): self
3911
    {
3912 1
        if ($times === 0) {
3913 1
            return static::create([], $this->iteratorClass);
3914
        }
3915
3916 1
        return static::create(
3917 1
            \array_fill(0, (int) $times, $this->getArray()),
3918 1
            $this->iteratorClass,
3919 1
            false
3920
        );
3921
    }
3922
3923
    /**
3924
     * Replace a key with a new key/value pair.
3925
     *
3926
     * @param mixed $replace
3927
     * @param mixed $key
3928
     * @param mixed $value
3929
     *
3930
     * @return static
3931
     *                <p>(Immutable)</p>
3932
     */
3933 2
    public function replace($replace, $key, $value): self
3934
    {
3935 2
        $that = clone $this;
3936
3937 2
        return $that->remove($replace)
3938 2
            ->set($key, $value);
3939
    }
3940
3941
    /**
3942
     * Create an array using the current array as values and the other array as keys.
3943
     *
3944
     * @param array $keys <p>An array of keys.</p>
3945
     *
3946
     * @return static
3947
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3948
     */
3949 2
    public function replaceAllKeys(array $keys): self
3950
    {
3951 2
        return static::create(
3952 2
            \array_combine($keys, $this->getArray()),
3953 2
            $this->iteratorClass,
3954 2
            false
3955
        );
3956
    }
3957
3958
    /**
3959
     * Create an array using the current array as keys and the other array as values.
3960
     *
3961
     * @param array $array <p>An array o values.</p>
3962
     *
3963
     * @return static
3964
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3965
     */
3966 2
    public function replaceAllValues(array $array): self
3967
    {
3968 2
        return static::create(
3969 2
            \array_combine($this->array, $array),
3970 2
            $this->iteratorClass,
3971 2
            false
3972
        );
3973
    }
3974
3975
    /**
3976
     * Replace the keys in an array with another set.
3977
     *
3978
     * @param array $keys <p>An array of keys matching the array's size</p>
3979
     *
3980
     * @return static
3981
     *                <p>(Immutable)</p>
3982
     */
3983 1
    public function replaceKeys(array $keys): self
3984
    {
3985 1
        $values = \array_values($this->getArray());
3986 1
        $result = \array_combine($keys, $values);
3987
3988 1
        return static::create(
3989 1
            $result,
3990 1
            $this->iteratorClass,
3991 1
            false
3992
        );
3993
    }
3994
3995
    /**
3996
     * Replace the first matched value in an array.
3997
     *
3998
     * @param mixed $search      <p>The value to replace.</p>
3999
     * @param mixed $replacement <p>The value to replace.</p>
4000
     *
4001
     * @return static
4002
     *                <p>(Immutable)</p>
4003
     */
4004 3
    public function replaceOneValue($search, $replacement = ''): self
4005
    {
4006 3
        $array = $this->getArray();
4007 3
        $key = \array_search($search, $array, true);
4008
4009 3
        if ($key !== false) {
4010 3
            $array[$key] = $replacement;
4011
        }
4012
4013 3
        return static::create(
4014 3
            $array,
4015 3
            $this->iteratorClass,
4016 3
            false
4017
        );
4018
    }
4019
4020
    /**
4021
     * Replace values in the current array.
4022
     *
4023
     * @param mixed $search      <p>The value to replace.</p>
4024
     * @param mixed $replacement <p>What to replace it with.</p>
4025
     *
4026
     * @return static
4027
     *                <p>(Immutable)</p>
4028
     */
4029 1
    public function replaceValues($search, $replacement = ''): self
4030
    {
4031 1
        return $this->each(
4032
            static function ($value) use ($search, $replacement) {
4033 1
                return \str_replace($search, $replacement, $value);
4034 1
            }
4035
        );
4036
    }
4037
4038
    /**
4039
     * Get the last elements from index $from until the end of this array.
4040
     *
4041
     * @param int $from
4042
     *
4043
     * @return static
4044
     *                <p>(Immutable)</p>
4045
     */
4046 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...
4047
    {
4048 15
        $tmpArray = $this->getArray();
4049
4050 15
        return static::create(
4051 15
            \array_splice($tmpArray, $from),
4052 15
            $this->iteratorClass,
4053 15
            false
4054
        );
4055
    }
4056
4057
    /**
4058
     * Return the array in the reverse order.
4059
     *
4060
     * @return static
4061
     *                <p>(Mutable) Return this Arrayy object.</p>
4062
     */
4063 9
    public function reverse(): self
4064
    {
4065 9
        $this->generatorToArray();
4066
4067 9
        $this->array = \array_reverse($this->array);
4068
4069 9
        return $this;
4070
    }
4071
4072
    /**
4073
     * Sort an array in reverse order.
4074
     *
4075
     * @param int $sort_flags [optional] <p>
4076
     *                        You may modify the behavior of the sort using the optional
4077
     *                        parameter sort_flags, for details
4078
     *                        see sort.
4079
     *                        </p>
4080
     *
4081
     * @return static
4082
     *                <p>(Mutable) Return this Arrayy object.</p>
4083
     */
4084 4
    public function rsort(int $sort_flags = 0): self
4085
    {
4086 4
        $this->generatorToArray();
4087
4088 4
        \rsort($this->array, $sort_flags);
4089
4090 4
        return $this;
4091
    }
4092
4093
    /**
4094
     * Search for the first index of the current array via $value.
4095
     *
4096
     * @param mixed $value
4097
     *
4098
     * @return false|float|int|string
4099
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
4100
     */
4101 21
    public function searchIndex($value)
4102
    {
4103 21
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
4104 20
            if ($value === $valueFromArray) {
4105 10
                return $keyFromArray;
4106
            }
4107
        }
4108
4109 11
        return false;
4110
    }
4111
4112
    /**
4113
     * Search for the value of the current array via $index.
4114
     *
4115
     * @param mixed $index
4116
     *
4117
     * @return static
4118
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
4119
     */
4120 9
    public function searchValue($index): self
4121
    {
4122 9
        $this->generatorToArray();
4123
4124
        // init
4125 9
        $return = [];
4126
4127 9
        if ($this->array === []) {
4128
            return static::create(
4129
                [],
4130
                $this->iteratorClass,
4131
                false
4132
            );
4133
        }
4134
4135
        // php cast "bool"-index into "int"-index
4136 9
        if ((bool) $index === $index) {
4137 1
            $index = (int) $index;
4138
        }
4139
4140 9
        if ($this->offsetExists($index)) {
4141 7
            $return = [$this->array[$index]];
4142
        }
4143
4144 9
        return static::create(
4145 9
            $return,
4146 9
            $this->iteratorClass,
4147 9
            false
4148
        );
4149
    }
4150
4151
    /**
4152
     * Set a value for the current array (optional using dot-notation).
4153
     *
4154
     * @param string $key   <p>The key to set.</p>
4155
     * @param mixed  $value <p>Its value.</p>
4156
     *
4157
     * @return static
4158
     *                <p>(Mutable)</p>
4159
     */
4160 18
    public function set($key, $value): self
4161
    {
4162 18
        $this->generatorToArray();
4163
4164 18
        $this->internalSet($key, $value);
4165
4166 18
        return $this;
4167
    }
4168
4169
    /**
4170
     * Get a value from a array and set it if it was not.
4171
     *
4172
     * WARNING: this method only set the value, if the $key is not already set
4173
     *
4174
     * @param mixed $key      <p>The key</p>
4175
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
4176
     *
4177
     * @return mixed
4178
     *               <p>(Mutable)</p>
4179
     */
4180 11
    public function setAndGet($key, $fallback = null)
4181
    {
4182 11
        $this->generatorToArray();
4183
4184
        // If the key doesn't exist, set it.
4185 11
        if (!$this->has($key)) {
4186 4
            $this->array = $this->set($key, $fallback)->getArray();
4187
        }
4188
4189 11
        return $this->get($key);
4190
    }
4191
4192
    /**
4193
     * Shifts a specified value off the beginning of array.
4194
     *
4195
     * @return mixed
4196
     *               <p>(Mutable) A shifted element from the current array.</p>
4197
     */
4198 5
    public function shift()
4199
    {
4200 5
        $this->generatorToArray();
4201
4202 5
        return \array_shift($this->array);
4203
    }
4204
4205
    /**
4206
     * Shuffle the current array.
4207
     *
4208
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
4209
     * @param array $array  [optional]
4210
     *
4211
     * @return static
4212
     *                <p>(Immutable)</p>
4213
     */
4214 2
    public function shuffle(bool $secure = false, array $array = null): self
4215
    {
4216 2
        if ($array === null) {
4217 2
            $array = $this->getArray();
4218
        }
4219
4220 2
        if ($secure !== true) {
4221
            /** @noinspection NonSecureShuffleUsageInspection */
4222 2
            \shuffle($array);
4223
        } else {
4224 1
            $size = \count($array, \COUNT_NORMAL);
4225 1
            $keys = \array_keys($array);
4226 1
            for ($i = $size - 1; $i > 0; --$i) {
4227
                try {
4228 1
                    $r = \random_int(0, $i);
4229
                } catch (\Exception $e) {
4230
                    /** @noinspection RandomApiMigrationInspection */
4231
                    $r = \mt_rand(0, $i);
4232
                }
4233 1
                if ($r !== $i) {
4234
                    $temp = $array[$keys[$r]];
4235
                    $array[$keys[$r]] = $array[$keys[$i]];
4236
                    $array[$keys[$i]] = $temp;
4237
                }
4238
            }
4239
4240
            // reset indices
4241 1
            $array = \array_values($array);
4242
        }
4243
4244 2
        foreach ($array as $key => $value) {
4245
            // check if recursive is needed
4246 2
            if (\is_array($value) === true) {
4247
                $array[$key] = $this->shuffle($secure, $value);
4248
            }
4249
        }
4250
4251 2
        return static::create(
4252 2
            $array,
4253 2
            $this->iteratorClass,
4254 2
            false
4255
        );
4256
    }
4257
4258
    /**
4259
     * Count the values from the current array.
4260
     *
4261
     * alias: for "Arrayy->count()"
4262
     *
4263
     * @param int $mode
4264
     *
4265
     * @return int
4266
     */
4267 20
    public function size(int $mode = \COUNT_NORMAL): int
4268
    {
4269 20
        return $this->count($mode);
4270
    }
4271
4272
    /**
4273
     * Checks whether array has exactly $size items.
4274
     *
4275
     * @param int $size
4276
     *
4277
     * @return bool
4278
     */
4279 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...
4280
    {
4281
        // init
4282 1
        $itemsTempCount = 0;
4283
4284 1
        foreach ($this->getGenerator() as $key => $value) {
4285 1
            ++$itemsTempCount;
4286 1
            if ($itemsTempCount > $size) {
4287 1
                return false;
4288
            }
4289
        }
4290
4291 1
        return $itemsTempCount === $size;
4292
    }
4293
4294
    /**
4295
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4296
     * smaller than $fromSize.
4297
     *
4298
     * @param int $fromSize
4299
     * @param int $toSize
4300
     *
4301
     * @return bool
4302
     */
4303 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4304
    {
4305 1
        if ($fromSize > $toSize) {
4306 1
            $tmp = $toSize;
4307 1
            $toSize = $fromSize;
4308 1
            $fromSize = $tmp;
4309
        }
4310
4311
        // init
4312 1
        $itemsTempCount = 0;
4313
4314 1
        foreach ($this->getGenerator() as $key => $value) {
4315 1
            ++$itemsTempCount;
4316 1
            if ($itemsTempCount > $toSize) {
4317 1
                return false;
4318
            }
4319
        }
4320
4321 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4322
    }
4323
4324
    /**
4325
     * Checks whether array has more than $size items.
4326
     *
4327
     * @param int $size
4328
     *
4329
     * @return bool
4330
     */
4331 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...
4332
    {
4333
        // init
4334 1
        $itemsTempCount = 0;
4335
4336 1
        foreach ($this->getGenerator() as $key => $value) {
4337 1
            ++$itemsTempCount;
4338 1
            if ($itemsTempCount > $size) {
4339 1
                return true;
4340
            }
4341
        }
4342
4343 1
        return $itemsTempCount > $size;
4344
    }
4345
4346
    /**
4347
     * Checks whether array has less than $size items.
4348
     *
4349
     * @param int $size
4350
     *
4351
     * @return bool
4352
     */
4353 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...
4354
    {
4355
        // init
4356 1
        $itemsTempCount = 0;
4357
4358 1
        foreach ($this->getGenerator() as $key => $value) {
4359 1
            ++$itemsTempCount;
4360 1
            if ($itemsTempCount > $size) {
4361 1
                return false;
4362
            }
4363
        }
4364
4365 1
        return $itemsTempCount < $size;
4366
    }
4367
4368
    /**
4369
     * Counts all elements in an array, or something in an object.
4370
     *
4371
     * <p>
4372
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4373
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4374
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4375
     * implemented and used in PHP.
4376
     * </p>
4377
     *
4378
     * @return int
4379
     *             <p>
4380
     *             The number of elements in var, which is
4381
     *             typically an array, since anything else will have one
4382
     *             element.
4383
     *             </p>
4384
     *             <p>
4385
     *             If var is not an array or an object with
4386
     *             implemented Countable interface,
4387
     *             1 will be returned.
4388
     *             There is one exception, if var is &null;,
4389
     *             0 will be returned.
4390
     *             </p>
4391
     *             <p>
4392
     *             Caution: count may return 0 for a variable that isn't set,
4393
     *             but it may also return 0 for a variable that has been initialized with an
4394
     *             empty array. Use isset to test if a variable is set.
4395
     *             </p>
4396
     */
4397 10
    public function sizeRecursive(): int
4398
    {
4399 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4400
    }
4401
4402
    /**
4403
     * Extract a slice of the array.
4404
     *
4405
     * @param int      $offset       <p>Slice begin index.</p>
4406
     * @param int|null $length       <p>Length of the slice.</p>
4407
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4408
     *
4409
     * @return static
4410
     *                <p>A slice of the original array with length $length.</p>
4411
     */
4412 5
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
4413
    {
4414 5
        return static::create(
4415 5
            \array_slice(
4416 5
                $this->getArray(),
4417 5
                $offset,
4418 5
                $length,
4419 5
                $preserveKeys
4420
            ),
4421 5
            $this->iteratorClass,
4422 5
            false
4423
        );
4424
    }
4425
4426
    /**
4427
     * Sort the current array and optional you can keep the keys.
4428
     *
4429
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4430
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4431
     *                              <strong>SORT_NATURAL</strong></p>
4432
     * @param bool       $keepKeys
4433
     *
4434
     * @return static
4435
     *                <p>(Mutable) Return this Arrayy object.</p>
4436
     */
4437 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4438
    {
4439 20
        $this->generatorToArray();
4440
4441 20
        return $this->sorting(
4442 20
            $this->array,
4443 20
            $direction,
4444 20
            $strategy,
4445 20
            $keepKeys
4446
        );
4447
    }
4448
4449
    /**
4450
     * Sort the current array by key.
4451
     *
4452
     * @see http://php.net/manual/en/function.ksort.php
4453
     * @see http://php.net/manual/en/function.krsort.php
4454
     *
4455
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4456
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4457
     *                              <strong>SORT_NATURAL</strong></p>
4458
     *
4459
     * @return static
4460
     *                <p>(Mutable) Return this Arrayy object.</p>
4461
     */
4462 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4463
    {
4464 18
        $this->generatorToArray();
4465
4466 18
        $this->sorterKeys($this->array, $direction, $strategy);
4467
4468 18
        return $this;
4469
    }
4470
4471
    /**
4472
     * Sort the current array by value.
4473
     *
4474
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4475
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4476
     *                              <strong>SORT_NATURAL</strong></p>
4477
     *
4478
     * @return static
4479
     *                <p>(Mutable)</p>
4480
     */
4481 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4482
    {
4483 1
        return $this->sort($direction, $strategy, true);
4484
    }
4485
4486
    /**
4487
     * Sort the current array by value.
4488
     *
4489
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4490
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4491
     *                              <strong>SORT_NATURAL</strong></p>
4492
     *
4493
     * @return static
4494
     *                <p>(Mutable)</p>
4495
     */
4496 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4497
    {
4498 1
        return $this->sort($direction, $strategy, false);
4499
    }
4500
4501
    /**
4502
     * Sort a array by value, by a closure or by a property.
4503
     *
4504
     * - If the sorter is null, the array is sorted naturally.
4505
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4506
     *
4507
     * @param callable|string|null $sorter
4508
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4509
     *                                        <strong>SORT_DESC</strong></p>
4510
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4511
     *                                        <strong>SORT_NATURAL</strong></p>
4512
     *
4513
     * @return static
4514
     *                <p>(Immutable)</p>
4515
     */
4516 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4517
    {
4518 1
        $array = $this->getArray();
4519 1
        $direction = $this->getDirection($direction);
4520
4521
        // Transform all values into their results.
4522 1
        if ($sorter) {
4523 1
            $arrayy = static::create(
4524 1
                $array,
4525 1
                $this->iteratorClass,
4526 1
                false
4527
            );
4528
4529 1
            $results = $arrayy->each(
4530
                function ($value) use ($sorter) {
4531 1
                    if (\is_callable($sorter) === true) {
4532 1
                        return $sorter($value);
4533
                    }
4534
4535 1
                    return $this->get($sorter);
4536 1
                }
4537
            );
4538
4539 1
            $results = $results->getArray();
4540
        } else {
4541 1
            $results = $array;
4542
        }
4543
4544
        // Sort by the results and replace by original values
4545 1
        \array_multisort($results, $direction, $strategy, $array);
4546
4547 1
        return static::create(
4548 1
            $array,
4549 1
            $this->iteratorClass,
4550 1
            false
4551
        );
4552
    }
4553
4554
    /**
4555
     * @param int      $offset
4556
     * @param int|null $length
4557
     * @param array    $replacement
4558
     *
4559
     * @return static
4560
     *                <p>(Immutable)</p>
4561
     */
4562 1
    public function splice(int $offset, int $length = null, $replacement = []): self
4563
    {
4564 1
        $tmpArray = $this->getArray();
4565
4566 1
        \array_splice(
4567 1
            $tmpArray,
4568 1
            $offset,
4569 1
            $length ?? $this->count(),
4570 1
            $replacement
4571
        );
4572
4573 1
        return static::create(
4574 1
            $tmpArray,
4575 1
            $this->iteratorClass,
4576 1
            false
4577
        );
4578
    }
4579
4580
    /**
4581
     * Split an array in the given amount of pieces.
4582
     *
4583
     * @param int  $numberOfPieces
4584
     * @param bool $keepKeys
4585
     *
4586
     * @return static
4587
     *                <p>(Immutable)</p>
4588
     */
4589 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4590
    {
4591 1
        $this->generatorToArray();
4592
4593 1
        $count = $this->count();
4594
4595 1
        if ($count === 0) {
4596 1
            $result = [];
4597
        } else {
4598 1
            $splitSize = (int) \ceil($count / $numberOfPieces);
4599 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4600
        }
4601
4602 1
        return static::create(
4603 1
            $result,
4604 1
            $this->iteratorClass,
4605 1
            false
4606
        );
4607
    }
4608
4609
    /**
4610
     * Stripe all empty items.
4611
     *
4612
     * @return static
4613
     *                <p>(Immutable)</p>
4614
     */
4615 1
    public function stripEmpty(): self
4616
    {
4617 1
        return $this->filter(
4618
            static function ($item) {
4619 1
                if ($item === null) {
4620 1
                    return false;
4621
                }
4622
4623 1
                return (bool) \trim((string) $item);
4624 1
            }
4625
        );
4626
    }
4627
4628
    /**
4629
     * Swap two values between positions by key.
4630
     *
4631
     * @param int|string $swapA <p>a key in the array</p>
4632
     * @param int|string $swapB <p>a key in the array</p>
4633
     *
4634
     * @return static
4635
     *                <p>(Immutable)</p>
4636
     */
4637 1
    public function swap($swapA, $swapB): self
4638
    {
4639 1
        $array = $this->getArray();
4640
4641 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4642
4643 1
        return static::create(
4644 1
            $array,
4645 1
            $this->iteratorClass,
4646 1
            false
4647
        );
4648
    }
4649
4650
    /**
4651
     * alias: for "Arrayy->getArray()"
4652
     *
4653
     * @param bool $convertAllArrayyElements
4654
     *
4655
     * @return array
4656
     *
4657
     * @see Arrayy::getArray()
4658
     */
4659 241
    public function toArray(bool $convertAllArrayyElements = false): array
4660
    {
4661 241
        return $this->getArray($convertAllArrayyElements);
4662
    }
4663
4664
    /**
4665
     * Convert the current array to JSON.
4666
     *
4667
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4668
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4669
     *
4670
     * @return string
4671
     */
4672 7
    public function toJson(int $options = 0, int $depth = 512): string
4673
    {
4674 7
        $return = \json_encode($this->getArray(), $options, $depth);
4675 7
        if ($return === false) {
4676
            return '';
4677
        }
4678
4679 7
        return $return;
4680
    }
4681
4682
    /**
4683
     * @param string[]|null $items
4684
     * @param string[]      $helper
4685
     *
4686
     * @return static
4687
     */
4688 1
    public function toPermutation(array $items = null, array $helper = []): self
4689
    {
4690
        // init
4691 1
        $return = [];
4692
4693 1
        if ($items === null) {
4694 1
            $items = $this->getArray();
4695
        }
4696
4697 1
        if (empty($items)) {
4698 1
            $return[] = $helper;
4699
        } else {
4700 1
            for ($i = \count($items) - 1; $i >= 0; --$i) {
4701 1
                $new_items = $items;
4702 1
                $new_helper = $helper;
4703 1
                list($tmp_helper) = \array_splice($new_items, $i, 1);
4704
                /** @noinspection PhpSillyAssignmentInspection */
4705
                /** @var string[] $new_items */
4706 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...
4707 1
                \array_unshift($new_helper, $tmp_helper);
4708
                /** @noinspection SlowArrayOperationsInLoopInspection */
4709 1
                $return = \array_merge(
4710 1
                    $return,
4711 1
                    $this->toPermutation($new_items, $new_helper)->toArray()
4712
                );
4713
            }
4714
        }
4715
4716 1
        return static::create(
4717 1
            $return,
4718 1
            $this->iteratorClass,
4719 1
            false
4720
        );
4721
    }
4722
4723
    /**
4724
     * Implodes array to a string with specified separator.
4725
     *
4726
     * @param string $separator [optional] <p>The element's separator.</p>
4727
     *
4728
     * @return string
4729
     *                <p>The string representation of array, separated by ",".</p>
4730
     */
4731 19
    public function toString(string $separator = ','): string
4732
    {
4733 19
        return $this->implode($separator);
4734
    }
4735
4736
    /**
4737
     * Return a duplicate free copy of the current array.
4738
     *
4739
     * @return static
4740
     *                <p>(Mutable)</p>
4741
     */
4742 13
    public function unique(): self
4743
    {
4744
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4745
4746 13
        $this->array = $this->reduce(
4747
            static function ($resultArray, $value) {
4748 12
                if (!\in_array($value, $resultArray, true)) {
4749 12
                    $resultArray[] = $value;
4750
                }
4751
4752 12
                return $resultArray;
4753 13
            },
4754 13
            []
4755 13
        )->getArray();
4756 13
        $this->generator = null;
4757
4758 13
        return $this;
4759
    }
4760
4761
    /**
4762
     * Return a duplicate free copy of the current array. (with the old keys)
4763
     *
4764
     * @return static
4765
     *                <p>(Mutable)</p>
4766
     */
4767 11
    public function uniqueKeepIndex(): self
4768
    {
4769
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4770
4771
        // init
4772 11
        $array = $this->getArray();
4773
4774 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...
4775 11
            \array_keys($array),
4776
            static function ($resultArray, $key) use ($array) {
4777 10
                if (!\in_array($array[$key], $resultArray, true)) {
4778 10
                    $resultArray[$key] = $array[$key];
4779
                }
4780
4781 10
                return $resultArray;
4782 11
            },
4783 11
            []
4784
        );
4785 11
        $this->generator = null;
4786
4787 11
        if ($this->array === null) {
4788
            $this->array = [];
4789
        } else {
4790 11
            $this->array = (array) $this->array;
4791
        }
4792
4793 11
        return $this;
4794
    }
4795
4796
    /**
4797
     * alias: for "Arrayy->unique()"
4798
     *
4799
     * @return static
4800
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4801
     *
4802
     * @see Arrayy::unique()
4803
     */
4804 10
    public function uniqueNewIndex(): self
4805
    {
4806 10
        return $this->unique();
4807
    }
4808
4809
    /**
4810
     * Prepends one or more values to the beginning of array at once.
4811
     *
4812
     * @param array ...$args
4813
     *
4814
     * @return static
4815
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4816
     */
4817 4
    public function unshift(...$args): self
4818
    {
4819 4
        $this->generatorToArray();
4820
4821 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...
4822
4823 4
        return $this;
4824
    }
4825
4826
    /**
4827
     * Tests whether the given closure retrun something valid for all elements of this array.
4828
     *
4829
     * @param \Closure $closure the predicate
4830
     *
4831
     * @return bool
4832
     *              <p>TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.</p>
4833
     */
4834 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...
4835
    {
4836 1
        foreach ($this->getGenerator() as $key => $value) {
4837 1
            if (!$closure($value, $key)) {
4838 1
                return false;
4839
            }
4840
        }
4841
4842 1
        return true;
4843
    }
4844
4845
    /**
4846
     * Get all values from a array.
4847
     *
4848
     * @return static
4849
     *                <p>(Immutable)</p>
4850
     */
4851 2
    public function values(): self
4852
    {
4853 2
        return static::create(
4854
            function () {
4855
                /** @noinspection YieldFromCanBeUsedInspection */
4856 2
                foreach ($this->getGenerator() as $value) {
4857 2
                    yield $value;
4858
                }
4859 2
            },
4860 2
            $this->iteratorClass,
4861 2
            false
4862
        );
4863
    }
4864
4865
    /**
4866
     * Apply the given function to every element in the array, discarding the results.
4867
     *
4868
     * @param callable $callable
4869
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4870
     *
4871
     * @return static
4872
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4873
     */
4874 12
    public function walk($callable, bool $recursive = false): self
4875
    {
4876 12
        $this->generatorToArray();
4877
4878 12
        if ($recursive === true) {
4879 6
            \array_walk_recursive($this->array, $callable);
4880
        } else {
4881 6
            \array_walk($this->array, $callable);
4882
        }
4883
4884 12
        return $this;
4885
    }
4886
4887
    /**
4888
     * Returns a collection of matching items.
4889
     *
4890
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
4891
     * @param mixed  $value                 the value to match
4892
     *
4893
     * @throws \InvalidArgumentException if property or method is not defined
4894
     *
4895
     * @return static
4896
     *
4897
     * @psalm-return static<T>
4898
     */
4899 1
    public function where(string $keyOrPropertyOrMethod, $value): self
4900
    {
4901 1
        return $this->filter(
4902
            function ($item) use ($keyOrPropertyOrMethod, $value) {
4903 1
                $accessorValue = $this->extractValue(
4904 1
                    $item,
4905 1
                    $keyOrPropertyOrMethod
4906
                );
4907
4908 1
                return $accessorValue === $value;
4909 1
            }
4910
        );
4911
    }
4912
4913
    /**
4914
     * Convert an array into a object.
4915
     *
4916
     * @param array $array PHP array
4917
     *
4918
     * @return \stdClass
4919
     */
4920 4
    final protected static function arrayToObject(array $array = []): \stdClass
4921
    {
4922
        // init
4923 4
        $object = new \stdClass();
4924
4925 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4926 1
            return $object;
4927
        }
4928
4929 3
        foreach ($array as $name => $value) {
4930 3
            if (\is_array($value) === true) {
4931 1
                $object->{$name} = static::arrayToObject($value);
4932
            } else {
4933 3
                $object->{$name} = $value;
4934
            }
4935
        }
4936
4937 3
        return $object;
4938
    }
4939
4940
    /**
4941
     * @param array|\Generator|null $input         <p>
4942
     *                                             An array containing keys to return.
4943
     *                                             </p>
4944
     * @param mixed|null            $search_values [optional] <p>
4945
     *                                             If specified, then only keys containing these values are returned.
4946
     *                                             </p>
4947
     * @param bool                  $strict        [optional] <p>
4948
     *                                             Determines if strict comparison (===) should be used during the
4949
     *                                             search.
4950
     *                                             </p>
4951
     *
4952
     * @return array
4953
     *               <p>an array of all the keys in input</p>
4954
     */
4955 11
    protected function array_keys_recursive(
4956
        $input = null,
4957
        $search_values = null,
4958
        bool $strict = true
4959
    ): array {
4960
        // init
4961 11
        $keys = [];
4962 11
        $keysTmp = [];
4963
4964 11
        if ($input === null) {
4965 4
            $input = $this->getGenerator();
4966
        }
4967
4968 11
        if ($search_values === null) {
4969 11
            foreach ($input as $key => $value) {
4970 11
                $keys[] = $key;
4971
4972
                // check if recursive is needed
4973 11
                if (\is_array($value) === true) {
4974 4
                    $keysTmp[] = $this->array_keys_recursive($value);
4975
                }
4976
            }
4977
        } else {
4978 1
            $is_array_tmp = \is_array($search_values);
4979
4980 1
            foreach ($input as $key => $value) {
4981 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...
4982
                    (
4983 1
                        $is_array_tmp === false
4984
                        &&
4985 1
                        $strict === true
4986
                        &&
4987 1
                        $search_values === $value
4988
                    )
4989
                    ||
4990
                    (
4991 1
                        $is_array_tmp === false
4992
                        &&
4993 1
                        $strict === false
4994
                        &&
4995 1
                        $search_values == $value
4996
                    )
4997
                    ||
4998
                    (
4999 1
                        $is_array_tmp === true
5000
                        &&
5001 1
                        \in_array($value, $search_values, $strict)
5002
                    )
5003
                ) {
5004 1
                    $keys[] = $key;
5005
                }
5006
5007
                // check if recursive is needed
5008 1
                if (\is_array($value) === true) {
5009 1
                    $keysTmp[] = $this->array_keys_recursive($value);
5010
                }
5011
            }
5012
        }
5013
5014 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
5015
    }
5016
5017
    /**
5018
     * @param mixed      $path
5019
     * @param callable   $callable
5020
     * @param array|null $currentOffset
5021
     *
5022
     * @return void
5023
     */
5024 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
5025
    {
5026 4
        $this->generatorToArray();
5027
5028 4
        if ($currentOffset === null) {
5029 4
            $currentOffset = &$this->array;
5030
        }
5031
5032 4
        $explodedPath = \explode($this->pathSeparator, $path);
5033 4
        if ($explodedPath === false) {
5034
            return;
5035
        }
5036
5037 4
        $nextPath = \array_shift($explodedPath);
5038
5039 4
        if (!isset($currentOffset[$nextPath])) {
5040
            return;
5041
        }
5042
5043 4
        if (!empty($explodedPath)) {
5044 1
            $this->callAtPath(
5045 1
                \implode($this->pathSeparator, $explodedPath),
5046 1
                $callable,
5047 1
                $currentOffset[$nextPath]
5048
            );
5049
        } else {
5050 4
            $callable($currentOffset[$nextPath]);
5051
        }
5052 4
    }
5053
5054
    /**
5055
     * Extracts the value of the given property or method from the object.
5056
     *
5057
     * @param \Arrayy\Arrayy $object                <p>The object to extract the value from.</p>
5058
     * @param string         $keyOrPropertyOrMethod <p>The property or method for which the
5059
     *                                              value should be extracted.</p>
5060
     *
5061
     * @throws \InvalidArgumentException if the method or property is not defined
5062
     *
5063
     * @return mixed
5064
     *               <p>The value extracted from the specified property or method.</p>
5065
     */
5066 2
    final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
5067
    {
5068 2
        if (isset($object[$keyOrPropertyOrMethod])) {
5069 2
            $return = $object->get($keyOrPropertyOrMethod);
5070
5071 2
            if ($return instanceof self) {
5072 1
                return $return->getArray();
5073
            }
5074
5075 1
            return $return;
5076
        }
5077
5078
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
5079
            return $object->{$keyOrPropertyOrMethod};
5080
        }
5081
5082
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
5083
            return $object->{$keyOrPropertyOrMethod}();
5084
        }
5085
5086
        throw new \InvalidArgumentException(\sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \gettype($object)));
5087
    }
5088
5089
    /**
5090
     * create a fallback for array
5091
     *
5092
     * 1. use the current array, if it's a array
5093
     * 2. fallback to empty array, if there is nothing
5094
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
5095
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
5096
     * 5. call "__toArray()" on object, if the method exists
5097
     * 6. cast a string or object with "__toString()" into an array
5098
     * 7. throw a "InvalidArgumentException"-Exception
5099
     *
5100
     * @param mixed $data
5101
     *
5102
     * @throws \InvalidArgumentException
5103
     *
5104
     * @return array
5105
     */
5106 1066
    protected function fallbackForArray(&$data): array
5107
    {
5108 1066
        $data = $this->internalGetArray($data);
5109
5110 1066
        if ($data === null) {
5111 2
            throw new \InvalidArgumentException('Passed value should be a array');
5112
        }
5113
5114 1064
        return $data;
5115
    }
5116
5117
    /**
5118
     * @return bool
5119
     *
5120
     * @noinspection ReturnTypeCanBeDeclaredInspection
5121
     */
5122 982
    protected function generatorToArray()
5123
    {
5124 982
        if ($this->generator) {
5125 2
            $this->array = $this->getArray();
5126 2
            $this->generator = null;
5127
5128 2
            return true;
5129
        }
5130
5131 982
        return false;
5132
    }
5133
5134
    /**
5135
     * Get correct PHP constant for direction.
5136
     *
5137
     * @param int|string $direction
5138
     *
5139
     * @return int
5140
     */
5141 39
    protected function getDirection($direction): int
5142
    {
5143 39
        if ((string) $direction === $direction) {
5144 10
            $direction = \strtolower($direction);
5145
5146 10
            if ($direction === 'desc') {
5147 2
                $direction = \SORT_DESC;
5148
            } else {
5149 8
                $direction = \SORT_ASC;
5150
            }
5151
        }
5152
5153
        if (
5154 39
            $direction !== \SORT_DESC
5155
            &&
5156 39
            $direction !== \SORT_ASC
5157
        ) {
5158
            $direction = \SORT_ASC;
5159
        }
5160
5161 39
        return $direction;
5162
    }
5163
5164
    /**
5165
     * @return TypeCheckPhpDoc[]
5166
     *
5167
     * @noinspection ReturnTypeCanBeDeclaredInspection
5168
     */
5169 16
    protected function getPropertiesFromPhpDoc()
5170
    {
5171 16
        static $PROPERTY_CACHE = [];
5172 16
        $cacheKey = 'Class::' . static::class;
5173
5174 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5175 15
            return $PROPERTY_CACHE[$cacheKey];
5176
        }
5177
5178
        // init
5179 2
        $properties = [];
5180
5181 2
        $reflector = new \ReflectionClass($this);
5182 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5183 2
        $docComment = $reflector->getDocComment();
5184 2
        if ($docComment) {
5185 2
            $docblock = $factory->create($docComment);
5186
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5187 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5188 2
                $properties[$tag->getVariableName()] = TypeCheckPhpDoc::fromPhpDocumentorProperty($tag);
5189
            }
5190
        }
5191
5192 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5193
    }
5194
5195
    /**
5196
     * @param mixed $glue
5197
     * @param mixed $pieces
5198
     * @param bool  $useKeys
5199
     *
5200
     * @return string
5201
     */
5202 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
5203
    {
5204 36
        if ($pieces instanceof self) {
5205 1
            $pieces = $pieces->getArray();
5206
        }
5207
5208 36
        if (\is_array($pieces) === true) {
5209 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
5210 36
            $pieces_count_not_zero = $pieces_count > 0;
5211
5212 36
            return \implode(
5213 36
                $glue,
5214 36
                \array_map(
5215 36
                    [$this, 'implode_recursive'],
5216 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
5217 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
5218
                )
5219
            );
5220
        }
5221
5222
        if (
5223 36
            \is_scalar($pieces) === true
5224
            ||
5225 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
5226
        ) {
5227 32
            return (string) $pieces;
5228
        }
5229
5230 8
        return '';
5231
    }
5232
5233
    /**
5234
     * @param mixed                 $needle   <p>
5235
     *                                        The searched value.
5236
     *                                        </p>
5237
     *                                        <p>
5238
     *                                        If needle is a string, the comparison is done
5239
     *                                        in a case-sensitive manner.
5240
     *                                        </p>
5241
     * @param array|\Generator|null $haystack <p>
5242
     *                                        The array.
5243
     *                                        </p>
5244
     * @param bool                  $strict   [optional] <p>
5245
     *                                        If the third parameter strict is set to true
5246
     *                                        then the in_array function will also check the
5247
     *                                        types of the
5248
     *                                        needle in the haystack.
5249
     *                                        </p>
5250
     *
5251
     * @return bool
5252
     *              <p>true if needle is found in the array, false otherwise</p>
5253
     */
5254 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
5255
    {
5256 18
        if ($haystack === null) {
5257
            $haystack = $this->getGenerator();
5258
        }
5259
5260 18
        foreach ($haystack as $item) {
5261 14
            if (\is_array($item) === true) {
5262 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
5263
            } else {
5264
                /** @noinspection NestedPositiveIfStatementsInspection */
5265 14
                if ($strict === true) {
5266 14
                    $returnTmp = $item === $needle;
5267
                } else {
5268
                    $returnTmp = $item == $needle;
5269
                }
5270
            }
5271
5272 14
            if ($returnTmp === true) {
5273 10
                return true;
5274
            }
5275
        }
5276
5277 8
        return false;
5278
    }
5279
5280
    /**
5281
     * @param mixed $data
5282
     *
5283
     * @return array|null
5284
     */
5285 1066
    protected function internalGetArray(&$data)
5286
    {
5287 1066
        if (\is_array($data) === true) {
5288 1063
            return $data;
5289
        }
5290
5291 52
        if (!$data) {
5292 6
            return [];
5293
        }
5294
5295 51
        if (\is_object($data) === true) {
5296 46
            if ($data instanceof \ArrayObject) {
5297 4
                return $data->getArrayCopy();
5298
            }
5299
5300 43
            if ($data instanceof \Generator) {
5301
                return static::createFromGeneratorImmutable($data)->getArray();
5302
            }
5303
5304 43
            if ($data instanceof \Traversable) {
5305
                return static::createFromObject($data)->getArray();
5306
            }
5307
5308 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...
5309
                return (array) $data->jsonSerialize();
5310
            }
5311
5312 43
            if (\method_exists($data, '__toArray')) {
5313
                return (array) $data->__toArray();
5314
            }
5315
5316 43
            if (\method_exists($data, '__toString')) {
5317
                return [(string) $data];
5318
            }
5319
        }
5320
5321 48
        if (\is_callable($data)) {
5322 41
            $this->generator = new ArrayyRewindableGenerator($data);
5323
5324 41
            return [];
5325
        }
5326
5327 9
        if (\is_scalar($data)) {
5328 7
            return [$data];
5329
        }
5330
5331 2
        return null;
5332
    }
5333
5334
    /**
5335
     * Internal mechanics of remove method.
5336
     *
5337
     * @param mixed $key
5338
     *
5339
     * @return bool
5340
     */
5341 18
    protected function internalRemove($key): bool
5342
    {
5343 18
        $this->generatorToArray();
5344
5345
        if (
5346 18
            $this->pathSeparator
5347
            &&
5348 18
            (string) $key === $key
5349
            &&
5350 18
            \strpos($key, $this->pathSeparator) !== false
5351
        ) {
5352
            $path = \explode($this->pathSeparator, (string) $key);
5353
5354
            if ($path !== false) {
5355
                // crawl though the keys
5356
                while (\count($path, \COUNT_NORMAL) > 1) {
5357
                    $key = \array_shift($path);
5358
5359
                    if (!$this->has($key)) {
5360
                        return false;
5361
                    }
5362
5363
                    $this->array = &$this->array[$key];
5364
                }
5365
5366
                $key = \array_shift($path);
5367
            }
5368
        }
5369
5370 18
        unset($this->array[$key]);
5371
5372 18
        return true;
5373
    }
5374
5375
    /**
5376
     * Internal mechanic of set method.
5377
     *
5378
     * @param int|string|null $key
5379
     * @param mixed           $value
5380
     * @param bool            $checkProperties
5381
     *
5382
     * @return bool
5383
     */
5384 938
    protected function internalSet(
5385
        $key,
5386
        &$value,
5387
        bool $checkProperties = true
5388
    ): bool {
5389
        if (
5390 938
            $checkProperties === true
5391
            &&
5392 938
            $this->properties !== []
5393
        ) {
5394 87
            $this->checkType($key, $value);
5395
        }
5396
5397 936
        if ($key === null) {
5398
            return false;
5399
        }
5400
5401 936
        $this->generatorToArray();
5402
5403 936
        $array = &$this->array;
5404
5405
        if (
5406 936
            $this->pathSeparator
5407
            &&
5408 936
            (string) $key === $key
5409
            &&
5410 936
            \strpos($key, $this->pathSeparator) !== false
5411
        ) {
5412 3
            $path = \explode($this->pathSeparator, (string) $key);
5413
5414 3
            if ($path !== false) {
5415
                // crawl through the keys
5416 3
                while (\count($path, \COUNT_NORMAL) > 1) {
5417 3
                    $key = \array_shift($path);
5418
5419 3
                    $array = &$array[$key];
5420
                }
5421
5422 3
                $key = \array_shift($path);
5423
            }
5424
        }
5425
5426 936
        $array[$key] = $value;
5427
5428 936
        return true;
5429
    }
5430
5431
    /**
5432
     * Convert a object into an array.
5433
     *
5434
     * @param object $object
5435
     *
5436
     * @return mixed
5437
     */
5438 5
    protected static function objectToArray($object)
5439
    {
5440 5
        if (!\is_object($object)) {
5441 4
            return $object;
5442
        }
5443
5444 5
        if (\is_object($object)) {
5445 5
            $object = \get_object_vars($object);
5446
        }
5447
5448 5
        return \array_map(['static', 'objectToArray'], $object);
5449
    }
5450
5451
    /**
5452
     * @param array $data
5453
     * @param bool  $checkPropertiesInConstructor
5454
     *
5455
     * @return void
5456
     */
5457 1064
    protected function setInitialValuesAndProperties(array &$data, bool $checkPropertiesInConstructor)
5458
    {
5459 1064
        $checkPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
5460
                                        &&
5461 1064
                                        $checkPropertiesInConstructor === true;
5462
5463 1064
        if ($this->properties !== []) {
5464 78
            foreach ($data as $key => &$valueInner) {
5465 78
                $this->internalSet(
5466 78
                    $key,
5467 78
                    $valueInner,
5468 78
                    $checkPropertiesInConstructor
5469
                );
5470
            }
5471
        } else {
5472
            if (
5473 997
                $this->checkPropertyTypes === true
5474
                ||
5475 997
                $checkPropertiesInConstructor === true
5476
            ) {
5477 16
                $this->properties = $this->getPropertiesFromPhpDoc();
5478
            }
5479
5480
            if (
5481 997
                $this->checkPropertiesMismatchInConstructor === true
5482
                &&
5483 997
                \count($data) !== 0
5484
                &&
5485 997
                \count(\array_diff_key($this->properties, $data)) > 0
5486
            ) {
5487 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...
5488
            }
5489
5490 996
            foreach ($data as $key => &$valueInner) {
5491 864
                $this->internalSet(
5492 864
                    $key,
5493 864
                    $valueInner,
5494 864
                    $checkPropertiesInConstructor
5495
                );
5496
            }
5497
        }
5498 1057
    }
5499
5500
    /**
5501
     * sorting keys
5502
     *
5503
     * @param array      $elements
5504
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5505
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5506
     *                              <strong>SORT_NATURAL</strong></p>
5507
     *
5508
     * @return static
5509
     *                <p>(Mutable) Return this Arrayy object.</p>
5510
     */
5511 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5512
    {
5513 18
        $direction = $this->getDirection($direction);
5514
5515 18
        switch ($direction) {
5516 18
            case 'desc':
5517
            case \SORT_DESC:
5518 6
                \krsort($elements, $strategy);
5519
5520 6
                break;
5521 13
            case 'asc':
5522 13
            case \SORT_ASC:
5523
            default:
5524 13
                \ksort($elements, $strategy);
5525
        }
5526
5527 18
        return $this;
5528
    }
5529
5530
    /**
5531
     * @param array      $elements  <p>Warning: used as reference</p>
5532
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5533
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5534
     *                              <strong>SORT_NATURAL</strong></p>
5535
     * @param bool       $keepKeys
5536
     *
5537
     * @return static
5538
     *                <p>(Mutable) Return this Arrayy object.</p>
5539
     */
5540 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5541
    {
5542 20
        $direction = $this->getDirection($direction);
5543
5544 20
        if (!$strategy) {
5545 20
            $strategy = \SORT_REGULAR;
5546
        }
5547
5548 20
        switch ($direction) {
5549 20
            case 'desc':
5550
            case \SORT_DESC:
5551 9
                if ($keepKeys) {
5552 5
                    \arsort($elements, $strategy);
5553
                } else {
5554 4
                    \rsort($elements, $strategy);
5555
                }
5556
5557 9
                break;
5558 11
            case 'asc':
5559 11
            case \SORT_ASC:
5560
            default:
5561 11
                if ($keepKeys) {
5562 4
                    \asort($elements, $strategy);
5563
                } else {
5564 7
                    \sort($elements, $strategy);
5565
                }
5566
        }
5567
5568 20
        return $this;
5569
    }
5570
5571
    /**
5572
     * @param int|string|null $key
5573
     * @param mixed           $value
5574
     *
5575
     * @return void
5576
     */
5577 87
    private function checkType($key, $value)
5578
    {
5579
        if (
5580 87
            $key !== null
5581
            &&
5582 87
            isset($this->properties[$key]) === false
5583
            &&
5584 87
            $this->checkPropertiesMismatch === true
5585
        ) {
5586
            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...
5587
        }
5588
5589 87
        if (isset($this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES])) {
5590 76
            $this->properties[self::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES]->checkType($value);
5591 17
        } elseif ($key !== null && isset($this->properties[$key])) {
5592 17
            $this->properties[$key]->checkType($value);
5593
        }
5594 85
    }
5595
}
5596