Completed
Push — master ( 633045...791dc4 )
by Lars
03:19
created

Arrayy::uniqueKeepIndex()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3.0026

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 28
ccs 14
cts 15
cp 0.9333
crap 3.0026
rs 9.472
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $array = [];
21
22
    /**
23
     * @var ArrayyRewindableGenerator|null
24
     */
25
    protected $generator;
26
27
    /**
28
     * @var string
29
     */
30
    protected $iteratorClass = ArrayyIterator::class;
31
32
    /**
33
     * @var string
34
     */
35
    protected $pathSeparator = '.';
36
37
    /**
38
     * @var bool
39
     */
40
    protected $checkPropertyTypes = false;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $checkForMissingPropertiesInConstructor = false;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $checkPropertiesMismatchInConstructor = false;
51
52
    /**
53
     * @var Property[]
54
     */
55
    protected $properties = [];
56
57
    /**
58
     * Initializes
59
     *
60
     * @param mixed  $data                                   <p>
61
     *                                                       Should be an array or a generator, otherwise it will try
62
     *                                                       to convert it into an array.
63
     *                                                       </p>
64
     * @param string $iteratorClass                          optional <p>
65
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
66
     *                                                       need this option.
67
     *                                                       </p>
68
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
69
     *                                                       You need to extend the "Arrayy"-class and you need to set
70
     *                                                       the $checkPropertiesMismatchInConstructor class property
71
     *                                                       to
72
     *                                                       true, otherwise this option didn't not work anyway.
73
     *                                                       </p>
74
     */
75 991
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 991
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 989
        parent::__construct([], 0, $iteratorClass);
84
85 989
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 989
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 989
            $this->checkPropertyTypes === true
91
            ||
92 989
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 15
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 989
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 989
            \count($data) !== 0
101
            &&
102 989
            \count(\array_diff_key($this->properties, $data)) > 0
103
        ) {
104 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
105
        }
106
107
        /** @noinspection AlterInForeachInspection */
108 988
        foreach ($data as $key => &$value) {
109 860
            $this->internalSet(
110 860
                $key,
111 860
                $value,
112 860
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 984
        $this->setIteratorClass($iteratorClass);
117 984
    }
118
119
    /**
120
     * Call object as function.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return mixed
125
     */
126 1
    public function __invoke($key = null)
127
    {
128 1
        if ($key !== null) {
129 1
            $this->generatorToArray();
130
131 1
            return $this->array[$key] ?? false;
132
        }
133
134
        return $this->getArray();
135
    }
136
137
    /**
138
     * Whether or not an element exists by key.
139
     *
140
     * @param mixed $key
141
     *
142
     * @return bool
143
     *              <p>True is the key/index exists, otherwise false.</p>
144
     */
145
    public function __isset($key): bool
146
    {
147
        return $this->offsetExists($key);
148
    }
149
150
    /**
151
     * Assigns a value to the specified element.
152
     *
153
     * @param mixed $key
154
     * @param mixed $value
155
     */
156 2
    public function __set($key, $value)
157
    {
158 2
        $this->internalSet($key, $value);
159 2
    }
160
161
    /**
162
     * magic to string
163
     *
164
     * @return string
165
     */
166 16
    public function __toString(): string
167
    {
168 16
        return $this->toString();
169
    }
170
171
    /**
172
     * Unset element by key.
173
     *
174
     * @param mixed $key
175
     */
176
    public function __unset($key)
177
    {
178
        $this->internalRemove($key);
179
    }
180
181
    /**
182
     * Get a value by key.
183
     *
184
     * @param mixed $key
185
     *
186
     * @return mixed
187
     *               <p>Get a Value from the current array.</p>
188
     */
189 4
    public function &__get($key)
190
    {
191 4
        $return = $this->get($key);
192
193 4
        if (\is_array($return) === true) {
194
            return static::create($return, $this->iteratorClass, false);
195
        }
196
197 4
        return $return;
198
    }
199
200
    /**
201
     * alias: for "Arrayy->append()"
202
     *
203
     * @param mixed $value
204
     *
205
     * @return static
206
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
207
     *
208
     * @see Arrayy::append()
209
     */
210 3
    public function add($value): self
211
    {
212 3
        return $this->append($value);
213
    }
214
215
    /**
216
     * Append a (key) + value to the current array.
217
     *
218
     * @param mixed $value
219
     * @param mixed $key
220
     *
221
     * @return static
222
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
223
     */
224 12
    public function append($value, $key = null): self
225
    {
226 12
        $this->generatorToArray();
227
228 12
        if ($key !== null) {
229
            if (
230
                isset($this->array[$key])
231
                &&
232
                \is_array($this->array[$key]) === true
233
            ) {
234
                $this->array[$key][] = $value;
235
            } else {
236
                $this->array[$key] = $value;
237
            }
238
        } else {
239 12
            $this->array[] = $value;
240
        }
241
242 12
        return $this;
243
    }
244
245
    /**
246
     * Sort the entries by value.
247
     *
248
     * @param int $sort_flags [optional] <p>
249
     *                        You may modify the behavior of the sort using the optional
250
     *                        parameter sort_flags, for details
251
     *                        see sort.
252
     *                        </p>
253
     *
254
     * @return static
255
     *                <p>(Mutable) Return this Arrayy object.</p>
256
     */
257 4
    public function asort(int $sort_flags = 0): self
258
    {
259 4
        $this->generatorToArray();
260
261 4
        \asort($this->array, $sort_flags);
262
263 4
        return $this;
264
    }
265
266
    /**
267
     * Counts all elements in an array, or something in an object.
268
     *
269
     * <p>
270
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
271
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
272
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
273
     * implemented and used in PHP.
274
     * </p>
275
     *
276
     * @see http://php.net/manual/en/function.count.php
277
     *
278
     * @param int $mode [optional] If the optional mode parameter is set to
279
     *                  COUNT_RECURSIVE (or 1), count
280
     *                  will recursively count the array. This is particularly useful for
281
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
282
     *
283
     * @return int
284
     *             <p>
285
     *             The number of elements in var, which is
286
     *             typically an array, since anything else will have one
287
     *             element.
288
     *             </p>
289
     *             <p>
290
     *             If var is not an array or an object with
291
     *             implemented Countable interface,
292
     *             1 will be returned.
293
     *             There is one exception, if var is &null;,
294
     *             0 will be returned.
295
     *             </p>
296
     *             <p>
297
     *             Caution: count may return 0 for a variable that isn't set,
298
     *             but it may also return 0 for a variable that has been initialized with an
299
     *             empty array. Use isset to test if a variable is set.
300
     *             </p>
301
     */
302 49
    public function count(int $mode = \COUNT_NORMAL): int
303
    {
304 49
        return \count($this->getArray(), $mode);
305
    }
306
307
    /**
308
     * Exchange the array for another one.
309
     *
310
     * @param array|static $data
311
     *
312
     * @return array
313
     */
314 1
    public function exchangeArray($data): array
315
    {
316 1
        $this->array = $this->fallbackForArray($data);
317
318 1
        return $this->array;
319
    }
320
321
    /**
322
     * Creates a copy of the ArrayyObject.
323
     *
324
     * @return array
325
     */
326 3
    public function getArrayCopy(): array
327
    {
328 3
        $this->generatorToArray();
329
330 3
        return $this->array;
331
    }
332
333
    /**
334
     * Returns a new iterator, thus implementing the \Iterator interface.
335
     *
336
     * @return \Iterator
337
     *                   <p>An iterator for the values in the array.</p>
338
     */
339 24
    public function getIterator(): \Iterator
340
    {
341 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
342 1
            return $this->generator;
343
        }
344
345 23
        $iterator = $this->getIteratorClass();
346
347 23
        if ($iterator === ArrayyIterator::class) {
348 23
            return new $iterator($this->getArray(), 0, static::class);
349
        }
350
351
        return new $iterator($this->getArray());
352
    }
353
354
    /**
355
     * Gets the iterator classname for the ArrayObject.
356
     *
357
     * @return string
358
     */
359 23
    public function getIteratorClass(): string
360
    {
361 23
        return $this->iteratorClass;
362
    }
363
364
    /**
365
     * Sort the entries by key
366
     *
367
     * @param int $sort_flags [optional] <p>
368
     *                        You may modify the behavior of the sort using the optional
369
     *                        parameter sort_flags, for details
370
     *                        see sort.
371
     *                        </p>
372
     *
373
     * @return static
374
     *                <p>(Mutable) Return this Arrayy object.</p>
375
     */
376 4
    public function ksort(int $sort_flags = 0): self
377
    {
378 4
        $this->generatorToArray();
379
380 4
        \ksort($this->array, $sort_flags);
381
382 4
        return $this;
383
    }
384
385
    /**
386
     * Sort an array using a case insensitive "natural order" algorithm
387
     *
388
     * @return static
389
     *                <p>(Mutable) Return this Arrayy object.</p>
390
     */
391
    public function natcasesort(): self
392
    {
393
        $this->generatorToArray();
394
395
        \natcasesort($this->array);
396
397
        return $this;
398
    }
399
400
    /**
401
     * Sort entries using a "natural order" algorithm
402
     *
403
     * @return static
404
     *                <p>(Mutable) Return this Arrayy object.</p>
405
     */
406 1
    public function natsort(): self
407
    {
408 1
        $this->generatorToArray();
409
410 1
        \natsort($this->array);
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Whether or not an offset exists.
417
     *
418
     * @param bool|int|string $offset
419
     *
420
     * @return bool
421
     */
422 61
    public function offsetExists($offset): bool
423
    {
424 61
        $this->generatorToArray();
425
426 61
        if ($this->array === []) {
427 5
            return false;
428
        }
429
430
        // php cast "bool"-index into "int"-index
431 56
        if ((bool) $offset === $offset) {
432 1
            $offset = (int) $offset;
433
        }
434
435 56
        $tmpReturn = $this->keyExists($offset);
436
437
        if (
438 56
            $tmpReturn === true
439
            ||
440
            (
441 21
                $tmpReturn === false
442
                &&
443 56
                \strpos((string) $offset, $this->pathSeparator) === false
444
            )
445
        ) {
446 54
            return $tmpReturn;
447
        }
448
449 3
        $offsetExists = false;
450
451
        if (
452 3
            $this->pathSeparator
453
            &&
454 3
            (string) $offset === $offset
455
            &&
456 3
            \strpos($offset, $this->pathSeparator) !== false
457
        ) {
458 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
459 3
            if ($explodedPath !== false) {
460 3
                $lastOffset = \array_pop($explodedPath);
461 3
                if ($lastOffset !== null) {
462 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
463
464 3
                    $this->callAtPath(
465 3
                        $containerPath,
466 3
                        static function ($container) use ($lastOffset, &$offsetExists) {
467 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
468 3
                        }
469
                    );
470
                }
471
            }
472
        }
473
474 3
        return $offsetExists;
475
    }
476
477
    /**
478
     * Returns the value at specified offset.
479
     *
480
     * @param int|string $offset
481
     *
482
     * @return mixed
483
     *               <p>Will return null if the offset did not exists.</p>
484
     */
485 30
    public function offsetGet($offset)
486
    {
487 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
488
    }
489
490
    /**
491
     * Assigns a value to the specified offset.
492
     *
493
     * @param int|string|null $offset
494
     * @param mixed           $value
495
     */
496 20
    public function offsetSet($offset, $value)
497
    {
498 20
        $this->generatorToArray();
499
500 20
        if ($offset === null) {
501 4
            $this->array[] = $value;
502
        } else {
503 16
            $this->internalSet($offset, $value);
504
        }
505 20
    }
506
507
    /**
508
     * Unset an offset.
509
     *
510
     * @param int|string $offset
511
     */
512 12
    public function offsetUnset($offset)
513
    {
514 12
        $this->generatorToArray();
515
516 12
        if ($this->array === []) {
517 3
            return;
518
        }
519
520 10
        if ($this->keyExists($offset)) {
521 7
            unset($this->array[$offset]);
522
523 7
            return;
524
        }
525
526
        if (
527 5
            $this->pathSeparator
528
            &&
529 5
            (string) $offset === $offset
530
            &&
531 5
            \strpos($offset, $this->pathSeparator) !== false
532
        ) {
533 2
            $path = \explode($this->pathSeparator, (string) $offset);
534
535 2
            if ($path !== false) {
536 2
                $pathToUnset = \array_pop($path);
537
538 2
                $this->callAtPath(
539 2
                    \implode($this->pathSeparator, $path),
540 2
                    static function (&$offset) use ($pathToUnset) {
541 2
                        unset($offset[$pathToUnset]);
542 2
                    }
543
                );
544
            }
545
        }
546
547 5
        unset($this->array[$offset]);
548 5
    }
549
550
    /**
551
     * Serialize the current "Arrayy"-object.
552
     *
553
     * @return string
554
     */
555 2
    public function serialize(): string
556
    {
557 2
        $this->generatorToArray();
558
559 2
        return parent::serialize();
560
    }
561
562
    /**
563
     * Sets the iterator classname for the current "Arrayy"-object.
564
     *
565
     * @param string $class
566
     *
567
     * @throws \InvalidArgumentException
568
     */
569 984
    public function setIteratorClass($class)
570
    {
571 984
        if (\class_exists($class)) {
572 984
            $this->iteratorClass = $class;
573
574 984
            return;
575
        }
576
577
        if (\strpos($class, '\\') === 0) {
578
            $class = '\\' . $class;
579
            if (\class_exists($class)) {
580
                $this->iteratorClass = $class;
581
582
                return;
583
            }
584
        }
585
586
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
587
    }
588
589
    /**
590
     * Sort the entries with a user-defined comparison function and maintain key association.
591
     *
592
     * @param callable $function
593
     *
594
     * @throws \InvalidArgumentException
595
     *
596
     * @return static
597
     *                <p>(Mutable) Return this Arrayy object.</p>
598
     */
599 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...
600
    {
601
        if (!\is_callable($function)) {
602
            throw new \InvalidArgumentException(
603
                'Passed function must be callable'
604
            );
605
        }
606
607
        $this->generatorToArray();
608
609
        \uasort($this->array, $function);
610
611
        return $this;
612
    }
613
614
    /**
615
     * Sort the entries by keys using a user-defined comparison function.
616
     *
617
     * @param callable $function
618
     *
619
     * @throws \InvalidArgumentException
620
     *
621
     * @return static
622
     *                <p>(Mutable) Return this Arrayy object.</p>
623
     */
624 5
    public function uksort($function): self
625
    {
626 5
        return $this->customSortKeys($function);
627
    }
628
629
    /**
630
     * Unserialize an string and return the instance of the "Arrayy"-class.
631
     *
632
     * @param string $string
633
     *
634
     * @return static
635
     */
636 2
    public function unserialize($string): self
637
    {
638 2
        parent::unserialize($string);
639
640 2
        return $this;
641
    }
642
643
    /**
644
     * Append a (key) + values to the current array.
645
     *
646
     * @param array $values
647
     * @param mixed $key
648
     *
649
     * @return static
650
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
651
     */
652 1
    public function appendArrayValues(array $values, $key = null): self
653
    {
654 1
        $this->generatorToArray();
655
656 1
        if ($key !== null) {
657
            if (
658 1
                isset($this->array[$key])
659
                &&
660 1
                \is_array($this->array[$key]) === true
661
            ) {
662 1
                foreach ($values as $value) {
663 1
                    $this->array[$key][] = $value;
664
                }
665
            } else {
666
                foreach ($values as $value) {
667 1
                    $this->array[$key] = $value;
668
                }
669
            }
670
        } else {
671
            foreach ($values as $value) {
672
                $this->array[] = $value;
673
            }
674
        }
675
676 1
        return $this;
677
    }
678
679
    /**
680
     * Add a suffix to each key.
681
     *
682
     * @param mixed $prefix
683
     *
684
     * @return static
685
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
686
     */
687 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...
688
    {
689
        // init
690 10
        $result = [];
691
692 10
        foreach ($this->getGenerator() as $key => $item) {
693 9
            if ($item instanceof self) {
694
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
695 9
            } elseif (\is_array($item) === true) {
696
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
697
                    ->appendToEachKey($prefix)
698
                    ->toArray();
699
            } else {
700 9
                $result[$prefix . $key] = $item;
701
            }
702
        }
703
704 10
        return self::create($result, $this->iteratorClass, false);
705
    }
706
707
    /**
708
     * Add a prefix to each value.
709
     *
710
     * @param mixed $prefix
711
     *
712
     * @return static
713
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
714
     */
715 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...
716
    {
717
        // init
718 10
        $result = [];
719
720 10
        foreach ($this->getGenerator() as $key => $item) {
721 9
            if ($item instanceof self) {
722
                $result[$key] = $item->appendToEachValue($prefix);
723 9
            } elseif (\is_array($item) === true) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item) === true) {
726 1
                $result[$key] = $item;
727
            } else {
728 9
                $result[$key] = $prefix . $item;
729
            }
730
        }
