Completed
Push — master ( 70e886...cfba1b )
by Lars
02:04
created

Arrayy::objectToArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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