Completed
Push — master ( b1da28...d63431 )
by Lars
02:17
created

Arrayy::searchIndex()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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