731
732 10
        return self::create($result, $this->iteratorClass, false);
733
    }
734
735
    /**
736
     * Sort an array in reverse order and maintain index association.
737
     *
738
     * @return static
739
     *                <p>(Mutable) Return this Arrayy object.</p>
740
     */
741 10
    public function arsort(): self
742
    {
743 10
        $this->generatorToArray();
744
745 10
        \arsort($this->array);
746
747 10
        return $this;
748
    }
749
750
    /**
751
     * Iterate over the current array and execute a callback for each loop.
752
     *
753
     * @param \Closure $closure
754
     *
755
     * @return static
756
     *                <p>(Immutable)</p>
757
     */
758 2
    public function at(\Closure $closure): self
759
    {
760 2
        $arrayy = clone $this;
761
762 2
        foreach ($arrayy->getGenerator() as $key => $value) {
763 2
            $closure($value, $key);
764
        }
765
766 2
        return static::create(
767 2
            $arrayy->toArray(),
768 2
            $this->iteratorClass,
769 2
            false
770
        );
771
    }
772
773
    /**
774
     * Returns the average value of the current array.
775
     *
776
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
777
     *
778
     * @return float|int
779
     *                   <p>The average value.</p>
780
     */
781 10
    public function average($decimals = 0)
782
    {
783 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
784
785 10
        if (!$count) {
786 2
            return 0;
787
        }
788
789 8
        if ((int) $decimals !== $decimals) {
790 3
            $decimals = 0;
791
        }
792
793 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
794
    }
795
796
    /**
797
     * Changes all keys in an array.
798
     *
799
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
800
     *                  or <strong>CASE_LOWER</strong> (default)</p>
801
     *
802
     * @return static
803
     *                <p>(Immutable)</p>
804
     */
805 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
806
    {
807
        if (
808 1
            $case !== \CASE_LOWER
809
            &&
810 1
            $case !== \CASE_UPPER
811
        ) {
812
            $case = \CASE_LOWER;
813
        }
814
815 1
        $return = [];
816 1
        foreach ($this->getGenerator() as $key => $value) {
817 1
            if ($case === \CASE_LOWER) {
818 1
                $key = \mb_strtolower((string) $key);
819
            } else {
820 1
                $key = \mb_strtoupper((string) $key);
821
            }
822
823 1
            $return[$key] = $value;
824
        }
825
826 1
        return static::create(
827 1
            $return,
828 1
            $this->iteratorClass,
829 1
            false
830
        );
831
    }
832
833
    /**
834
     * Change the path separator of the array wrapper.
835
     *
836
     * By default, the separator is: "."
837
     *
838
     * @param string $separator <p>Separator to set.</p>
839
     *
840
     * @return static
841
     *                <p>Mutable</p>
842
     */
843 10
    public function changeSeparator($separator): self
844
    {
845 10
        $this->pathSeparator = $separator;
846
847 10
        return $this;
848
    }
849
850
    /**
851
     * Create a chunked version of the current array.
852
     *
853
     * @param int  $size         <p>Size of each chunk.</p>
854
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
855
     *
856
     * @return static
857
     *                <p>(Immutable) A new array of chunks from the original array.</p>
858
     */
859 4
    public function chunk($size, $preserveKeys = false): self
860
    {
861 4
        return static::create(
862 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 4
            $this->iteratorClass,
864 4
            false
865
        );
866
    }
867
868
    /**
869
     * Clean all falsy values from the current array.
870
     *
871
     * @return static
872
     *                <p>(Immutable)</p>
873
     */
874 8
    public function clean(): self
875
    {
876 8
        return $this->filter(
877 8
            static function ($value) {
878 7
                return (bool) $value;
879 8
            }
880
        );
881
    }
882
883
    /**
884
     * WARNING!!! -> Clear the current array.
885
     *
886
     * @return static
887
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
888
     */
889 4
    public function clear(): self
890
    {
891 4
        $this->array = [];
892 4
        $this->generator = null;
893
894 4
        return $this;
895
    }
896
897
    /**
898
     * Check if an item is in the current array.
899
     *
900
     * @param float|int|string $value
901
     * @param bool             $recursive
902
     * @param bool             $strict
903
     *
904
     * @return bool
905
     */
906 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 22
        if ($recursive === true) {
909 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 13
        foreach ($this->getGenerator() as $valueFromArray) {
913 10
            if ($strict) {
914 10
                if ($value === $valueFromArray) {
915 10
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920 6
                    return true;
921
                }
922
            }
923
        }
924
925 6
        return false;
926
    }
927
928
    /**
929
     * Check if an (case-insensitive) string is in the current array.
930
     *
931
     * @param string $value
932
     * @param bool   $recursive
933
     *
934
     * @return bool
935
     */
936 26
    public function containsCaseInsensitive($value, $recursive = false): bool
937
    {
938 26
        if ($recursive === true) {
939 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
940 22
                if (\is_array($valueTmp) === true) {
941 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
942 5
                    if ($return === true) {
943 5
                        return $return;
944
                    }
945 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
946 22
                    return true;
947
                }
948
            }
949
950 10
            return false;
951
        }
952
953 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
954 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
955 11
                return true;
956
            }
957
        }
958
959 5
        return false;
960
    }
961
962
    /**
963
     * Check if the given key/index exists in the array.
964
     *
965
     * @param int|string $key <p>key/index to search for</p>
966
     *
967
     * @return bool
968
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
969
     */
970 4
    public function containsKey($key): bool
971
    {
972 4
        return $this->offsetExists($key);
973
    }
974
975
    /**
976
     * Check if all given needles are present in the array as key/index.
977
     *
978
     * @param array $needles   <p>The keys you are searching for.</p>
979
     * @param bool  $recursive
980
     *
981
     * @return bool
982
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
983
     */
984 2
    public function containsKeys(array $needles, $recursive = false): bool
985
    {
986 2
        if ($recursive === true) {
987
            return
988 2
                \count(
989 2
                    \array_intersect(
990 2
                        $needles,
991 2
                        $this->keys(true)->getArray()
992
                    ),
993 2
                    \COUNT_RECURSIVE
994
                )
995
                ===
996 2
                \count(
997 2
                    $needles,
998 2
                    \COUNT_RECURSIVE
999
                );
1000
        }
1001
1002 1
        return \count(
1003 1
            \array_intersect($needles, $this->keys()->getArray()),
1004 1
            \COUNT_NORMAL
1005
        )
1006
               ===
1007 1
               \count(
1008 1
                   $needles,
1009 1
                   \COUNT_NORMAL
1010
               );
1011
    }
1012
1013
    /**
1014
     * Check if all given needles are present in the array as key/index.
1015
     *
1016
     * @param array $needles <p>The keys you are searching for.</p>
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 1
    public function containsKeysRecursive(array $needles): bool
1022
    {
1023 1
        return $this->containsKeys($needles, true);
1024
    }
1025
1026
    /**
1027
     * alias: for "Arrayy->contains()"
1028
     *
1029
     * @param float|int|string $value
1030
     *
1031
     * @return bool
1032
     *
1033
     * @see Arrayy::contains()
1034
     */
1035 9
    public function containsValue($value): bool
1036
    {
1037 9
        return $this->contains($value);
1038
    }
1039
1040
    /**
1041
     * alias: for "Arrayy->contains($value, true)"
1042
     *
1043
     * @param float|int|string $value
1044
     *
1045
     * @return bool
1046
     *
1047
     * @see Arrayy::contains()
1048
     */
1049 18
    public function containsValueRecursive($value): bool
1050
    {
1051 18
        return $this->contains($value, true);
1052
    }
1053
1054
    /**
1055
     * Check if all given needles are present in the array.
1056
     *
1057
     * @param array $needles
1058
     *
1059
     * @return bool
1060
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1061
     */
1062 1
    public function containsValues(array $needles): bool
1063
    {
1064 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1065
               ===
1066 1
               \count($needles, \COUNT_NORMAL);
1067
    }
1068
1069
    /**
1070
     * Counts all the values of an array
1071
     *
1072
     * @see http://php.net/manual/en/function.array-count-values.php
1073
     *
1074
     * @return static
1075
     *                <p>
1076
     *                (Immutable)
1077
     *                An associative Arrayy-object of values from input as
1078
     *                keys and their count as value.
1079
     *                </p>
1080
     */
1081 7
    public function countValues(): self
1082
    {
1083 7
        return new static(\array_count_values($this->getArray()));
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $array
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkForMissingPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 580
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1097
    {
1098 580
        return new static(
1099 580
            $array,
1100 580
            $iteratorClass,
1101 580
            $checkForMissingPropertiesInConstructor
1102
        );
1103
    }
1104
1105
    /**
1106
     * WARNING: Creates an Arrayy object by reference.
1107
     *
1108
     * @param array $array
1109
     *
1110
     * @return static
1111
     *                <p>(Mutable) Return this Arrayy object.</p>
1112
     */
1113 1
    public function createByReference(array &$array = []): self
1114
    {
1115 1
        $array = $this->fallbackForArray($array);
1116
1117 1
        $this->array = &$array;
1118
1119 1
        return $this;
1120
    }
1121
1122
    /**
1123
     * Create an new instance from a callable function which will return an Generator.
1124
     *
1125
     * @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...
1126
     *
1127
     * @return static
1128
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1129
     */
1130 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1131
    {
1132 5
        return new static($generatorFunction);
1133
    }
1134
1135
    /**
1136
     * Create an new instance filled with a copy of values from a "Generator"-object.
1137
     *
1138
     * @param \Generator $generator
1139
     *
1140
     * @return static
1141
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1142
     */
1143 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1144
    {
1145 4
        return new static(\iterator_to_array($generator, true));
1146
    }
1147
1148
    /**
1149
     * Create an new Arrayy object via JSON.
1150
     *
1151
     * @param string $json
1152
     *
1153
     * @return static
1154
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1155
     */
1156 5
    public static function createFromJson(string $json): self
1157
    {
1158 5
        return static::create(\json_decode($json, true));
1159
    }
1160
1161
    /**
1162
     * Create an new instance filled with values from an object that is iterable.
1163
     *
1164
     * @param \Traversable $object <p>iterable object</p>
1165
     *
1166
     * @return static
1167
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1168
     */
1169 4
    public static function createFromObject(\Traversable $object): self
1170
    {
1171
        // init
1172 4
        $array = new static();
1173
1174 4
        if ($object instanceof self) {
1175 4
            $objectArray = $object->getGenerator();
1176
        } else {
1177
            $objectArray = $object;
1178
        }
1179
1180 4
        foreach ($objectArray as $key => $value) {
1181 3
            $array[$key] = $value;
1182
        }
1183
1184 4
        return $array;
1185
    }
1186
1187
    /**
1188
     * Create an new instance filled with values from an object.
1189
     *
1190
     * @param object $object
1191
     *
1192
     * @return static
1193
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1194
     */
1195 5
    public static function createFromObjectVars($object): self
1196
    {
1197 5
        return new static(self::objectToArray($object));
1198
    }
1199
1200
    /**
1201
     * Create an new Arrayy object via string.
1202
     *
1203
     * @param string      $str       <p>The input string.</p>
1204
     * @param string|null $delimiter <p>The boundary string.</p>
1205
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1206
     *                               used.</p>
1207
     *
1208
     * @return static
1209
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1210
     */
1211 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1212
    {
1213 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...
1214 1
            \preg_match_all($regEx, $str, $array);
1215
1216 1
            if (!empty($array)) {
1217 1
                $array = $array[0];
1218
            }
1219
        } else {
1220
            /** @noinspection NestedPositiveIfStatementsInspection */
1221 9
            if ($delimiter !== null) {
1222 7
                $array = \explode($delimiter, $str);
1223
            } else {
1224 2
                $array = [$str];
1225
            }
1226
        }
1227
1228
        // trim all string in the array
1229 10
        \array_walk(
1230 10
            $array,
1231 10
            static function (&$val) {
1232 10
                if ((string) $val === $val) {
1233 10
                    $val = \trim($val);
1234
                }
1235 10
            }
1236
        );
1237
1238 10
        return static::create($array);
1239
    }
1240
1241
    /**
1242
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1243
     *
1244
     * @param \Traversable $traversable
1245
     *
1246
     * @return static
1247
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1248
     */
1249 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1250
    {
1251 1
        return new static(\iterator_to_array($traversable, true));
1252
    }
1253
1254
    /**
1255
     * Create an new instance containing a range of elements.
1256
     *
1257
     * @param mixed $low  <p>First value of the sequence.</p>
1258
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1259
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1260
     *
1261
     * @return static
1262
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1263
     */
1264 2
    public static function createWithRange($low, $high, int $step = 1): self
1265
    {
1266 2
        return static::create(\range($low, $high, $step));
1267
    }
1268
1269
    /**
1270
     * Custom sort by index via "uksort".
1271
     *
1272
     * @see http://php.net/manual/en/function.uksort.php
1273
     *
1274
     * @param callable $function
1275
     *
1276
     * @throws \InvalidArgumentException
1277
     *
1278
     * @return static
1279
     *                <p>(Mutable) Return this Arrayy object.</p>
1280
     */
1281 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...
1282
    {
1283 5
        if (\is_callable($function) === false) {
1284
            throw new \InvalidArgumentException(
1285
                'Passed function must be callable'
1286
            );
1287
        }
1288
1289 5
        $this->generatorToArray();
1290
1291 5
        \uksort($this->array, $function);
1292
1293 5
        return $this;
1294
    }
1295
1296
    /**
1297
     * Custom sort by value via "usort".
1298
     *
1299
     * @see http://php.net/manual/en/function.usort.php
1300
     *
1301
     * @param callable $function
1302
     *
1303
     * @throws \InvalidArgumentException
1304
     *
1305
     * @return static
1306
     *                <p>(Mutable) Return this Arrayy object.</p>
1307
     */
1308 5 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...
1309
    {
1310 5
        if (\is_callable($function) === false) {
1311
            throw new \InvalidArgumentException(
1312
                'Passed function must be callable'
1313
            );
1314
        }
1315
1316 5
        $this->generatorToArray();
1317
1318 5
        \usort($this->array, $function);
1319
1320 5
        return $this;
1321
    }
1322
1323
    /**
1324
     * Delete the given key or keys.
1325
     *
1326
     * @param int|int[]|string|string[] $keyOrKeys
1327
     */
1328 4
    public function delete($keyOrKeys)
1329
    {
1330 4
        $keyOrKeys = (array) $keyOrKeys;
1331
1332 4
        foreach ($keyOrKeys as $key) {
1333 4
            $this->offsetUnset($key);
1334
        }
1335 4
    }
1336
1337
    /**
1338
     * Return values that are only in the current array.
1339
     *
1340
     * @param array $array
1341
     *
1342
     * @return static
1343
     *                <p>(Immutable)</p>
1344
     */
1345 12
    public function diff(array $array = []): self
1346
    {
1347 12
        return static::create(
1348 12
            \array_diff($this->getArray(), $array),
1349 12
            $this->iteratorClass,
1350 12
            false
1351
        );
1352
    }
1353
1354
    /**
1355
     * Return values that are only in the current array.
1356
     *
1357
     * @param array $array
1358
     *
1359
     * @return static
1360
     *                <p>(Immutable)</p>
1361
     */
1362 8
    public function diffKey(array $array = []): self
1363
    {
1364 8
        return static::create(
1365 8
            \array_diff_key($this->getArray(), $array),
1366 8
            $this->iteratorClass,
1367 8
            false
1368
        );
1369
    }
1370
1371
    /**
1372
     * Return values and Keys that are only in the current array.
1373
     *
1374
     * @param array $array
1375
     *
1376
     * @return static
1377
     *                <p>(Immutable)</p>
1378
     */
1379 8
    public function diffKeyAndValue(array $array = []): self
1380
    {
1381 8
        return static::create(
1382 8
            \array_diff_assoc($this->getArray(), $array),
1383 8
            $this->iteratorClass,
1384 8
            false
1385
        );
1386
    }
1387
1388
    /**
1389
     * Return values that are only in the current multi-dimensional array.
1390
     *
1391
     * @param array      $array
1392
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1393
     *
1394
     * @return static
1395
     *                <p>(Immutable)</p>
1396
     */
1397 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1398
    {
1399
        // init
1400 1
        $result = [];
1401
1402
        if (
1403 1
            $helperVariableForRecursion !== null
1404
            &&
1405 1
            \is_array($helperVariableForRecursion) === true
1406
        ) {
1407
            $arrayForTheLoop = $helperVariableForRecursion;
1408
        } else {
1409 1
            $arrayForTheLoop = $this->getGenerator();
1410
        }
1411
1412 1
        foreach ($arrayForTheLoop as $key => $value) {
1413 1
            if ($value instanceof self) {
1414
                $value = $value->getArray();
1415
            }
1416
1417 1
            if (\array_key_exists($key, $array)) {
1418 1
                if ($value !== $array[$key]) {
1419 1
                    $result[$key] = $value;
1420
                }
1421
            } else {
1422 1
                $result[$key] = $value;
1423
            }
1424
        }
1425
1426 1
        return static::create(
1427 1
            $result,
1428 1
            $this->iteratorClass,
1429 1
            false
1430
        );
1431
    }
1432
1433
    /**
1434
     * Return values that are only in the new $array.
1435
     *
1436
     * @param array $array
1437
     *
1438
     * @return static
1439
     *                <p>(Immutable)</p>
1440
     */
1441 8
    public function diffReverse(array $array = []): self
1442
    {
1443 8
        return static::create(
1444 8
            \array_diff($array, $this->getArray()),
1445 8
            $this->iteratorClass,
1446 8
            false
1447
        );
1448
    }
1449
1450
    /**
1451
     * Divide an array into two arrays. One with keys and the other with values.
1452
     *
1453
     * @return static
1454
     *                <p>(Immutable)</p>
1455
     */
1456 1
    public function divide(): self
1457
    {
1458 1
        return static::create(
1459
            [
1460 1
                $this->keys(),
1461 1
                $this->values(),
1462
            ],
1463 1
            $this->iteratorClass,
1464 1
            false
1465
        );
1466
    }
1467
1468
    /**
1469
     * Iterate over the current array and modify the array's value.
1470
     *
1471
     * @param \Closure $closure
1472
     *
1473
     * @return static
1474
     *                <p>(Immutable)</p>
1475
     */
1476 4 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...
1477
    {
1478
        // init
1479 4
        $array = [];
1480
1481 4
        foreach ($this->getGenerator() as $key => $value) {
1482 4
            $array[$key] = $closure($value, $key);
1483
        }
1484
1485 4
        return static::create(
1486 4
            $array,
1487 4
            $this->iteratorClass,
1488 4
            false
1489
        );
1490
    }
1491
1492
    /**
1493
     * Check if a value is in the current array using a closure.
1494
     *
1495
     * @param \Closure $closure
1496
     *
1497
     * @return bool
1498
     *              <p>Returns true if the given value is found, false otherwise.</p>
1499
     */
1500 4
    public function exists(\Closure $closure): bool
1501
    {
1502
        // init
1503 4
        $isExists = false;
1504
1505 4
        foreach ($this->getGenerator() as $key => $value) {
1506 3
            if ($closure($value, $key)) {
1507 1
                $isExists = true;
1508
1509 3
                break;
1510
            }
1511
        }
1512
1513 4
        return $isExists;
1514
    }
1515
1516
    /**
1517
     * Fill the array until "$num" with "$default" values.
1518
     *
1519
     * @param int   $num
1520
     * @param mixed $default
1521
     *
1522
     * @return static
1523
     *                <p>(Immutable)</p>
1524
     */
1525 8
    public function fillWithDefaults(int $num, $default = null): self
1526
    {
1527 8
        if ($num < 0) {
1528 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1529
        }
1530
1531 7
        $this->generatorToArray();
1532
1533 7
        $tmpArray = $this->array;
1534
1535 7
        $count = \count($tmpArray);
1536
1537 7
        while ($count < $num) {
1538 4
            $tmpArray[] = $default;
1539 4
            ++$count;
1540
        }
1541
1542 7
        return static::create(
1543 7
            $tmpArray,
1544 7
            $this->iteratorClass,
1545 7
            false
1546
        );
1547
    }
1548
1549
    /**
1550
     * Find all items in an array that pass the truth test.
1551
     *
1552
     * @param \Closure|null $closure [optional] <p>
1553
     *                               The callback function to use
1554
     *                               </p>
1555
     *                               <p>
1556
     *                               If no callback is supplied, all entries of
1557
     *                               input equal to false (see
1558
     *                               converting to
1559
     *                               boolean) will be removed.
1560
     *                               </p>
1561
     *                               * @param int $flag [optional] <p>
1562
     *                               Flag determining what arguments are sent to <i>callback</i>:
1563
     *                               </p><ul>
1564
     *                               <li>
1565
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1566
     *                               to <i>callback</i> instead of the value</span>
1567
     *                               </li>
1568
     *                               <li>
1569
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1570
     *                               arguments to <i>callback</i> instead of the value</span>
1571
     *                               </li>
1572
     *                               </ul>
1573
     *
1574
     * @return static
1575
     *                <p>(Immutable)</p>
1576
     */
1577 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1578
    {
1579 11
        if (!$closure) {
1580 1
            return $this->clean();
1581
        }
1582
1583 11
        return static::create(
1584 11
            \array_filter($this->getArray(), $closure, $flag),
1585 11
            $this->iteratorClass,
1586 11
            false
1587
        );
1588
    }
1589
1590
    /**
1591
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1592
     * property within that.
1593
     *
1594
     * @param string          $property
1595
     * @param string|string[] $value
1596
     * @param string          $comparisonOp
1597
     *                                      <p>
1598
     *                                      'eq' (equals),<br />
1599
     *                                      'gt' (greater),<br />
1600
     *                                      'gte' || 'ge' (greater or equals),<br />
1601
     *                                      'lt' (less),<br />
1602
     *                                      'lte' || 'le' (less or equals),<br />
1603
     *                                      'ne' (not equals),<br />
1604
     *                                      'contains',<br />
1605
     *                                      'notContains',<br />
1606
     *                                      'newer' (via strtotime),<br />
1607
     *                                      'older' (via strtotime),<br />
1608
     *                                      </p>
1609
     *
1610
     * @return static
1611
     *                <p>(Immutable)</p>
1612
     */
1613 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1614
    {
1615 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...
1616 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1617
        }
1618
1619
        $ops = [
1620 1
            'eq' => static function ($item, $prop, $value) {
1621 1
                return $item[$prop] === $value;
1622 1
            },
1623 1
            'gt' => static function ($item, $prop, $value) {
1624
                return $item[$prop] > $value;
1625 1
            },
1626 1
            'ge' => static function ($item, $prop, $value) {
1627
                return $item[$prop] >= $value;
1628 1
            },
1629 1
            'gte' => static function ($item, $prop, $value) {
1630
                return $item[$prop] >= $value;
1631 1
            },
1632 1
            'lt' => static function ($item, $prop, $value) {
1633 1
                return $item[$prop] < $value;
1634 1
            },
1635 1
            'le' => static function ($item, $prop, $value) {
1636
                return $item[$prop] <= $value;
1637 1
            },
1638 1
            'lte' => static function ($item, $prop, $value) {
1639
                return $item[$prop] <= $value;
1640 1
            },
1641 1
            'ne' => static function ($item, $prop, $value) {
1642
                return $item[$prop] !== $value;
1643 1
            },
1644 1
            'contains' => static function ($item, $prop, $value) {
1645 1
                return \in_array($item[$prop], (array) $value, true);
1646 1
            },
1647 1
            'notContains' => static function ($item, $prop, $value) {
1648
                return !\in_array($item[$prop], (array) $value, true);
1649 1
            },
1650 1
            'newer' => static function ($item, $prop, $value) {
1651
                return \strtotime($item[$prop]) > \strtotime($value);
1652 1
            },
1653 1
            'older' => static function ($item, $prop, $value) {
1654
                return \strtotime($item[$prop]) < \strtotime($value);
1655 1
            },
1656
        ];
1657
1658 1
        $result = \array_values(
1659 1
            \array_filter(
1660 1
                $this->getArray(),
1661 1
                static function ($item) use (
1662 1
                    $property,
1663 1
                    $value,
1664 1
                    $ops,
1665 1
                    $comparisonOp
1666
                ) {
1667 1
                    $item = (array) $item;
1668 1
                    $itemArrayy = new static($item);
1669 1
                    $item[$property] = $itemArrayy->get($property, []);
1670
1671 1
                    return $ops[$comparisonOp]($item, $property, $value);
1672 1
                }
1673
            )
1674
        );
1675
1676 1
        return static::create(
1677 1
            $result,
1678 1
            $this->iteratorClass,
1679 1
            false
1680
        );
1681
    }
1682
1683
    /**
1684
     * Find the first item in an array that passes the truth test,
1685
     *  otherwise return false
1686
     *
1687
     * @param \Closure $closure
1688
     *
1689
     * @return false|mixed
1690
     *                     <p>Return false if we did not find the value.</p>
1691
     */
1692 8
    public function find(\Closure $closure)
1693
    {
1694 8
        foreach ($this->getGenerator() as $key => $value) {
1695 6
            if ($closure($value, $key)) {
1696 6
                return $value;
1697
            }
1698
        }
1699
1700 3
        return false;
1701
    }
1702
1703
    /**
1704
     * find by ...
1705
     *
1706
     * @param string          $property
1707
     * @param string|string[] $value
1708
     * @param string          $comparisonOp
1709
     *
1710
     * @return static
1711
     *                <p>(Immutable)</p>
1712
     */
1713 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1714
    {
1715 1
        return $this->filterBy($property, $value, $comparisonOp);
1716
    }
1717
1718
    /**
1719
     * Get the first value from the current array.
1720
     *
1721
     * @return mixed
1722
     *               <p>Return null if there wasn't a element.</p>
1723
     */
1724 17
    public function first()
1725
    {
1726 17
        $key_first = $this->firstKey();
1727 17
        if ($key_first === null) {
1728 3
            return null;
1729
        }
1730
1731 14
        return $this->get($key_first);
1732
    }
1733
1734
    /**
1735
     * Get the first key from the current array.
1736
     *
1737
     * @return mixed
1738
     *               <p>Return null if there wasn't a element.</p>
1739
     */
1740 24
    public function firstKey()
1741
    {
1742 24
        $this->generatorToArray();
1743
1744 24
        return \array_key_first($this->array);
1745
    }
1746
1747
    /**
1748
     * Get the first value(s) from the current array.
1749
     * And will return an empty array if there was no first entry.
1750
     *
1751
     * @param int|null $number <p>How many values you will take?</p>
1752
     *
1753
     * @return static
1754
     *                <p>(Immutable)</p>
1755
     */
1756 36 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...
1757
    {
1758 36
        $arrayTmp = $this->getArray();
1759
1760 36
        if ($number === null) {
1761 14
            $array = (array) \array_shift($arrayTmp);
1762
        } else {
1763 22
            $number = (int) $number;
1764 22
            $array = \array_splice($arrayTmp, 0, $number);
1765
        }
1766
1767 36
        return static::create(
1768 36
            $array,
1769 36
            $this->iteratorClass,
1770 36
            false
1771
        );
1772
    }
1773
1774
    /**
1775
     * Get the first value(s) from the current array.
1776
     * And will return an empty array if there was no first entry.
1777
     *
1778
     * @param int|null $number <p>How many values you will take?</p>
1779
     *
1780
     * @return static
1781
     *                <p>(Immutable)</p>
1782
     */
1783 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...
1784
    {
1785 3
        $arrayTmp = $this->keys()->getArray();
1786
1787 3
        if ($number === null) {
1788
            $array = (array) \array_shift($arrayTmp);
1789
        } else {
1790 3
            $number = (int) $number;
1791 3
            $array = \array_splice($arrayTmp, 0, $number);
1792
        }
1793
1794 3
        return static::create(
1795 3
            $array,
1796 3
            $this->iteratorClass,
1797 3
            false
1798
        );
1799
    }
1800
1801
    /**
1802
     * Get and rmove the first value(s) from the current array.
1803
     * And will return an empty array if there was no first entry.
1804
     *
1805
     * @param int|null $number <p>How many values you will take?</p>
1806
     *
1807
     * @return static
1808
     *                <p>(Mutable)</p>
1809
     */
1810 34
    public function firstsMutable(int $number = null): self
1811
    {
1812 34
        $this->generatorToArray();
1813
1814 34
        if ($number === null) {
1815 19
            $this->array = (array) \array_shift($this->array);
1816
        } else {
1817 15
            $number = (int) $number;
1818 15
            $this->array = \array_splice($this->array, 0, $number);
1819
        }
1820
1821 34
        return $this;
1822
    }
1823
1824
    /**
1825
     * Exchanges all keys with their associated values in an array.
1826
     *
1827
     * @return static
1828
     *                <p>(Immutable)</p>
1829
     */
1830 1
    public function flip(): self
1831
    {
1832 1
        return static::create(
1833 1
            \array_flip($this->getArray()),
1834 1
            $this->iteratorClass,
1835 1
            false
1836
        );
1837
    }
1838
1839
    /**
1840
     * Get a value from an array (optional using dot-notation).
1841
     *
1842
     * @param mixed $key      <p>The key to look for.</p>
1843
     * @param mixed $fallback <p>Value to fallback to.</p>
1844
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1845
     *                        class.</p>
1846
     *
1847
     * @return mixed|static
1848
     */
1849 104
    public function get($key, $fallback = null, array $array = null)
1850
    {
1851 104
        if ($array !== null) {
1852 4
            $usedArray = $array;
1853
        } else {
1854 101
            $this->generatorToArray();
1855
1856 101
            $usedArray = $this->array;
1857
        }
1858
1859 104
        if ($key === null) {
1860 1
            return static::create(
1861 1
                $usedArray,
1862 1
                $this->iteratorClass,
1863 1
                false
1864
            );
1865
        }
1866
1867
        // php cast "bool"-index into "int"-index
1868 104
        if ((bool) $key === $key) {
1869 3
            $key = (int) $key;
1870
        }
1871
1872 104
        if (\array_key_exists($key, $usedArray) === true) {
1873 93
            if (\is_array($usedArray[$key]) === true) {
1874 11
                return static::create(
1875 11
                    $usedArray[$key],
1876 11
                    $this->iteratorClass,
1877 11
                    false
1878
                );
1879
            }
1880
1881 84
            return $usedArray[$key];
1882
        }
1883
1884
        // crawl through array, get key according to object or not
1885 24
        $usePath = false;
1886
        if (
1887 24
            $this->pathSeparator
1888
            &&
1889 24
            (string) $key === $key
1890
            &&
1891 24
            \strpos($key, $this->pathSeparator) !== false
1892
        ) {
1893 7
            $segments = \explode($this->pathSeparator, (string) $key);
1894 7
            if ($segments !== false) {
1895 7
                $usePath = true;
1896
1897 7
                foreach ($segments as $segment) {
1898
                    if (
1899
                        (
1900 7
                            \is_array($usedArray) === true
1901
                            ||
1902 7
                            $usedArray instanceof \ArrayAccess
1903
                        )
1904
                        &&
1905 7
                        isset($usedArray[$segment])
1906
                    ) {
1907 7
                        $usedArray = $usedArray[$segment];
1908
1909 7
                        continue;
1910
                    }
1911
1912
                    if (
1913 6
                        \is_object($usedArray) === true
1914
                        &&
1915 6
                        \property_exists($usedArray, $segment)
1916
                    ) {
1917 1
                        $usedArray = $usedArray->{$segment};
1918
1919 1
                        continue;
1920
                    }
1921
1922 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1923
                }
1924
            }
1925
        }
1926
1927 24
        if (!$usePath && !isset($usedArray[$key])) {
1928 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1929
        }
1930
1931 7
        if (\is_array($usedArray) === true) {
1932 1
            return static::create(
1933 1
                $usedArray,
1934 1
                $this->iteratorClass,
1935 1
                false
1936
            );
1937
        }
1938
1939 7
        return $usedArray;
1940
    }
1941
1942
    /**
1943
     * alias: for "Arrayy->getArray()"
1944
     *
1945
     * @return array
1946
     *
1947
     * @see Arrayy::getArray()
1948
     */
1949 1
    public function getAll(): array
1950
    {
1951 1
        return $this->getArray();
1952
    }
1953
1954
    /**
1955
     * Get the current array from the "Arrayy"-object.
1956
     *
1957
     * @return array
1958
     */
1959 799
    public function getArray(): array
1960
    {
1961
        // init
1962 799
        $array = [];
1963
1964 799
        foreach ($this->getGenerator() as $key => $value) {
1965 690
            $array[$key] = $value;
1966
        }
1967
1968 799
        return $array;
1969
    }
1970
1971
    /**
1972
     * Returns the values from a single column of the input array, identified by
1973
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1974
     *
1975
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1976
     * array by the values from the $indexKey column in the input array.
1977
     *
1978
     * @param mixed $columnKey
1979
     * @param mixed $indexKey
1980
     *
1981
     * @return static
1982
     *                <p>(Immutable)</p>
1983
     */
1984 1
    public function getColumn($columnKey = null, $indexKey = null): self
1985
    {
1986 1
        return static::create(
1987 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1988 1
            $this->iteratorClass,
1989 1
            false
1990
        );
1991
    }
1992
1993
    /**
1994
     * Get the current array from the "Arrayy"-object as generator.
1995
     *
1996
     * @return \Generator
1997
     */
1998 875
    public function getGenerator(): \Generator
1999
    {
2000 875
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2001 39
            yield from $this->generator;
2002
        }
2003
2004 875
        yield from $this->array;
2005 832
    }
2006
2007
    /**
2008
     * alias: for "Arrayy->keys()"
2009
     *
2010
     * @return static
2011
     *                <p>(Immutable)</p>
2012
     *
2013
     * @see Arrayy::keys()
2014
     */
2015 2
    public function getKeys(): self
2016
    {
2017 2
        return $this->keys();
2018
    }
2019
2020
    /**
2021
     * Get the current array from the "Arrayy"-object as object.
2022
     *
2023
     * @return \stdClass
2024
     */
2025 4
    public function getObject(): \stdClass
2026
    {
2027 4
        return self::arrayToObject($this->getArray());
2028
    }
2029
2030
    /**
2031
     * alias: for "Arrayy->randomImmutable()"
2032
     *
2033
     * @return static
2034
     *                <p>(Immutable)</p>
2035
     *
2036
     * @see Arrayy::randomImmutable()
2037
     */
2038 4
    public function getRandom(): self
2039
    {
2040 4
        return $this->randomImmutable();
2041
    }
2042
2043
    /**
2044
     * alias: for "Arrayy->randomKey()"
2045
     *
2046
     * @return mixed
2047
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2048
     *
2049
     * @see Arrayy::randomKey()
2050
     */
2051 3
    public function getRandomKey()
2052
    {
2053 3
        return $this->randomKey();
2054
    }
2055
2056
    /**
2057
     * alias: for "Arrayy->randomKeys()"
2058
     *
2059
     * @param int $number
2060
     *
2061
     * @return static
2062
     *                <p>(Immutable)</p>
2063
     *
2064
     * @see Arrayy::randomKeys()
2065
     */
2066 8
    public function getRandomKeys(int $number): self
2067
    {
2068 8
        return $this->randomKeys($number);
2069
    }
2070
2071
    /**
2072
     * alias: for "Arrayy->randomValue()"
2073
     *
2074
     * @return mixed
2075
     *               <p>Get a random value or null if there wasn't a value.</p>
2076
     *
2077
     * @see Arrayy::randomValue()
2078
     */
2079 3
    public function getRandomValue()
2080
    {
2081 3
        return $this->randomValue();
2082
    }
2083
2084
    /**
2085
     * alias: for "Arrayy->randomValues()"
2086
     *
2087
     * @param int $number
2088
     *
2089
     * @return static
2090
     *                <p>(Immutable)</p>
2091
     *
2092
     * @see Arrayy::randomValues()
2093
     */
2094 6
    public function getRandomValues(int $number): self
2095
    {
2096 6
        return $this->randomValues($number);
2097
    }
2098
2099
    /**
2100
     * Group values from a array according to the results of a closure.
2101
     *
2102
     * @param callable|string $grouper  <p>A callable function name.</p>
2103
     * @param bool            $saveKeys
2104
     *
2105
     * @return static
2106
     *                <p>(Immutable)</p>
2107
     */
2108 4
    public function group($grouper, bool $saveKeys = false): self
2109
    {
2110
        // init
2111 4
        $result = [];
2112
2113
        // Iterate over values, group by property/results from closure.
2114 4
        foreach ($this->getGenerator() as $key => $value) {
2115 4
            if (\is_callable($grouper) === true) {
2116 3
                $groupKey = $grouper($value, $key);
2117
            } else {
2118 1
                $groupKey = $this->get($grouper);
2119
            }
2120
2121 4
            $newValue = $this->get($groupKey, null, $result);
2122
2123 4
            if ($groupKey instanceof self) {
2124
                $groupKey = $groupKey->getArray();
2125
            }
2126
2127 4
            if ($newValue instanceof self) {
2128 4
                $newValue = $newValue->getArray();
2129
            }
2130
2131
            // Add to results.
2132 4
            if ($groupKey !== null) {
2133 3
                if ($saveKeys) {
2134 2
                    $result[$groupKey] = $newValue;
2135 2
                    $result[$groupKey][$key] = $value;
2136
                } else {
2137 1
                    $result[$groupKey] = $newValue;
2138 4
                    $result[$groupKey][] = $value;
2139
                }
2140
            }
2141
        }
2142
2143 4
        return static::create(
2144 4
            $result,
2145 4
            $this->iteratorClass,
2146 4
            false
2147
        );
2148
    }
2149
2150
    /**
2151
     * Check if an array has a given key.
2152
     *
2153
     * @param mixed $key
2154
     *
2155
     * @return bool
2156
     */
2157 23
    public function has($key): bool
2158
    {
2159 23
        static $UN_FOUND = null;
2160
2161 23
        if ($UN_FOUND === null) {
2162
            // Generate unique string to use as marker.
2163 1
            $UN_FOUND = \uniqid('arrayy', true);
2164
        }
2165
2166 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2167
    }
2168
2169
    /**
2170
     * Implodes the values of this array.
2171
     *
2172
     * @param string $glue
2173
     *
2174
     * @return string
2175
     */
2176 28
    public function implode(string $glue = ''): string
2177
    {
2178 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2179
    }
2180
2181
    /**
2182
     * Implodes the keys of this array.
2183
     *
2184
     * @param string $glue
2185
     *
2186
     * @return string
2187
     */
2188 8
    public function implodeKeys(string $glue = ''): string
2189
    {
2190 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2191
    }
2192
2193
    /**
2194
     * Given a list and an iterate-function that returns
2195
     * a key for each element in the list (or a property name),
2196
     * returns an object with an index of each item.
2197
     *
2198
     * @param mixed $key
2199
     *
2200
     * @return static
2201
     *                <p>(Immutable)</p>
2202
     */
2203 4
    public function indexBy($key): self
2204
    {
2205
        // init
2206 4
        $results = [];
2207
2208 4
        foreach ($this->getGenerator() as $a) {
2209 4
            if (\array_key_exists($key, $a) === true) {
2210 4
                $results[$a[$key]] = $a;
2211
            }
2212
        }
2213
2214 4
        return static::create(
2215 4
            $results,
2216 4
            $this->iteratorClass,
2217 4
            false
2218
        );
2219
    }
2220
2221
    /**
2222
     * alias: for "Arrayy->searchIndex()"
2223
     *
2224
     * @param mixed $value <p>The value to search for.</p>
2225
     *
2226
     * @return mixed
2227
     *
2228
     * @see Arrayy::searchIndex()
2229
     */
2230 4
    public function indexOf($value)
2231
    {
2232 4
        return $this->searchIndex($value);
2233
    }
2234
2235
    /**
2236
     * Get everything but the last..$to items.
2237
     *
2238
     * @param int $to
2239
     *
2240
     * @return static
2241
     *                <p>(Immutable)</p>
2242
     */
2243 12
    public function initial(int $to = 1): self
2244
    {
2245 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2246
    }
2247
2248
    /**
2249
     * Return an array with all elements found in input array.
2250
     *
2251
     * @param array $search
2252
     * @param bool  $keepKeys
2253
     *
2254
     * @return static
2255
     *                <p>(Immutable)</p>
2256
     */
2257 3
    public function intersection(array $search, bool $keepKeys = false): self
2258
    {
2259 3
        if ($keepKeys) {
2260 1
            return static::create(
2261 1
                \array_uintersect(
2262 1
                    $this->array,
2263 1
                    $search,
2264 1
                    static function ($a, $b) {
2265 1
                        return $a === $b ? 0 : -1;
2266 1
                    }
2267
                ),
2268 1
                $this->iteratorClass,
2269 1
                false
2270
            );
2271
        }
2272
2273 2
        return static::create(
2274 2
            \array_values(\array_intersect($this->getArray(), $search)),
2275 2
            $this->iteratorClass,
2276 2
            false
2277
        );
2278
    }
2279
2280
    /**
2281
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2282
     *
2283
     * @param array $search
2284
     *
2285
     * @return bool
2286
     */
2287 1
    public function intersects(array $search): bool
2288
    {
2289 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2290
    }
2291
2292
    /**
2293
     * Invoke a function on all of an array's values.
2294
     *
2295
     * @param callable $callable
2296
     * @param mixed    $arguments
2297
     *
2298
     * @return static
2299
     *                <p>(Immutable)</p>
2300
     */
2301 1
    public function invoke($callable, $arguments = []): self
2302
    {
2303
        // If one argument given for each iteration, create an array for it.
2304 1
        if (\is_array($arguments) === false) {
2305 1
            $arguments = \array_fill(
2306 1
                0,
2307 1
                \count($this->getArray(), \COUNT_NORMAL),
2308 1
                $arguments
2309
            );
2310
        }
2311
2312
        // If the callable has arguments, pass them.
2313 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...
2314 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2315
        } else {
2316 1
            $array = $this->map($callable);
2317
        }
2318
2319 1
        return static::create(
2320 1
            $array,
2321 1
            $this->iteratorClass,
2322 1
            false
2323
        );
2324
    }
2325
2326
    /**
2327
     * Check whether array is associative or not.
2328
     *
2329
     * @param bool $recursive
2330
     *
2331
     * @return bool
2332
     *              <p>Returns true if associative, false otherwise.</p>
2333
     */
2334 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...
2335
    {
2336 15
        if ($this->isEmpty()) {
2337 3
            return false;
2338
        }
2339
2340 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2341 13
            if ((string) $key !== $key) {
2342 13
                return false;
2343
            }
2344
        }
2345
2346 3
        return true;
2347
    }
2348
2349
    /**
2350
     * Check if a given key or keys are empty.
2351
     *
2352
     * @param int|int[]|string|string[]|null $keys
2353
     *
2354
     * @return bool
2355
     *              <p>Returns true if empty, false otherwise.</p>
2356
     */
2357 38
    public function isEmpty($keys = null): bool
2358
    {
2359 38
        if ($this->generator) {
2360
            return $this->getArray() === [];
2361
        }
2362
2363 38
        if ($keys === null) {
2364 38
            return $this->array === [];
2365
        }
2366
2367
        foreach ((array) $keys as $key) {
2368
            if (!empty($this->get($key))) {
2369
                return false;
2370
            }
2371
        }
2372
2373
        return true;
2374
    }
2375
2376
    /**
2377
     * Check if the current array is equal to the given "$array" or not.
2378
     *
2379
     * @param array $array
2380
     *
2381
     * @return bool
2382
     */
2383 1
    public function isEqual(array $array): bool
2384
    {
2385 1
        return $this->getArray() === $array;
2386
    }
2387
2388
    /**
2389
     * Check if the current array is a multi-array.
2390
     *
2391
     * @return bool
2392
     */
2393 22
    public function isMultiArray(): bool
2394
    {
2395
        return !(
2396 22
            \count($this->getArray(), \COUNT_NORMAL)
2397
            ===
2398 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2399
        );
2400
    }
2401
2402
    /**
2403
     * Check whether array is numeric or not.
2404
     *
2405
     * @return bool
2406
     *              <p>Returns true if numeric, false otherwise.</p>
2407
     */
2408 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...
2409
    {
2410 5
        if ($this->isEmpty()) {
2411 2
            return false;
2412
        }
2413
2414 4
        foreach ($this->keys()->getGenerator() as $key) {
2415 4
            if ((int) $key !== $key) {
2416 4
                return false;
2417
            }
2418
        }
2419
2420 2
        return true;
2421
    }
2422
2423
    /**
2424
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2425
     *
2426
     * @param bool $recursive
2427
     *
2428
     * @return bool
2429
     */
2430 1
    public function isSequential(bool $recursive = false): bool
2431
    {
2432
2433
        // recursive
2434
2435 1
        if ($recursive === true) {
2436
            return $this->array_keys_recursive($this->getArray())
2437
                   ===
2438
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2439
        }
2440
2441
        // non recursive
2442
2443 1
        return \array_keys($this->getArray())
2444
               ===
2445 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2446
    }
2447
2448
    /**
2449
     * @return array
2450
     */
2451
    public function jsonSerialize(): array
2452
    {
2453
        return $this->getArray();
2454
    }
2455
2456
    /**
2457
     * Checks if the given key exists in the provided array.
2458
     *
2459
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2460
     *       then you need to use "Arrayy->offsetExists()".
2461
     *
2462
     * @param int|string $key the key to look for
2463
     *
2464
     * @return bool
2465
     */
2466 58
    public function keyExists($key): bool
2467
    {
2468 58
        return \array_key_exists($key, $this->array);
2469
    }
2470
2471
    /**
2472
     * Get all keys from the current array.
2473
     *
2474
     * @param bool       $recursive     [optional] <p>
2475
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2476
     *                                  </p>
2477
     * @param mixed|null $search_values [optional] <p>
2478
     *                                  If specified, then only keys containing these values are returned.
2479
     *                                  </p>
2480
     * @param bool       $strict        [optional] <p>
2481
     *                                  Determines if strict comparison (===) should be used during the search.
2482
     *                                  </p>
2483
     *
2484
     * @return static
2485
     *                <p>(Immutable) An array of all the keys in input.</p>
2486
     */
2487 29
    public function keys(
2488
        bool $recursive = false,
2489
        $search_values = null,
2490
        bool $strict = true
2491
    ): self {
2492
2493
        // recursive
2494
2495 29
        if ($recursive === true) {
2496 4
            $array = $this->array_keys_recursive(
2497 4
                null,
2498 4
                $search_values,
2499 4
                $strict
2500
            );
2501
2502 4
            return static::create(
2503 4
                $array,
2504 4
                $this->iteratorClass,
2505 4
                false
2506
            );
2507
        }
2508
2509
        // non recursive
2510
2511 28
        if ($search_values === null) {
2512 28
            $arrayFunction = function () {
2513 28
                foreach ($this->getGenerator() as $key => $value) {
2514 26
                    yield $key;
2515
                }
2516 28
            };
2517
        } else {
2518 1
            $arrayFunction = function () use ($search_values, $strict) {
2519 1
                $is_array_tmp = \is_array($search_values);
2520
2521 1
                foreach ($this->getGenerator() as $key => $value) {
2522 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...
2523
                        (
2524 1
                            $is_array_tmp === false
2525
                            &&
2526 1
                            $strict === true
2527
                            &&
2528 1
                            $search_values === $value
2529
                        )
2530
                        ||
2531
                        (
2532 1
                            $is_array_tmp === false
2533
                            &&
2534 1
                            $strict === false
2535
                            &&
2536 1
                            $search_values == $value
2537
                        )
2538
                        ||
2539
                        (
2540 1
                            $is_array_tmp === true
2541
                            &&
2542 1
                            \in_array($value, $search_values, $strict)
2543
                        )
2544
                    ) {
2545 1
                        yield $key;
2546
                    }
2547
                }
2548 1
            };
2549
        }
2550
2551 28
        return static::create(
2552 28
            $arrayFunction,
2553 28
            $this->iteratorClass,
2554 28
            false
2555
        );
2556
    }
2557
2558
    /**
2559
     * Sort an array by key in reverse order.
2560
     *
2561
     * @param int $sort_flags [optional] <p>
2562
     *                        You may modify the behavior of the sort using the optional
2563
     *                        parameter sort_flags, for details
2564
     *                        see sort.
2565
     *                        </p>
2566
     *
2567
     * @return static
2568
     *                <p>(Mutable) Return this Arrayy object.</p>
2569
     */
2570 4
    public function krsort(int $sort_flags = 0): self
2571
    {
2572 4
        $this->generatorToArray();
2573
2574 4
        \krsort($this->array, $sort_flags);
2575
2576 4
        return $this;
2577
    }
2578
2579
    /**
2580
     * Get the last value from the current array.
2581
     *
2582
     * @return mixed
2583
     *               <p>Return null if there wasn't a element.</p>
2584
     */
2585 16
    public function last()
2586
    {
2587 16
        $key_last = $this->lastKey();
2588 16
        if ($key_last === null) {
2589 2
            return null;
2590
        }
2591
2592 14
        return $this->get($key_last);
2593
    }
2594
2595
    /**
2596
     * Get the last key from the current array.
2597
     *
2598
     * @return mixed
2599
     *               <p>Return null if there wasn't a element.</p>
2600
     */
2601 20
    public function lastKey()
2602
    {
2603 20
        $this->generatorToArray();
2604
2605 20
        return \array_key_last($this->array);
2606
    }
2607
2608
    /**
2609
     * Get the last value(s) from the current array.
2610
     *
2611
     * @param int|null $number
2612
     *
2613
     * @return static
2614
     *                <p>(Immutable)</p>
2615
     */
2616 13
    public function lastsImmutable(int $number = null): self
2617
    {
2618 13
        if ($this->isEmpty()) {
2619 1
            return static::create(
2620 1
                [],
2621 1
                $this->iteratorClass,
2622 1
                false
2623
            );
2624
        }
2625
2626 12
        if ($number === null) {
2627 8
            $poppedValue = $this->last();
2628
2629 8
            if ($poppedValue === null) {
2630 1
                $poppedValue = [$poppedValue];
2631
            } else {
2632 7
                $poppedValue = (array) $poppedValue;
2633
            }
2634
2635 8
            $arrayy = static::create(
2636 8
                $poppedValue,
2637 8
                $this->iteratorClass,
2638 8
                false
2639
            );
2640
        } else {
2641 4
            $number = (int) $number;
2642 4
            $arrayy = $this->rest(-$number);
2643
        }
2644
2645 12
        return $arrayy;
2646
    }
2647
2648
    /**
2649
     * Get the last value(s) from the current array.
2650
     *
2651
     * @param int|null $number
2652
     *
2653
     * @return static
2654
     *                <p>(Mutable)</p>
2655
     */
2656 13
    public function lastsMutable(int $number = null): self
2657
    {
2658 13
        if ($this->isEmpty()) {
2659 1
            return $this;
2660
        }
2661
2662 12
        if ($number === null) {
2663 8
            $poppedValue = $this->last();
2664
2665 8
            if ($poppedValue === null) {
2666 1
                $poppedValue = [$poppedValue];
2667
            } else {
2668 7
                $poppedValue = (array) $poppedValue;
2669
            }
2670
2671 8
            $this->array = static::create(
2672 8
                $poppedValue,
2673 8
                $this->iteratorClass,
2674 8
                false
2675 8
            )->getArray();
2676
        } else {
2677 4
            $number = (int) $number;
2678 4
            $this->array = $this->rest(-$number)->getArray();
2679
        }
2680
2681 12
        $this->generator = null;
2682
2683 12
        return $this;
2684
    }
2685
2686
    /**
2687
     * Count the values from the current array.
2688
     *
2689
     * alias: for "Arrayy->count()"
2690
     *
2691
     * @param int $mode
2692
     *
2693
     * @return int
2694
     *
2695
     * @see Arrayy::count()
2696
     */
2697 20
    public function length(int $mode = \COUNT_NORMAL): int
2698
    {
2699 20
        return $this->count($mode);
2700
    }
2701
2702
    /**
2703
     * Apply the given function to the every element of the array,
2704
     * collecting the results.
2705
     *
2706
     * @param callable $callable
2707
     * @param bool     $useKeyAsSecondParameter
2708
     * @param mixed    ...$arguments
2709
     *
2710
     * @return static
2711
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2712
     */
2713 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2714
    {
2715 5
        $useArguments = \func_num_args() > 2;
2716
2717 5
        return static::create(
2718 5
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2719 5
                foreach ($this->getGenerator() as $key => $value) {
2720 4
                    if ($useArguments) {
2721 3
                        if ($useKeyAsSecondParameter) {
2722
                            yield $key => $callable($value, $key, ...$arguments);
2723
                        } else {
2724 3
                            yield $key => $callable($value, ...$arguments);
2725
                        }
2726
                    } else {
2727
                        /** @noinspection NestedPositiveIfStatementsInspection */
2728 4
                        if ($useKeyAsSecondParameter) {
2729
                            yield $key => $callable($value, $key);
2730
                        } else {
2731 4
                            yield $key => $callable($value);
2732
                        }
2733
                    }
2734
                }
2735 5
            },
2736 5
            $this->iteratorClass,
2737 5
            false
2738
        );
2739
    }
2740
2741
    /**
2742
     * Check if all items in current array match a truth test.
2743
     *
2744
     * @param \Closure $closure
2745
     *
2746
     * @return bool
2747
     */
2748 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...
2749
    {
2750 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2751 2
            return false;
2752
        }
2753
2754 13
        foreach ($this->getGenerator() as $key => $value) {
2755 13
            $value = $closure($value, $key);
2756
2757 13
            if ($value === false) {
2758 13
                return false;
2759
            }
2760
        }
2761
2762 7
        return true;
2763
    }
2764
2765
    /**
2766
     * Check if any item in the current array matches a truth test.
2767
     *
2768
     * @param \Closure $closure
2769
     *
2770
     * @return bool
2771
     */
2772 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...
2773
    {
2774 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2775 2
            return false;
2776
        }
2777
2778 12
        foreach ($this->getGenerator() as $key => $value) {
2779 12
            $value = $closure($value, $key);
2780
2781 12
            if ($value === true) {
2782 12
                return true;
2783
            }
2784
        }
2785
2786 4
        return false;
2787
    }
2788
2789
    /**
2790
     * Get the max value from an array.
2791
     *
2792
     * @return mixed
2793
     */
2794 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...
2795
    {
2796 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2797 1
            return false;
2798
        }
2799
2800 9
        return \max($this->getArray());
2801
    }
2802
2803
    /**
2804
     * Merge the new $array into the current array.
2805
     *
2806
     * - keep key,value from the current array, also if the index is in the new $array
2807
     *
2808
     * @param array $array
2809
     * @param bool  $recursive
2810
     *
2811
     * @return static
2812
     *                <p>(Immutable)</p>
2813
     */
2814 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...
2815
    {
2816 25
        if ($recursive === true) {
2817 4
            $result = \array_replace_recursive($this->getArray(), $array);
2818
        } else {
2819 21
            $result = \array_replace($this->getArray(), $array);
2820
        }
2821
2822 25
        return static::create(
2823 25
            $result,
2824 25
            $this->iteratorClass,
2825 25
            false
2826
        );
2827
    }
2828
2829
    /**
2830
     * Merge the new $array into the current array.
2831
     *
2832
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2833
     * - create new indexes
2834
     *
2835
     * @param array $array
2836
     * @param bool  $recursive
2837
     *
2838
     * @return static
2839
     *                <p>(Immutable)</p>
2840
     */
2841 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...
2842
    {
2843 16
        if ($recursive === true) {
2844 4
            $result = \array_merge_recursive($this->getArray(), $array);
2845
        } else {
2846 12
            $result = \array_merge($this->getArray(), $array);
2847
        }
2848
2849 16
        return static::create(
2850 16
            $result,
2851 16
            $this->iteratorClass,
2852 16
            false
2853
        );
2854
    }
2855
2856
    /**
2857
     * Merge the the current array into the $array.
2858
     *
2859
     * - use key,value from the new $array, also if the index is in the current array
2860
     *
2861
     * @param array $array
2862
     * @param bool  $recursive
2863
     *
2864
     * @return static
2865
     *                <p>(Immutable)</p>
2866
     */
2867 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...
2868
    {
2869 16
        if ($recursive === true) {
2870 4
            $result = \array_replace_recursive($array, $this->getArray());
2871
        } else {
2872 12
            $result = \array_replace($array, $this->getArray());
2873
        }
2874
2875 16
        return static::create(
2876 16
            $result,
2877 16
            $this->iteratorClass,
2878 16
            false
2879
        );
2880
    }
2881
2882
    /**
2883
     * Merge the current array into the new $array.
2884
     *
2885
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2886
     * - create new indexes
2887
     *
2888
     * @param array $array
2889
     * @param bool  $recursive
2890
     *
2891
     * @return static
2892
     *                <p>(Immutable)</p>
2893
     */
2894 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...
2895
    {
2896 17
        if ($recursive === true) {
2897 4
            $result = \array_merge_recursive($array, $this->getArray());
2898
        } else {
2899 13
            $result = \array_merge($array, $this->getArray());
2900
        }
2901
2902 17
        return static::create(
2903 17
            $result,
2904 17
            $this->iteratorClass,
2905 17
            false
2906
        );
2907
    }
2908
2909
    /**
2910
     * @return ArrayyMeta|static
2911
     */
2912 14
    public static function meta()
2913
    {
2914 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2915
    }
2916
2917
    /**
2918
     * Get the min value from an array.
2919
     *
2920
     * @return mixed
2921
     */
2922 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...
2923
    {
2924 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2925 1
            return false;
2926
        }
2927
2928 9
        return \min($this->getArray());
2929
    }
2930
2931
    /**
2932
     * Get the most used value from the array.
2933
     *
2934
     * @return mixed
2935
     *               <p>Return null if there wasn't a element.</p>
2936
     */
2937 3
    public function mostUsedValue()
2938
    {
2939 3
        return $this->countValues()->arsort()->firstKey();
2940
    }
2941
2942
    /**
2943
     * Get the most used value from the array.
2944
     *
2945
     * @param int|null $number <p>How many values you will take?</p>
2946
     *
2947
     * @return static
2948
     *                <p>(Immutable)</p>
2949
     */
2950 3
    public function mostUsedValues(int $number = null): self
2951
    {
2952 3
        return $this->countValues()->arsort()->firstsKeys($number);
2953
    }
2954
2955
    /**
2956
     * Move an array element to a new index.
2957
     *
2958
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2959
     *
2960
     * @param int|string $from
2961
     * @param int        $to
2962
     *
2963
     * @return static
2964
     *                <p>(Immutable)</p>
2965
     */
2966 1
    public function moveElement($from, $to): self
2967
    {
2968 1
        $array = $this->getArray();
2969
2970 1
        if ((int) $from === $from) {
2971 1
            $tmp = \array_splice($array, $from, 1);
2972 1
            \array_splice($array, (int) $to, 0, $tmp);
2973 1
            $output = $array;
2974 1
        } elseif ((string) $from === $from) {
2975 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2976 1
            $itemToMove = $array[$from];
2977 1
            if ($indexToMove !== false) {
2978 1
                \array_splice($array, $indexToMove, 1);
2979
            }
2980 1
            $i = 0;
2981 1
            $output = [];
2982 1
            foreach ($array as $key => $item) {
2983 1
                if ($i === $to) {
2984 1
                    $output[$from] = $itemToMove;
2985
                }
2986 1
                $output[$key] = $item;
2987 1
                ++$i;
2988
            }
2989
        } else {
2990
            $output = [];
2991
        }
2992
2993 1
        return static::create(
2994 1
            $output,
2995 1
            $this->iteratorClass,
2996 1
            false
2997
        );
2998
    }
2999
3000
    /**
3001
     * Move an array element to the first place.
3002
     *
3003
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3004
     *       loss the keys of an indexed array.
3005
     *
3006
     * @param int|string $key
3007
     *
3008
     * @return static
3009
     *                <p>(Immutable)</p>
3010
     */
3011 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...
3012
    {
3013 1
        $array = $this->getArray();
3014
3015 1
        if ($this->offsetExists($key)) {
3016 1
            $tmpValue = $this->get($key);
3017 1
            unset($array[$key]);
3018 1
            $array = [$key => $tmpValue] + $array;
3019
        }
3020
3021 1
        return static::create(
3022 1
            $array,
3023 1
            $this->iteratorClass,
3024 1
            false
3025
        );
3026
    }
3027
3028
    /**
3029
     * Move an array element to the last place.
3030
     *
3031
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3032
     *       loss the keys of an indexed array.
3033
     *
3034
     * @param int|string $key
3035
     *
3036
     * @return static
3037
     *                <p>(Immutable)</p>
3038
     */
3039 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...
3040
    {
3041 1
        $array = $this->getArray();
3042
3043 1
        if ($this->offsetExists($key)) {
3044 1
            $tmpValue = $this->get($key);
3045 1
            unset($array[$key]);
3046 1
            $array += [$key => $tmpValue];
3047
        }
3048
3049 1
        return static::create(
3050 1
            $array,
3051 1
            $this->iteratorClass,
3052 1
            false
3053
        );
3054
    }
3055
3056
    /**
3057
     * Get a subset of the items from the given array.
3058
     *
3059
     * @param mixed[] $keys
3060
     *
3061
     * @return static
3062
     *                <p>(Immutable)</p>
3063
     */
3064
    public function only(array $keys): self
3065
    {
3066
        $array = $this->getArray();
3067
3068
        return static::create(
3069
            \array_intersect_key($array, \array_flip($keys)),
3070
            $this->iteratorClass,
3071
            false
3072
        );
3073
    }
3074
3075
    /**
3076
     * Pad array to the specified size with a given value.
3077
     *
3078
     * @param int   $size  <p>Size of the result array.</p>
3079
     * @param mixed $value <p>Empty value by default.</p>
3080
     *
3081
     * @return static
3082
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3083
     */
3084 4
    public function pad(int $size, $value): self
3085
    {
3086 4
        return static::create(
3087 4
            \array_pad($this->getArray(), $size, $value),
3088 4
            $this->iteratorClass,
3089 4
            false
3090
        );
3091
    }
3092
3093
    /**
3094
     * Pop a specified value off the end of the current array.
3095
     *
3096
     * @return mixed
3097
     *               <p>(Mutable) The popped element from the current array.</p>
3098
     */
3099 4
    public function pop()
3100
    {
3101 4
        $this->generatorToArray();
3102
3103 4
        return \array_pop($this->array);
3104
    }
3105
3106
    /**
3107
     * Prepend a (key) + value to the current array.
3108
     *
3109
     * @param mixed $value
3110
     * @param mixed $key
3111
     *
3112
     * @return static
3113
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3114
     */
3115 9
    public function prepend($value, $key = null): self
3116
    {
3117 9
        $this->generatorToArray();
3118
3119 9
        if ($key === null) {
3120 8
            \array_unshift($this->array, $value);
3121
        } else {
3122 2
            $this->array = [$key => $value] + $this->array;
3123
        }
3124
3125 9
        return $this;
3126
    }
3127
3128
    /**
3129
     * Add a suffix to each key.
3130
     *
3131
     * @param mixed $suffix
3132
     *
3133
     * @return static
3134
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3135
     */
3136 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...
3137
    {
3138
        // init
3139 10
        $result = [];
3140
3141 10
        foreach ($this->getGenerator() as $key => $item) {
3142 9
            if ($item instanceof self) {
3143
                $result[$key] = $item->prependToEachKey($suffix);
3144 9
            } elseif (\is_array($item) === true) {
3145
                $result[$key] = self::create(
3146
                    $item,
3147
                    $this->iteratorClass,
3148
                    false
3149
                )->prependToEachKey($suffix)
3150
                    ->toArray();
3151
            } else {
3152 9
                $result[$key . $suffix] = $item;
3153
            }
3154
        }
3155
3156 10
        return self::create(
3157 10
            $result,
3158 10
            $this->iteratorClass,
3159 10
            false
3160
        );
3161
    }
3162
3163
    /**
3164
     * Add a suffix to each value.
3165
     *
3166
     * @param mixed $suffix
3167
     *
3168
     * @return static
3169
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3170
     */
3171 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...
3172
    {
3173
        // init
3174 10
        $result = [];
3175
3176 10
        foreach ($this->getGenerator() as $key => $item) {
3177 9
            if ($item instanceof self) {
3178
                $result[$key] = $item->prependToEachValue($suffix);
3179 9
            } elseif (\is_array($item) === true) {
3180
                $result[$key] = self::create(
3181
                    $item,
3182
                    $this->iteratorClass,
3183
                    false
3184
                )->prependToEachValue($suffix)
3185
                    ->toArray();
3186 9
            } elseif (\is_object($item) === true) {
3187 1
                $result[$key] = $item;
3188
            } else {
3189 9
                $result[$key] = $item . $suffix;
3190
            }
3191
        }
3192
3193 10
        return self::create(
3194 10
            $result,
3195 10
            $this->iteratorClass,
3196 10
            false
3197
        );
3198
    }
3199
3200
    /**
3201
     * Return the value of a given key and
3202
     * delete the key.
3203
     *
3204
     * @param int|int[]|string|string[]|null $keyOrKeys
3205
     * @param mixed                          $fallback
3206
     *
3207
     * @return mixed
3208
     */
3209 1
    public function pull($keyOrKeys = null, $fallback = null)
3210
    {
3211 1
        if ($keyOrKeys === null) {
3212
            $array = $this->getArray();
3213
            $this->clear();
3214
3215
            return $array;
3216
        }
3217
3218 1
        if (\is_array($keyOrKeys) === true) {
3219 1
            $valueOrValues = [];
3220 1
            foreach ($keyOrKeys as $key) {
3221 1
                $valueOrValues[] = $this->get($key, $fallback);
3222 1
                $this->offsetUnset($key);
3223
            }
3224
        } else {
3225 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3226 1
            $this->offsetUnset($keyOrKeys);
3227
        }
3228
3229 1
        return $valueOrValues;
3230
    }
3231
3232
    /**
3233
     * Push one or more values onto the end of array at once.
3234
     *
3235
     * @return static
3236
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3237
     */
3238 4 View Code Duplication
    public function push(/* variadic arguments allowed */): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3239
    {
3240 4
        $this->generatorToArray();
3241
3242 4
        if (\func_num_args()) {
3243 4
            $args = \func_get_args();
3244 4
            \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...
3245
        }
3246
3247 4
        return $this;
3248
    }
3249
3250
    /**
3251
     * Get a random value from the current array.
3252
     *
3253
     * @param int|null $number <p>How many values you will take?</p>
3254
     *
3255
     * @return static
3256
     *                <p>(Immutable)</p>
3257
     */
3258 18
    public function randomImmutable(int $number = null): self
3259
    {
3260 18
        $this->generatorToArray();
3261
3262 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
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...
3263 1
            return static::create(
3264 1
                [],
3265 1
                $this->iteratorClass,
3266 1
                false
3267
            );
3268
        }
3269
3270 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...
3271
            /** @noinspection NonSecureArrayRandUsageInspection */
3272 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3273
3274 13
            return static::create(
3275 13
                $arrayRandValue,
3276 13
                $this->iteratorClass,
3277 13
                false
3278
            );
3279
        }
3280
3281 5
        $arrayTmp = $this->array;
3282
        /** @noinspection NonSecureShuffleUsageInspection */
3283 5
        \shuffle($arrayTmp);
3284
3285 5
        return static::create(
3286 5
            $arrayTmp,
3287 5
            $this->iteratorClass,
3288 5
            false
3289 5
        )->firstsImmutable($number);
3290
    }
3291
3292
    /**
3293
     * Pick a random key/index from the keys of this array.
3294
     *
3295
     * @throws \RangeException If array is empty
3296
     *
3297
     * @return mixed
3298
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3299
     */
3300 4
    public function randomKey()
3301
    {
3302 4
        $result = $this->randomKeys(1);
3303
3304 4
        if (!isset($result[0])) {
3305
            $result[0] = null;
3306
        }
3307
3308 4
        return $result[0];
3309
    }
3310
3311
    /**
3312
     * Pick a given number of random keys/indexes out of this array.
3313
     *
3314
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3315
     *
3316
     * @throws \RangeException If array is empty
3317
     *
3318
     * @return static
3319
     *                <p>(Immutable)</p>
3320
     */
3321 13
    public function randomKeys(int $number): self
3322
    {
3323 13
        $this->generatorToArray();
3324
3325 13
        $count = \count($this->array, \COUNT_NORMAL);
3326
3327 13
        if ($number === 0 || $number > $count) {
3328 2
            throw new \RangeException(
3329 2
                \sprintf(
3330 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3331 2
                    $number,
3332 2
                    $count
3333
                )
3334
            );
3335
        }
3336
3337 11
        $result = (array) \array_rand($this->array, $number);
3338
3339 11
        return static::create(
3340 11
            $result,
3341 11
            $this->iteratorClass,
3342 11
            false
3343
        );
3344
    }
3345
3346
    /**
3347
     * Get a random value from the current array.
3348
     *
3349
     * @param int|null $number <p>How many values you will take?</p>
3350
     *
3351
     * @return static
3352
     *                <p>(Mutable)</p>
3353
     */
3354 17
    public function randomMutable(int $number = null): self
3355
    {
3356 17
        $this->generatorToArray();
3357
3358 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
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...
3359
            return static::create(
3360
                [],
3361
                $this->iteratorClass,
3362
                false
3363
            );
3364
        }
3365
3366 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...
3367
            /** @noinspection NonSecureArrayRandUsageInspection */
3368 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3369 7
            $this->array = $arrayRandValue;
3370
3371 7
            return $this;
3372
        }
3373
3374
        /** @noinspection NonSecureShuffleUsageInspection */
3375 11
        \shuffle($this->array);
3376
3377 11
        return $this->firstsMutable($number);
3378
    }
3379
3380
    /**
3381
     * Pick a random value from the values of this array.
3382
     *
3383
     * @return mixed
3384
     *               <p>Get a random value or null if there wasn't a value.</p>
3385
     */
3386 4
    public function randomValue()
3387
    {
3388 4
        $result = $this->randomImmutable();
3389
3390 4
        if (!isset($result[0])) {
3391
            $result[0] = null;
3392
        }
3393
3394 4
        return $result[0];
3395
    }
3396
3397
    /**
3398
     * Pick a given number of random values out of this array.
3399
     *
3400
     * @param int $number
3401
     *
3402
     * @return static
3403
     *                <p>(Mutable)</p>
3404
     */
3405 7
    public function randomValues(int $number): self
3406
    {
3407 7
        return $this->randomMutable($number);
3408
    }
3409
3410
    /**
3411
     * Get a random value from an array, with the ability to skew the results.
3412
     *
3413
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3414
     *
3415
     * @param array    $array
3416
     * @param int|null $number <p>How many values you will take?</p>
3417
     *
3418
     * @return static
3419
     *                <p>(Immutable)</p>
3420
     */
3421 9
    public function randomWeighted(array $array, int $number = null): self
3422
    {
3423
        // init
3424 9
        $options = [];
3425
3426 9
        foreach ($array as $option => $weight) {
3427 9
            if ($this->searchIndex($option) !== false) {
3428 9
                for ($i = 0; $i < $weight; ++$i) {
3429 1
                    $options[] = $option;
3430
                }
3431
            }
3432
        }
3433
3434 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3435
    }
3436
3437
    /**
3438
     * Reduce the current array via callable e.g. anonymous-function.
3439
     *
3440
     * @param callable $callable
3441
     * @param array    $init
3442
     *
3443
     * @return static
3444
     *                <p>(Immutable)</p>
3445
     */
3446 16
    public function reduce($callable, array $init = []): self
3447
    {
3448 16
        if ($this->generator) {
3449 1
            $result = $init;
3450
3451 1
            foreach ($this->getGenerator() as $value) {
3452 1
                $result = $callable($result, $value);
3453
            }
3454
3455 1
            return static::create(
3456 1
                $result,
3457 1
                $this->iteratorClass,
3458 1
                false
3459
            );
3460
        }
3461
3462 16
        $result = \array_reduce($this->array, $callable, $init);
3463
3464 16
        if ($result === null) {
3465
            $this->array = [];
3466
        } else {
3467 16
            $this->array = (array) $result;
3468
        }
3469
3470 16
        return static::create(
3471 16
            $this->array,
3472 16
            $this->iteratorClass,
3473 16
            false
3474
        );
3475
    }
3476
3477
    /**
3478
     * @param bool $unique
3479
     *
3480
     * @return static
3481
     *                <p>(Immutable)</p>
3482
     */
3483 14
    public function reduce_dimension(bool $unique = true): self
3484
    {
3485
        // init
3486 14
        $result = [];
3487
3488 14
        foreach ($this->getGenerator() as $val) {
3489 12
            if (\is_array($val) === true) {
3490 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3491
            } else {
3492 12
                $result[] = [$val];
3493
            }
3494
        }
3495
3496 14
        $result = $result === [] ? [] : \array_merge(...$result);
3497
3498 14
        $resultArrayy = new self($result);
3499
3500 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3501
    }
3502
3503
    /**
3504
     * Create a numerically re-indexed Arrayy object.
3505
     *
3506
     * @return static
3507
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3508
     */
3509 9
    public function reindex(): self
3510
    {
3511 9
        $this->generatorToArray();
3512
3513 9
        $this->array = \array_values($this->array);
3514
3515 9
        return $this;
3516
    }
3517
3518
    /**
3519
     * Return all items that fail the truth test.
3520
     *
3521
     * @param \Closure $closure
3522
     *
3523
     * @return static
3524
     *                <p>(Immutable)</p>
3525
     */
3526 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...
3527
    {
3528
        // init
3529 1
        $filtered = [];
3530
3531 1
        foreach ($this->getGenerator() as $key => $value) {
3532 1
            if (!$closure($value, $key)) {
3533 1
                $filtered[$key] = $value;
3534
            }
3535
        }
3536
3537 1
        return static::create(
3538 1
            $filtered,
3539 1
            $this->iteratorClass,
3540 1
            false
3541
        );
3542
    }
3543
3544
    /**
3545
     * Remove a value from the current array (optional using dot-notation).
3546
     *
3547
     * @param mixed $key
3548
     *
3549
     * @return static
3550
     *                <p>(Mutable)</p>
3551
     */
3552 18
    public function remove($key): self
3553
    {
3554
        // recursive call
3555 18
        if (\is_array($key) === true) {
3556
            foreach ($key as $k) {
3557
                $this->internalRemove($k);
3558
            }
3559
3560
            return static::create(
3561
                $this->getArray(),
3562
                $this->iteratorClass,
3563
                false
3564
            );
3565
        }
3566
3567 18
        $this->internalRemove($key);
3568
3569 18
        return static::create(
3570 18
            $this->getArray(),
3571 18
            $this->iteratorClass,
3572 18
            false
3573
        );
3574
    }
3575
3576
    /**
3577
     * Remove the first value from the current array.
3578
     *
3579
     * @return static
3580
     *                <p>(Immutable)</p>
3581
     */
3582 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...
3583
    {
3584 7
        $tmpArray = $this->getArray();
3585
3586 7
        \array_shift($tmpArray);
3587
3588 7
        return static::create(
3589 7
            $tmpArray,
3590 7
            $this->iteratorClass,
3591 7
            false
3592
        );
3593
    }
3594
3595
    /**
3596
     * Remove the last value from the current array.
3597
     *
3598
     * @return static
3599
     *                <p>(Immutable)</p>
3600
     */
3601 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...
3602
    {
3603 7
        $tmpArray = $this->getArray();
3604
3605 7
        \array_pop($tmpArray);
3606
3607 7
        return static::create(
3608 7
            $tmpArray,
3609 7
            $this->iteratorClass,
3610 7
            false
3611
        );
3612
    }
3613
3614
    /**
3615
     * Removes a particular value from an array (numeric or associative).
3616
     *
3617
     * @param mixed $value
3618
     *
3619
     * @return static
3620
     *                <p>(Immutable)</p>
3621
     */
3622 7
    public function removeValue($value): self
3623
    {
3624 7
        $this->generatorToArray();
3625
3626
        // init
3627 7
        $isNumericArray = true;
3628
3629 7
        foreach ($this->getGenerator() as $key => $item) {
3630 6
            if ($item === $value) {
3631 6
                if ((int) $key !== $key) {
3632
                    $isNumericArray = false;
3633
                }
3634 6
                unset($this->array[$key]);
3635
            }
3636
        }
3637
3638 7
        if ($isNumericArray) {
3639 7
            $this->array = \array_values($this->array);
3640
        }
3641
3642 7
        return static::create(
3643 7
            $this->array,
3644 7
            $this->iteratorClass,
3645 7
            false
3646
        );
3647
    }
3648
3649
    /**
3650
     * Generate array of repeated arrays.
3651
     *
3652
     * @param int $times <p>How many times has to be repeated.</p>
3653
     *
3654
     * @return static
3655
     *                <p>(Immutable)</p>
3656
     */
3657 1
    public function repeat($times): self
3658
    {
3659 1
        if ($times === 0) {
3660 1
            return new static();
3661
        }
3662
3663 1
        return static::create(
3664 1
            \array_fill(0, (int) $times, $this->getArray()),
3665 1
            $this->iteratorClass,
3666 1
            false
3667
        );
3668
    }
3669
3670
    /**
3671
     * Replace a key with a new key/value pair.
3672
     *
3673
     * @param mixed $replace
3674
     * @param mixed $key
3675
     * @param mixed $value
3676
     *
3677
     * @return static
3678
     *                <p>(Immutable)</p>
3679
     */
3680 2
    public function replace($replace, $key, $value): self
3681
    {
3682 2
        $that = clone $this;
3683
3684 2
        return $that->remove($replace)
3685 2
            ->set($key, $value);
3686
    }
3687
3688
    /**
3689
     * Create an array using the current array as values and the other array as keys.
3690
     *
3691
     * @param array $keys <p>An array of keys.</p>
3692
     *
3693
     * @return static
3694
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3695
     */
3696 2
    public function replaceAllKeys(array $keys): self
3697
    {
3698 2
        return static::create(
3699 2
            \array_combine($keys, $this->getArray()),
3700 2
            $this->iteratorClass,
3701 2
            false
3702
        );
3703
    }
3704
3705
    /**
3706
     * Create an array using the current array as keys and the other array as values.
3707
     *
3708
     * @param array $array <p>An array o values.</p>
3709
     *
3710
     * @return static
3711
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3712
     */
3713 2
    public function replaceAllValues(array $array): self
3714
    {
3715 2
        return static::create(
3716 2
            \array_combine($this->array, $array),
3717 2
            $this->iteratorClass,
3718 2
            false
3719
        );
3720
    }
3721
3722
    /**
3723
     * Replace the keys in an array with another set.
3724
     *
3725
     * @param array $keys <p>An array of keys matching the array's size</p>
3726
     *
3727
     * @return static
3728
     *                <p>(Immutable)</p>
3729
     */
3730 1
    public function replaceKeys(array $keys): self
3731
    {
3732 1
        $values = \array_values($this->getArray());
3733 1
        $result = \array_combine($keys, $values);
3734
3735 1
        return static::create(
3736 1
            $result,
3737 1
            $this->iteratorClass,
3738 1
            false
3739
        );
3740
    }
3741
3742
    /**
3743
     * Replace the first matched value in an array.
3744
     *
3745
     * @param mixed $search      <p>The value to replace.</p>
3746
     * @param mixed $replacement <p>The value to replace.</p>
3747
     *
3748
     * @return static
3749
     *                <p>(Immutable)</p>
3750
     */
3751 3
    public function replaceOneValue($search, $replacement = ''): self
3752
    {
3753 3
        $array = $this->getArray();
3754 3
        $key = \array_search($search, $array, true);
3755
3756 3
        if ($key !== false) {
3757 3
            $array[$key] = $replacement;
3758
        }
3759
3760 3
        return static::create(
3761 3
            $array,
3762 3
            $this->iteratorClass,
3763 3
            false
3764
        );
3765
    }
3766
3767
    /**
3768
     * Replace values in the current array.
3769
     *
3770
     * @param mixed $search      <p>The value to replace.</p>
3771
     * @param mixed $replacement <p>What to replace it with.</p>
3772
     *
3773
     * @return static
3774
     *                <p>(Immutable)</p>
3775
     */
3776 1
    public function replaceValues($search, $replacement = ''): self
3777
    {
3778 1
        return $this->each(
3779 1
            static function ($value) use ($search, $replacement) {
3780 1
                return \str_replace($search, $replacement, $value);
3781 1
            }
3782
        );
3783
    }
3784
3785
    /**
3786
     * Get the last elements from index $from until the end of this array.
3787
     *
3788
     * @param int $from
3789
     *
3790
     * @return static
3791
     *                <p>(Immutable)</p>
3792
     */
3793 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...
3794
    {
3795 15
        $tmpArray = $this->getArray();
3796
3797 15
        return static::create(
3798 15
            \array_splice($tmpArray, $from),
3799 15
            $this->iteratorClass,
3800 15
            false
3801
        );
3802
    }
3803
3804
    /**
3805
     * Return the array in the reverse order.
3806
     *
3807
     * @return static
3808
     *                <p>(Mutable) Return this Arrayy object.</p>
3809
     */
3810 8
    public function reverse(): self
3811
    {
3812 8
        $this->generatorToArray();
3813
3814 8
        $this->array = \array_reverse($this->array);
3815
3816 8
        return $this;
3817
    }
3818
3819
    /**
3820
     * Sort an array in reverse order.
3821
     *
3822
     * @param int $sort_flags [optional] <p>
3823
     *                        You may modify the behavior of the sort using the optional
3824
     *                        parameter sort_flags, for details
3825
     *                        see sort.
3826
     *                        </p>
3827
     *
3828
     * @return static
3829
     *                <p>(Mutable) Return this Arrayy object.</p>
3830
     */
3831 4
    public function rsort(int $sort_flags = 0): self
3832
    {
3833 4
        $this->generatorToArray();
3834
3835 4
        \rsort($this->array, $sort_flags);
3836
3837 4
        return $this;
3838
    }
3839
3840
    /**
3841
     * Search for the first index of the current array via $value.
3842
     *
3843
     * @param mixed $value
3844
     *
3845
     * @return false|float|int|string
3846
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3847
     */
3848 20
    public function searchIndex($value)
3849
    {
3850 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3851 19
            if ($value === $valueFromArray) {
3852 19
                return $keyFromArray;
3853
            }
3854
        }
3855
3856 11
        return false;
3857
    }
3858
3859
    /**
3860
     * Search for the value of the current array via $index.
3861
     *
3862
     * @param mixed $index
3863
     *
3864
     * @return static
3865
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3866
     */
3867 9
    public function searchValue($index): self
3868
    {
3869 9
        $this->generatorToArray();
3870
3871
        // init
3872 9
        $return = [];
3873
3874 9
        if ($this->array === []) {
3875
            return static::create(
3876
                [],
3877
                $this->iteratorClass,
3878
                false
3879
            );
3880
        }
3881
3882
        // php cast "bool"-index into "int"-index
3883 9
        if ((bool) $index === $index) {
3884 1
            $index = (int) $index;
3885
        }
3886
3887 9
        if ($this->offsetExists($index)) {
3888 7
            $return = [$this->array[$index]];
3889
        }
3890
3891 9
        return static::create(
3892 9
            $return,
3893 9
            $this->iteratorClass,
3894 9
            false
3895
        );
3896
    }
3897
3898
    /**
3899
     * Set a value for the current array (optional using dot-notation).
3900
     *
3901
     * @param string $key   <p>The key to set.</p>
3902
     * @param mixed  $value <p>Its value.</p>
3903
     *
3904
     * @return static
3905
     *                <p>(Mutable)</p>
3906
     */
3907 18
    public function set($key, $value): self
3908
    {
3909 18
        $this->generatorToArray();
3910
3911 18
        $this->internalSet($key, $value);
3912
3913 18
        return $this;
3914
    }
3915
3916
    /**
3917
     * Get a value from a array and set it if it was not.
3918
     *
3919
     * WARNING: this method only set the value, if the $key is not already set
3920
     *
3921
     * @param mixed $key      <p>The key</p>
3922
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3923
     *
3924
     * @return mixed
3925
     *               <p>(Mutable)</p>
3926
     */
3927 11
    public function setAndGet($key, $fallback = null)
3928
    {
3929 11
        $this->generatorToArray();
3930
3931
        // If the key doesn't exist, set it.
3932 11
        if (!$this->has($key)) {
3933 4
            $this->array = $this->set($key, $fallback)->getArray();
3934
        }
3935
3936 11
        return $this->get($key);
3937
    }
3938
3939
    /**
3940
     * Shifts a specified value off the beginning of array.
3941
     *
3942
     * @return mixed
3943
     *               <p>(Mutable) A shifted element from the current array.</p>
3944
     */
3945 4
    public function shift()
3946
    {
3947 4
        $this->generatorToArray();
3948
3949 4
        return \array_shift($this->array);
3950
    }
3951
3952
    /**
3953
     * Shuffle the current array.
3954
     *
3955
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3956
     * @param array $array  [optional]
3957
     *
3958
     * @return static
3959
     *                <p>(Immutable)</p>
3960
     */
3961 1
    public function shuffle(bool $secure = false, array $array = null): self
3962
    {
3963 1
        if ($array === null) {
3964 1
            $array = $this->getArray();
3965
        }
3966
3967 1
        if ($secure !== true) {
3968
            /** @noinspection NonSecureShuffleUsageInspection */
3969 1
            \shuffle($array);
3970
        } else {
3971 1
            $size = \count($array, \COUNT_NORMAL);
3972 1
            $keys = \array_keys($array);
3973 1
            for ($i = $size - 1; $i > 0; --$i) {
3974
                try {
3975 1
                    $r = \random_int(0, $i);
3976
                } catch (\Exception $e) {
3977
                    /** @noinspection RandomApiMigrationInspection */
3978
                    $r = \mt_rand(0, $i);
3979
                }
3980 1
                if ($r !== $i) {
3981 1
                    $temp = $array[$keys[$r]];
3982 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3983 1
                    $array[$keys[$i]] = $temp;
3984
                }
3985
            }
3986
3987
            // reset indices
3988 1
            $array = \array_values($array);
3989
        }
3990
3991 1
        foreach ($array as $key => $value) {
3992
            // check if recursive is needed
3993 1
            if (\is_array($value) === true) {
3994 1
                $array[$key] = $this->shuffle($secure, $value);
3995
            }
3996
        }
3997
3998 1
        return static::create(
3999 1
            $array,
4000 1
            $this->iteratorClass,
4001 1
            false
4002
        );
4003
    }
4004
4005
    /**
4006
     * Count the values from the current array.
4007
     *
4008
     * alias: for "Arrayy->count()"
4009
     *
4010
     * @param int $mode
4011
     *
4012
     * @return int
4013
     */
4014 20
    public function size(int $mode = \COUNT_NORMAL): int
4015
    {
4016 20
        return $this->count($mode);
4017
    }
4018
4019
    /**
4020
     * Checks whether array has exactly $size items.
4021
     *
4022
     * @param int $size
4023
     *
4024
     * @return bool
4025
     */
4026 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...
4027
    {
4028
        // init
4029 1
        $itemsTempCount = 0;
4030
4031 1
        foreach ($this->getGenerator() as $key => $value) {
4032 1
            ++$itemsTempCount;
4033 1
            if ($itemsTempCount > $size) {
4034 1
                return false;
4035
            }
4036
        }
4037
4038 1
        return $itemsTempCount === $size;
4039
    }
4040
4041
    /**
4042
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4043
     * smaller than $fromSize.
4044
     *
4045
     * @param int $fromSize
4046
     * @param int $toSize
4047
     *
4048
     * @return bool
4049
     */
4050 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4051
    {
4052 1
        if ($fromSize > $toSize) {
4053 1
            $tmp = $toSize;
4054 1
            $toSize = $fromSize;
4055 1
            $fromSize = $tmp;
4056
        }
4057
4058
        // init
4059 1
        $itemsTempCount = 0;
4060
4061 1
        foreach ($this->getGenerator() as $key => $value) {
4062 1
            ++$itemsTempCount;
4063 1
            if ($itemsTempCount > $toSize) {
4064 1
                return false;
4065
            }
4066
        }
4067
4068 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4069
    }
4070
4071
    /**
4072
     * Checks whether array has more than $size items.
4073
     *
4074
     * @param int $size
4075
     *
4076
     * @return bool
4077
     */
4078 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...
4079
    {
4080
        // init
4081 1
        $itemsTempCount = 0;
4082
4083 1
        foreach ($this->getGenerator() as $key => $value) {
4084 1
            ++$itemsTempCount;
4085 1
            if ($itemsTempCount > $size) {
4086 1
                return true;
4087
            }
4088
        }
4089
4090 1
        return $itemsTempCount > $size;
4091
    }
4092
4093
    /**
4094
     * Checks whether array has less than $size items.
4095
     *
4096
     * @param int $size
4097
     *
4098
     * @return bool
4099
     */
4100 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...
4101
    {
4102
        // init
4103 1
        $itemsTempCount = 0;
4104
4105 1
        foreach ($this->getGenerator() as $key => $value) {
4106 1
            ++$itemsTempCount;
4107 1
            if ($itemsTempCount > $size) {
4108 1
                return false;
4109
            }
4110
        }
4111
4112 1
        return $itemsTempCount < $size;
4113
    }
4114
4115
    /**
4116
     * Counts all elements in an array, or something in an object.
4117
     *
4118
     * <p>
4119
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4120
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4121
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4122
     * implemented and used in PHP.
4123
     * </p>
4124
     *
4125
     * @return int
4126
     *             <p>
4127
     *             The number of elements in var, which is
4128
     *             typically an array, since anything else will have one
4129
     *             element.
4130
     *             </p>
4131
     *             <p>
4132
     *             If var is not an array or an object with
4133
     *             implemented Countable interface,
4134
     *             1 will be returned.
4135
     *             There is one exception, if var is &null;,
4136
     *             0 will be returned.
4137
     *             </p>
4138
     *             <p>
4139
     *             Caution: count may return 0 for a variable that isn't set,
4140
     *             but it may also return 0 for a variable that has been initialized with an
4141
     *             empty array. Use isset to test if a variable is set.
4142
     *             </p>
4143
     */
4144 10
    public function sizeRecursive(): int
4145
    {
4146 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4147
    }
4148
4149
    /**
4150
     * Extract a slice of the array.
4151
     *
4152
     * @param int      $offset       <p>Slice begin index.</p>
4153
     * @param int|null $length       <p>Length of the slice.</p>
4154
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4155
     *
4156
     * @return static
4157
     *                <p>A slice of the original array with length $length.</p>
4158
     */
4159 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4160
    {
4161 4
        return static::create(
4162 4
            \array_slice(
4163 4
                $this->getArray(),
4164 4
                $offset,
4165 4
                $length,
4166 4
                $preserveKeys
4167
            ),
4168 4
            $this->iteratorClass,
4169 4
            false
4170
        );
4171
    }
4172
4173
    /**
4174
     * Sort the current array and optional you can keep the keys.
4175
     *
4176
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4177
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4178
     *                              <strong>SORT_NATURAL</strong></p>
4179
     * @param bool       $keepKeys
4180
     *
4181
     * @return static
4182
     *                <p>(Mutable) Return this Arrayy object.</p>
4183
     */
4184 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4185
    {
4186 20
        $this->generatorToArray();
4187
4188 20
        return $this->sorting(
4189 20
            $this->array,
4190 20
            $direction,
4191 20
            $strategy,
4192 20
            $keepKeys
4193
        );
4194
    }
4195
4196
    /**
4197
     * Sort the current array by key.
4198
     *
4199
     * @see http://php.net/manual/en/function.ksort.php
4200
     * @see http://php.net/manual/en/function.krsort.php
4201
     *
4202
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4203
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4204
     *                              <strong>SORT_NATURAL</strong></p>
4205
     *
4206
     * @return static
4207
     *                <p>(Mutable) Return this Arrayy object.</p>
4208
     */
4209 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4210
    {
4211 18
        $this->generatorToArray();
4212
4213 18
        $this->sorterKeys($this->array, $direction, $strategy);
4214
4215 18
        return $this;
4216
    }
4217
4218
    /**
4219
     * Sort the current array by value.
4220
     *
4221
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4222
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4223
     *                              <strong>SORT_NATURAL</strong></p>
4224
     *
4225
     * @return static
4226
     *                <p>(Mutable)</p>
4227
     */
4228 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4229
    {
4230 1
        return $this->sort($direction, $strategy, true);
4231
    }
4232
4233
    /**
4234
     * Sort the current array by value.
4235
     *
4236
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4237
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4238
     *                              <strong>SORT_NATURAL</strong></p>
4239
     *
4240
     * @return static
4241
     *                <p>(Mutable)</p>
4242
     */
4243 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4244
    {
4245 1
        return $this->sort($direction, $strategy, false);
4246
    }
4247
4248
    /**
4249
     * Sort a array by value, by a closure or by a property.
4250
     *
4251
     * - If the sorter is null, the array is sorted naturally.
4252
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4253
     *
4254
     * @param callable|string|null $sorter
4255
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4256
     *                                        <strong>SORT_DESC</strong></p>
4257
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4258
     *                                        <strong>SORT_NATURAL</strong></p>
4259
     *
4260
     * @return static
4261
     *                <p>(Immutable)</p>
4262
     */
4263 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4264
    {
4265 1
        $array = $this->getArray();
4266 1
        $direction = $this->getDirection($direction);
4267
4268
        // Transform all values into their results.
4269 1
        if ($sorter) {
4270 1
            $arrayy = static::create(
4271 1
                $array,
4272 1
                $this->iteratorClass,
4273 1
                false
4274
            );
4275
4276 1
            $results = $arrayy->each(
4277 1
                function ($value) use ($sorter) {
4278 1
                    if (\is_callable($sorter) === true) {
4279 1
                        return $sorter($value);
4280
                    }
4281
4282 1
                    return $this->get($sorter);
4283 1
                }
4284
            );
4285
4286 1
            $results = $results->getArray();
4287
        } else {
4288 1
            $results = $array;
4289
        }
4290
4291
        // Sort by the results and replace by original values
4292 1
        \array_multisort($results, $direction, $strategy, $array);
4293
4294 1
        return static::create(
4295 1
            $array,
4296 1
            $this->iteratorClass,
4297 1
            false
4298
        );
4299
    }
4300
4301
    /**
4302
     * Split an array in the given amount of pieces.
4303
     *
4304
     * @param int  $numberOfPieces
4305
     * @param bool $keepKeys
4306
     *
4307
     * @return static
4308
     *                <p>(Immutable)</p>
4309
     */
4310 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4311
    {
4312 1
        $this->generatorToArray();
4313
4314 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4315
4316 1
        if ($arrayCount === 0) {
4317 1
            $result = [];
4318
        } else {
4319 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4320 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4321
        }
4322
4323 1
        return static::create(
4324 1
            $result,
4325 1
            $this->iteratorClass,
4326 1
            false
4327
        );
4328
    }
4329
4330
    /**
4331
     * Stripe all empty items.
4332
     *
4333
     * @return static
4334
     *                <p>(Immutable)</p>
4335
     */
4336 1
    public function stripEmpty(): self
4337
    {
4338 1
        return $this->filter(
4339 1
            static function ($item) {
4340 1
                if ($item === null) {
4341 1
                    return false;
4342
                }
4343
4344 1
                return (bool) \trim((string) $item);
4345 1
            }
4346
        );
4347
    }
4348
4349
    /**
4350
     * Swap two values between positions by key.
4351
     *
4352
     * @param int|string $swapA <p>a key in the array</p>
4353
     * @param int|string $swapB <p>a key in the array</p>
4354
     *
4355
     * @return static
4356
     *                <p>(Immutable)</p>
4357
     */
4358 1
    public function swap($swapA, $swapB): self
4359
    {
4360 1
        $array = $this->getArray();
4361
4362 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4363
4364 1
        return static::create(
4365 1
            $array,
4366 1
            $this->iteratorClass,
4367 1
            false
4368
        );
4369
    }
4370
4371
    /**
4372
     * alias: for "Arrayy->getArray()"
4373
     *
4374
     * @see Arrayy::getArray()
4375
     */
4376 205
    public function toArray()
4377
    {
4378 205
        return $this->getArray();
4379
    }
4380
4381
    /**
4382
     * Convert the current array to JSON.
4383
     *
4384
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4385
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4386
     *
4387
     * @return string
4388
     */
4389 6
    public function toJson(int $options = 0, int $depth = 512): string
4390
    {
4391 6
        $return = \json_encode($this->getArray(), $options, $depth);
4392 6
        if ($return === false) {
4393
            return '';
4394
        }
4395
4396 6
        return $return;
4397
    }
4398
4399
    /**
4400
     * Implodes array to a string with specified separator.
4401
     *
4402
     * @param string $separator [optional] <p>The element's separator.</p>
4403
     *
4404
     * @return string
4405
     *                <p>The string representation of array, separated by ",".</p>
4406
     */
4407 20
    public function toString(string $separator = ','): string
4408
    {
4409 20
        return $this->implode($separator);
4410
    }
4411
4412
    /**
4413
     * Return a duplicate free copy of the current array.
4414
     *
4415
     * @return static
4416
     *                <p>(Mutable)</p>
4417
     */
4418 12
    public function unique(): self
4419
    {
4420
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4421
4422 12
        $this->array = $this->reduce(
4423 12
            static function ($resultArray, $value) {
4424 11
                if (!\in_array($value, $resultArray, true)) {
4425 11
                    $resultArray[] = $value;
4426
                }
4427
4428 11
                return $resultArray;
4429 12
            },
4430 12
            []
4431 12
        )->getArray();
4432 12
        $this->generator = null;
4433
4434 12
        return $this;
4435
    }
4436
4437
    /**
4438
     * Return a duplicate free copy of the current array. (with the old keys)
4439
     *
4440
     * @return static
4441
     *                <p>(Mutable)</p>
4442
     */
4443 11
    public function uniqueKeepIndex(): self
4444
    {
4445
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4446
4447
        // init
4448 11
        $array = $this->getArray();
4449
4450 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...
4451 11
            \array_keys($array),
4452 11
            static function ($resultArray, $key) use ($array) {
4453 10
                if (!\in_array($array[$key], $resultArray, true)) {
4454 10
                    $resultArray[$key] = $array[$key];
4455
                }
4456
4457 10
                return $resultArray;
4458 11
            },
4459 11
            []
4460
        );
4461 11
        $this->generator = null;
4462
4463 11
        if ($this->array === null) {
4464
            $this->array = [];
4465
        } else {
4466 11
            $this->array = (array) $this->array;
4467
        }
4468
4469 11
        return $this;
4470
    }
4471
4472
    /**
4473
     * alias: for "Arrayy->unique()"
4474
     *
4475
     * @return static
4476
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4477
     *
4478
     * @see Arrayy::unique()
4479
     */
4480 10
    public function uniqueNewIndex(): self
4481
    {
4482 10
        return $this->unique();
4483
    }
4484
4485
    /**
4486
     * Prepends one or more values to the beginning of array at once.
4487
     *
4488
     * @return static
4489
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4490
     */
4491 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */): 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...
4492
    {
4493 4
        $this->generatorToArray();
4494
4495 4
        if (\func_num_args()) {
4496 4
            $args = \func_get_args();
4497 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...
4498
        }
4499
4500 4
        return $this;
4501
    }
4502
4503
    /**
4504
     * Get all values from a array.
4505
     *
4506
     * @return static
4507
     *                <p>(Immutable)</p>
4508
     */
4509 2
    public function values(): self
4510
    {
4511 2
        return static::create(
4512 2
            function () {
4513
                /** @noinspection YieldFromCanBeUsedInspection */
4514 2
                foreach ($this->getGenerator() as $value) {
4515 2
                    yield $value;
4516
                }
4517 2
            },
4518 2
            $this->iteratorClass,
4519 2
            false
4520
        );
4521
    }
4522
4523
    /**
4524
     * Apply the given function to every element in the array, discarding the results.
4525
     *
4526
     * @param callable $callable
4527
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4528
     *
4529
     * @return static
4530
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4531
     */
4532 11
    public function walk($callable, bool $recursive = false): self
4533
    {
4534 11
        $this->generatorToArray();
4535
4536 11
        if ($recursive === true) {
4537 6
            \array_walk_recursive($this->array, $callable);
4538
        } else {
4539 5
            \array_walk($this->array, $callable);
4540
        }
4541
4542 11
        return $this;
4543
    }
4544
4545
    /**
4546
     * Convert an array into a object.
4547
     *
4548
     * @param array $array PHP array
4549
     *
4550
     * @return \stdClass
4551
     */
4552 4
    protected static function arrayToObject(array $array = []): \stdClass
4553
    {
4554
        // init
4555 4
        $object = new \stdClass();
4556
4557 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4558 1
            return $object;
4559
        }
4560
4561 3
        foreach ($array as $name => $value) {
4562 3
            if (\is_array($value) === true) {
4563 1
                $object->{$name} = self::arrayToObject($value);
4564
            } else {
4565 3
                $object->{$name} = $value;
4566
            }
4567
        }
4568
4569 3
        return $object;
4570
    }
4571
4572
    /**
4573
     * @param array|\Generator|null $input         <p>
4574
     *                                             An array containing keys to return.
4575
     *                                             </p>
4576
     * @param mixed|null            $search_values [optional] <p>
4577
     *                                             If specified, then only keys containing these values are returned.
4578
     *                                             </p>
4579
     * @param bool                  $strict        [optional] <p>
4580
     *                                             Determines if strict comparison (===) should be used during the
4581
     *                                             search.
4582
     *                                             </p>
4583
     *
4584
     * @return array
4585
     *               <p>an array of all the keys in input</p>
4586
     */
4587 11
    protected function array_keys_recursive(
4588
        $input = null,
4589
        $search_values = null,
4590
        bool $strict = true
4591
    ): array {
4592
        // init
4593 11
        $keys = [];
4594 11
        $keysTmp = [];
4595
4596 11
        if ($input === null) {
4597 4
            $input = $this->getGenerator();
4598
        }
4599
4600 11
        if ($search_values === null) {
4601 11
            foreach ($input as $key => $value) {
4602 11
                $keys[] = $key;
4603
4604
                // check if recursive is needed
4605 11
                if (\is_array($value) === true) {
4606 11
                    $keysTmp[] = $this->array_keys_recursive($value);
4607
                }
4608
            }
4609
        } else {
4610 1
            $is_array_tmp = \is_array($search_values);
4611
4612 1
            foreach ($input as $key => $value) {
4613 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...
4614
                    (
4615 1
                        $is_array_tmp === false
4616
                        &&
4617 1
                        $strict === true
4618
                        &&
4619 1
                        $search_values === $value
4620
                    )
4621
                    ||
4622
                    (
4623 1
                        $is_array_tmp === false
4624
                        &&
4625 1
                        $strict === false
4626
                        &&
4627 1
                        $search_values == $value
4628
                    )
4629
                    ||
4630
                    (
4631 1
                        $is_array_tmp === true
4632
                        &&
4633 1
                        \in_array($value, $search_values, $strict)
4634
                    )
4635
                ) {
4636 1
                    $keys[] = $key;
4637
                }
4638
4639
                // check if recursive is needed
4640 1
                if (\is_array($value) === true) {
4641 1
                    $keysTmp[] = $this->array_keys_recursive($value);
4642
                }
4643
            }
4644
        }
4645
4646 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
4647
    }
4648
4649
    /**
4650
     * @param mixed      $path
4651
     * @param callable   $callable
4652
     * @param array|null $currentOffset
4653
     */
4654 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4655
    {
4656 4
        $this->generatorToArray();
4657
4658 4
        if ($currentOffset === null) {
4659 4
            $currentOffset = &$this->array;
4660
        }
4661
4662 4
        $explodedPath = \explode($this->pathSeparator, $path);
4663 4
        if ($explodedPath === false) {
4664
            return;
4665
        }
4666
4667 4
        $nextPath = \array_shift($explodedPath);
4668
4669 4
        if (!isset($currentOffset[$nextPath])) {
4670
            return;
4671
        }
4672
4673 4
        if (!empty($explodedPath)) {
4674 1
            $this->callAtPath(
4675 1
                \implode($this->pathSeparator, $explodedPath),
4676 1
                $callable,
4677 1
                $currentOffset[$nextPath]
4678
            );
4679
        } else {
4680 4
            $callable($currentOffset[$nextPath]);
4681
        }
4682 4
    }
4683
4684
    /**
4685
     * create a fallback for array
4686
     *
4687
     * 1. use the current array, if it's a array
4688
     * 2. fallback to empty array, if there is nothing
4689
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4690
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4691
     * 5. call "__toArray()" on object, if the method exists
4692
     * 6. cast a string or object with "__toString()" into an array
4693
     * 7. throw a "InvalidArgumentException"-Exception
4694
     *
4695
     * @param mixed $data
4696
     *
4697
     * @throws \InvalidArgumentException
4698
     *
4699
     * @return array
4700
     */
4701 991
    protected function fallbackForArray(&$data): array
4702
    {
4703 991
        $data = $this->internalGetArray($data);
4704
4705 991
        if ($data === null) {
4706 2
            throw new \InvalidArgumentException(
4707 2
                'Passed value should be a array'
4708
            );
4709
        }
4710
4711 989
        return $data;
4712
    }
4713
4714
    /**
4715
     * Get correct PHP constant for direction.
4716
     *
4717
     * @param int|string $direction
4718
     *
4719
     * @return int
4720
     */
4721 39
    protected function getDirection($direction): int
4722
    {
4723 39
        if ((string) $direction === $direction) {
4724 10
            $direction = \strtolower($direction);
4725
4726 10
            if ($direction === 'desc') {
4727 2
                $direction = \SORT_DESC;
4728
            } else {
4729 8
                $direction = \SORT_ASC;
4730
            }
4731
        }
4732
4733
        if (
4734 39
            $direction !== \SORT_DESC
4735
            &&
4736 39
            $direction !== \SORT_ASC
4737
        ) {
4738
            $direction = \SORT_ASC;
4739
        }
4740
4741 39
        return $direction;
4742
    }
4743
4744
    /**
4745
     * @param mixed $glue
4746
     * @param mixed $pieces
4747
     * @param bool  $useKeys
4748
     *
4749
     * @return string
4750
     */
4751 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4752
    {
4753 36
        if ($pieces instanceof self) {
4754 1
            $pieces = $pieces->getArray();
4755
        }
4756
4757 36
        if (\is_array($pieces) === true) {
4758 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4759 36
            $pieces_count_not_zero = $pieces_count > 0;
4760
4761 36
            return \implode(
4762 36
                $glue,
4763 36
                \array_map(
4764 36
                    [$this, 'implode_recursive'],
4765 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4766 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4767
                )
4768
            );
4769
        }
4770
4771
        if (
4772 36
            \is_scalar($pieces) === true
4773
            ||
4774 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4775
        ) {
4776 31
            return (string) $pieces;
4777
        }
4778
4779 9
        return '';
4780
    }
4781
4782
    /**
4783
     * @param mixed                 $needle   <p>
4784
     *                                        The searched value.
4785
     *                                        </p>
4786
     *                                        <p>
4787
     *                                        If needle is a string, the comparison is done
4788
     *                                        in a case-sensitive manner.
4789
     *                                        </p>
4790
     * @param array|\Generator|null $haystack <p>
4791
     *                                        The array.
4792
     *                                        </p>
4793
     * @param bool                  $strict   [optional] <p>
4794
     *                                        If the third parameter strict is set to true
4795
     *                                        then the in_array function will also check the
4796
     *                                        types of the
4797
     *                                        needle in the haystack.
4798
     *                                        </p>
4799
     *
4800
     * @return bool
4801
     *              <p>true if needle is found in the array, false otherwise</p>
4802
     */
4803 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4804
    {
4805 18
        if ($haystack === null) {
4806
            $haystack = $this->getGenerator();
4807
        }
4808
4809 18
        foreach ($haystack as $item) {
4810 14
            if (\is_array($item) === true) {
4811 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4812
            } else {
4813
                /** @noinspection NestedPositiveIfStatementsInspection */
4814 14
                if ($strict === true) {
4815 14
                    $returnTmp = $item === $needle;
4816
                } else {
4817
                    $returnTmp = $item == $needle;
4818
                }
4819
            }
4820
4821 14
            if ($returnTmp === true) {
4822 14
                return true;
4823
            }
4824
        }
4825
4826 8
        return false;
4827
    }
4828
4829
    /**
4830
     * @param mixed $data
4831
     *
4832
     * @return array|null
4833
     */
4834 991
    protected function internalGetArray(&$data)
4835
    {
4836 991
        if (\is_array($data) === true) {
4837 988
            return $data;
4838
        }
4839
4840 49
        if (!$data) {
4841 6
            return [];
4842
        }
4843
4844 48
        if (\is_object($data) === true) {
4845 43
            if ($data instanceof \ArrayObject) {
4846 2
                return $data->getArrayCopy();
4847
            }
4848
4849 42
            if ($data instanceof \Generator) {
4850
                return static::createFromGeneratorImmutable($data)->getArray();
4851
            }
4852
4853 42
            if ($data instanceof \Traversable) {
4854
                return static::createFromObject($data)->getArray();
4855
            }
4856
4857 42
            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...
4858
                return (array) $data->jsonSerialize();
4859
            }
4860
4861 42
            if (\method_exists($data, '__toArray')) {
4862
                return (array) $data->__toArray();
4863
            }
4864
4865 42
            if (\method_exists($data, '__toString')) {
4866
                return [(string) $data];
4867
            }
4868
        }
4869
4870 47
        if (\is_callable($data)) {
4871 40
            $this->generator = new ArrayyRewindableGenerator($data);
4872
4873 40
            return [];
4874
        }
4875
4876 9
        if (\is_scalar($data)) {
4877 7
            return [$data];
4878
        }
4879
4880 2
        return null;
4881
    }
4882
4883
    /**
4884
     * Internal mechanics of remove method.
4885
     *
4886
     * @param mixed $key
4887
     *
4888
     * @return bool
4889
     */
4890 18
    protected function internalRemove($key): bool
4891
    {
4892 18
        $this->generatorToArray();
4893
4894
        if (
4895 18
            $this->pathSeparator
4896
            &&
4897 18
            (string) $key === $key
4898
            &&
4899 18
            \strpos($key, $this->pathSeparator) !== false
4900
        ) {
4901
            $path = \explode($this->pathSeparator, (string) $key);
4902
4903
            if ($path !== false) {
4904
                // crawl though the keys
4905
                while (\count($path, \COUNT_NORMAL) > 1) {
4906
                    $key = \array_shift($path);
4907
4908
                    if (!$this->has($key)) {
4909
                        return false;
4910
                    }
4911
4912
                    $this->array = &$this->array[$key];
4913
                }
4914
4915
                $key = \array_shift($path);
4916
            }
4917
        }
4918
4919 18
        unset($this->array[$key]);
4920
4921 18
        return true;
4922
    }
4923
4924
    /**
4925
     * Internal mechanic of set method.
4926
     *
4927
     * @param int|string|null $key
4928
     * @param mixed           $value
4929
     * @param bool            $checkProperties
4930
     *
4931
     * @return bool
4932
     */
4933 865
    protected function internalSet($key, $value, $checkProperties = true): bool
4934
    {
4935
        if (
4936 865
            $checkProperties === true
4937
            &&
4938 865
            $this->properties !== []
4939
        ) {
4940 13
            if (isset($this->properties[$key]) === false) {
4941
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4942
            }
4943
4944 13
            $this->properties[$key]->checkType($value);
4945
        }
4946
4947 864
        if ($key === null) {
4948
            return false;
4949
        }
4950
4951 864
        $this->generatorToArray();
4952
4953 864
        $array = &$this->array;
4954
4955
        if (
4956 864
            $this->pathSeparator
4957
            &&
4958 864
            (string) $key === $key
4959
            &&
4960 864
            \strpos($key, $this->pathSeparator) !== false
4961
        ) {
4962 3
            $path = \explode($this->pathSeparator, (string) $key);
4963
4964 3
            if ($path !== false) {
4965
                // crawl through the keys
4966 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4967 3
                    $key = \array_shift($path);
4968
4969 3
                    $array = &$array[$key];
4970
                }
4971
4972 3
                $key = \array_shift($path);
4973
            }
4974
        }
4975
4976 864
        $array[$key] = $value;
4977
4978 864
        return true;
4979
    }
4980
4981
    /**
4982
     * Convert a object into an array.
4983
     *
4984
     * @param object $object
4985
     *
4986
     * @return mixed
4987
     */
4988 5
    protected static function objectToArray($object)
4989
    {
4990 5
        if (!\is_object($object)) {
4991 4
            return $object;
4992
        }
4993
4994 5
        if (\is_object($object)) {
4995 5
            $object = \get_object_vars($object);
4996
        }
4997
4998 5
        return \array_map(['static', 'objectToArray'], $object);
4999
    }
5000
5001
    /**
5002
     * sorting keys
5003
     *
5004
     * @param array      $elements
5005
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5006
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5007
     *                              <strong>SORT_NATURAL</strong></p>
5008
     *
5009
     * @return static
5010
     *                <p>(Mutable) Return this Arrayy object.</p>
5011
     */
5012 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5013
    {
5014 18
        $direction = $this->getDirection($direction);
5015
5016
        switch ($direction) {
5017 18
            case 'desc':
5018 18
            case \SORT_DESC:
5019 6
                \krsort($elements, $strategy);
5020
5021 6
                break;
5022 13
            case 'asc':
5023 13
            case \SORT_ASC:
5024
            default:
5025 13
                \ksort($elements, $strategy);
5026
        }
5027
5028 18
        return $this;
5029
    }
5030
5031
    /**
5032
     * @param array      $elements  <p>Warning: used as reference</p>
5033
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5034
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5035
     *                              <strong>SORT_NATURAL</strong></p>
5036
     * @param bool       $keepKeys
5037
     *
5038
     * @return static
5039
     *                <p>(Mutable) Return this Arrayy object.</p>
5040
     */
5041 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5042
    {
5043 20
        $direction = $this->getDirection($direction);
5044
5045 20
        if (!$strategy) {
5046 20
            $strategy = \SORT_REGULAR;
5047
        }
5048
5049
        switch ($direction) {
5050 20
            case 'desc':
5051 20
            case \SORT_DESC:
5052 9
                if ($keepKeys) {
5053 5
                    \arsort($elements, $strategy);
5054
                } else {
5055 4
                    \rsort($elements, $strategy);
5056
                }
5057
5058 9
                break;
5059 11
            case 'asc':
5060 11
            case \SORT_ASC:
5061
            default:
5062 11
                if ($keepKeys) {
5063 4
                    \asort($elements, $strategy);
5064
                } else {
5065 7
                    \sort($elements, $strategy);
5066
                }
5067
        }
5068
5069 20
        return $this;
5070
    }
5071
5072
    /**
5073
     * @return bool
5074
     *
5075
     * @noinspection ReturnTypeCanBeDeclaredInspection
5076
     */
5077 909
    private function generatorToArray()
5078
    {
5079 909
        if ($this->generator) {
5080 2
            $this->array = $this->getArray();
5081 2
            $this->generator = null;
5082
5083 2
            return true;
5084
        }
5085
5086 909
        return false;
5087
    }
5088
5089
    /**
5090
     * @return Property[]
5091
     *
5092
     * @noinspection ReturnTypeCanBeDeclaredInspection
5093
     */
5094 15
    private function getPropertiesFromPhpDoc()
5095
    {
5096 15
        static $PROPERTY_CACHE = [];
5097 15
        $cacheKey = 'Class::' . static::class;
5098
5099 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5100 14
            return $PROPERTY_CACHE[$cacheKey];
5101
        }
5102
5103
        // init
5104 2
        $properties = [];
5105
5106 2
        $reflector = new \ReflectionClass($this);
5107 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5108 2
        $docComment = $reflector->getDocComment();
5109 2
        if ($docComment) {
5110 2
            $docblock = $factory->create($docComment);
5111
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5112 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5113 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
5114
            }
5115
        }
5116
5117 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5118
    }
5119
}
5120