Completed
Push — master ( 189322...ce8c86 )
by Lars
02:08
created

Arrayy::generatorToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 2
rs 9.9
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 905
    public function __construct(
80
        $data = [],
81
        string $iteratorClass = ArrayyIterator::class,
82
        bool $checkForMissingPropertiesInConstructor = true
83
    ) {
84 905
        $data = $this->fallbackForArray($data);
85
86
        // used only for serialize + unserialize, all other methods are overwritten
87 903
        parent::__construct([], 0, $iteratorClass);
88
89 903
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
90
                                                  &&
91 903
                                                  $checkForMissingPropertiesInConstructor === true;
92
93
        if (
94 903
            $this->checkPropertyTypes === true
95
            ||
96 903
            $checkForMissingPropertiesInConstructor === true
97
        ) {
98 15
            $this->properties = $this->getPropertiesFromPhpDoc();
99
        }
100
101
        if (
102 903
            $this->checkPropertiesMismatchInConstructor === true
103
            &&
104 903
            \count($data) !== 0
105
            &&
106 903
            \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 902
        foreach ($data as $key => &$value) {
112 788
            $this->internalSet(
113 788
                $key,
114 788
                $value,
115 788
                $checkForMissingPropertiesInConstructor
116
            );
117
        }
118
119 898
        $this->setIteratorClass($iteratorClass);
120 898
    }
121
122
    /**
123
     * Call object as function.
124
     *
125
     * @param mixed $key
126
     *
127
     * @return mixed
128
     */
129 1
    public function __invoke($key = null)
130
    {
131 1
        if ($key !== null) {
132 1
            $this->generatorToArray();
133
134 1
            return $this->array[$key] ?? false;
135
        }
136
137
        return $this->getArray();
138
    }
139
140
    /**
141
     * Whether or not an element exists by key.
142
     *
143
     * @param mixed $key
144
     *
145
     * @return bool
146
     *              <p>True is the key/index exists, otherwise false.</p>
147
     */
148
    public function __isset($key): bool
149
    {
150
        return $this->offsetExists($key);
151
    }
152
153
    /**
154
     * Assigns a value to the specified element.
155
     *
156
     * @param mixed $key
157
     * @param mixed $value
158
     */
159 2
    public function __set($key, $value)
160
    {
161 2
        $this->internalSet($key, $value);
162 2
    }
163
164
    /**
165
     * magic to string
166
     *
167
     * @return string
168
     */
169 15
    public function __toString(): string
170
    {
171 15
        return $this->toString();
172
    }
173
174
    /**
175
     * Unset element by key.
176
     *
177
     * @param mixed $key
178
     */
179
    public function __unset($key)
180
    {
181
        $this->internalRemove($key);
182
    }
183
184
    /**
185
     * Get a value by key.
186
     *
187
     * @param mixed $key
188
     *
189
     * @return mixed
190
     *               <p>Get a Value from the current array.</p>
191
     */
192 4
    public function &__get($key)
193
    {
194 4
        $return = $this->get($key);
195
196 4
        if (\is_array($return)) {
197
            return static::create($return, $this->iteratorClass, false);
198
        }
199
200 4
        return $return;
201
    }
202
203
    /**
204
     * alias: for "Arrayy->append()"
205
     *
206
     * @param mixed $value
207
     *
208
     * @return static
209
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
210
     *
211
     * @see Arrayy::append()
212
     */
213 3
    public function add($value): self
214
    {
215 3
        return $this->append($value);
216
    }
217
218
    /**
219
     * Append a (key) + value to the current array.
220
     *
221
     * @param mixed $value
222
     * @param mixed $key
223
     *
224
     * @return static
225
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
226
     */
227 10
    public function append($value, $key = null): self
228
    {
229 10
        $this->generatorToArray();
230
231 10
        if ($key !== null) {
232
            if (
233
                isset($this->array[$key])
234
                &&
235
                \is_array($this->array[$key])
236
            ) {
237
                $this->array[$key][] = $value;
238
            } else {
239
                $this->array[$key] = $value;
240
            }
241
        } else {
242 10
            $this->array[] = $value;
243
        }
244
245 10
        return $this;
246
    }
247
248
    /**
249
     * Sort the entries by value.
250
     *
251
     * @param int $sort_flags [optional] <p>
252
     *                        You may modify the behavior of the sort using the optional
253
     *                        parameter sort_flags, for details
254
     *                        see sort.
255
     *                        </p>
256
     *
257
     * @return static
258
     *                <p>(Mutable) Return this Arrayy object.</p>
259
     */
260 4
    public function asort(int $sort_flags = 0): self
261
    {
262 4
        $this->generatorToArray();
263
264 4
        \asort($this->array, $sort_flags);
265
266 4
        return $this;
267
    }
268
269
    /**
270
     * Counts all elements in an array, or something in an object.
271
     *
272
     * <p>
273
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
274
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
275
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
276
     * implemented and used in PHP.
277
     * </p>
278
     *
279
     * @see http://php.net/manual/en/function.count.php
280
     *
281
     * @param int $mode [optional] If the optional mode parameter is set to
282
     *                  COUNT_RECURSIVE (or 1), count
283
     *                  will recursively count the array. This is particularly useful for
284
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
285
     *
286
     * @return int
287
     *             <p>
288
     *             The number of elements in var, which is
289
     *             typically an array, since anything else will have one
290
     *             element.
291
     *             </p>
292
     *             <p>
293
     *             If var is not an array or an object with
294
     *             implemented Countable interface,
295
     *             1 will be returned.
296
     *             There is one exception, if var is &null;,
297
     *             0 will be returned.
298
     *             </p>
299
     *             <p>
300
     *             Caution: count may return 0 for a variable that isn't set,
301
     *             but it may also return 0 for a variable that has been initialized with an
302
     *             empty array. Use isset to test if a variable is set.
303
     *             </p>
304
     */
305 38
    public function count(int $mode = \COUNT_NORMAL): int
306
    {
307 38
        return \count($this->getArray(), $mode);
308
    }
309
310
    /**
311
     * Exchange the array for another one.
312
     *
313
     * @param array|static $data
314
     *
315
     * @return array
316
     */
317 1
    public function exchangeArray($data): array
318
    {
319 1
        $this->array = $this->fallbackForArray($data);
320
321 1
        return $this->array;
322
    }
323
324
    /**
325
     * Creates a copy of the ArrayyObject.
326
     *
327
     * @return array
328
     */
329 1
    public function getArrayCopy(): array
330
    {
331 1
        $this->generatorToArray();
332
333 1
        return $this->array;
334
    }
335
336
    /**
337
     * Returns a new iterator, thus implementing the \Iterator interface.
338
     *
339
     * @return \Iterator
340
     *                        <p>An iterator for the values in the array.</p>
341
     */
342 13
    public function getIterator(): \Iterator
343
    {
344 13
        if ($this->generator instanceof ArrayyRewindableGenerator) {
345 1
            return $this->generator;
346
        }
347
348 12
        $iterator = $this->getIteratorClass();
349
350 12
        return new $iterator($this->getArray(), 0, static::class);
351
    }
352
353
    /**
354
     * Gets the iterator classname for the ArrayObject.
355
     *
356
     * @return string
357
     */
358 12
    public function getIteratorClass(): string
359
    {
360 12
        return $this->iteratorClass;
361
    }
362
363
    /**
364
     * Sort the entries by key
365
     *
366
     * @param int $sort_flags [optional] <p>
367
     *                        You may modify the behavior of the sort using the optional
368
     *                        parameter sort_flags, for details
369
     *                        see sort.
370
     *                        </p>
371
     *
372
     * @return static
373
     *                <p>(Mutable) Return this Arrayy object.</p>
374
     */
375 4
    public function ksort(int $sort_flags = 0): self
376
    {
377 4
        $this->generatorToArray();
378
379 4
        \ksort($this->array, $sort_flags);
380
381 4
        return $this;
382
    }
383
384
    /**
385
     * Sort an array using a case insensitive "natural order" algorithm
386
     *
387
     * @return static
388
     *                <p>(Mutable) Return this Arrayy object.</p>
389
     */
390
    public function natcasesort(): self
391
    {
392
        $this->generatorToArray();
393
394
        \natcasesort($this->array);
395
396
        return $this;
397
    }
398
399
    /**
400
     * Sort entries using a "natural order" algorithm
401
     *
402
     * @return static
403
     *                <p>(Mutable) Return this Arrayy object.</p>
404
     */
405 1
    public function natsort(): self
406
    {
407 1
        $this->generatorToArray();
408
409 1
        \natsort($this->array);
410
411 1
        return $this;
412
    }
413
414
    /**
415
     * Whether or not an offset exists.
416
     *
417
     * @param bool|float|int|string $offset
418
     *
419
     * @return bool
420
     */
421 45
    public function offsetExists($offset): bool
422
    {
423 45
        $this->generatorToArray();
424
425 45
        if ($this->isEmpty()) {
426 4
            return false;
427
        }
428
429
        // php cast "bool"-index into "int"-index
430 41
        if ((bool) $offset === $offset) {
431 1
            $offset = (int) $offset;
432
        }
433
434 41
        $tmpReturn = \array_key_exists($offset, $this->array);
435
436
        if (
437 41
            $tmpReturn === true
438
            ||
439
            (
440 16
                $tmpReturn === false
441
                &&
442 41
                \strpos((string) $offset, $this->pathSeparator) === false
443
            )
444
        ) {
445 39
            return $tmpReturn;
446
        }
447
448 3
        $offsetExists = false;
449
450 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
451 3
            $offsetExists = false;
452 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
453 3
            $lastOffset = \array_pop($explodedPath);
454 3
            $containerPath = \implode($this->pathSeparator, $explodedPath);
455
456 3
            $this->callAtPath(
457 3
                $containerPath,
458
                static function ($container) use ($lastOffset, &$offsetExists) {
459 3
                    $offsetExists = \array_key_exists($lastOffset, $container);
460 3
                }
461
            );
462
        }
463
464 3
        return $offsetExists;
465
    }
466
467
    /**
468
     * Returns the value at specified offset.
469
     *
470
     * @param float|int|string $offset
471
     *
472
     * @return mixed
473
     *               <p>Will return null if the offset did not exists.</p>
474
     */
475 31
    public function offsetGet($offset)
476
    {
477 31
        return $this->offsetExists($offset) ? $this->get($offset) : null;
478
    }
479
480
    /**
481
     * Assigns a value to the specified offset.
482
     *
483
     * @param int|string|null $offset
484
     * @param mixed           $value
485
     */
486 19
    public function offsetSet($offset, $value)
487
    {
488 19
        $this->generatorToArray();
489
490 19
        if ($offset === null) {
491 4
            $this->array[] = $value;
492
        } else {
493 15
            $this->internalSet($offset, $value);
494
        }
495 19
    }
496
497
    /**
498
     * Unset an offset.
499
     *
500
     * @param float|int|string $offset
501
     */
502 7
    public function offsetUnset($offset)
503
    {
504 7
        $this->generatorToArray();
505
506 7
        if ($this->isEmpty()) {
507 1
            return;
508
        }
509
510 6
        if (\array_key_exists($offset, $this->array)) {
511 4
            unset($this->array[$offset]);
512
513 4
            return;
514
        }
515
516 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
517 2
            $path = \explode($this->pathSeparator, (string) $offset);
518 2
            $pathToUnset = \array_pop($path);
519
520 2
            $this->callAtPath(
521 2
                \implode($this->pathSeparator, $path),
522
                static function (&$offset) use ($pathToUnset) {
523 2
                    unset($offset[$pathToUnset]);
524 2
                }
525
            );
526
        }
527 3
    }
528
529
    /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
530
531
    /**
532
     * Serialize the current "Arrayy"-object.
533
     *
534
     * @return string
535
     */
536 2
    public function serialize(): string
537
    {
538 2
        $this->generatorToArray();
539
540 2
        return parent::serialize();
541
    }
542
543
    /**
544
     * Sets the iterator classname for the current "Arrayy"-object.
545
     *
546
     * @param string $class
547
     *
548
     * @throws \InvalidArgumentException
549
     */
550 898
    public function setIteratorClass($class)
551
    {
552 898
        if (\class_exists($class)) {
553 898
            $this->iteratorClass = $class;
554
555 898
            return;
556
        }
557
558
        if (\strpos($class, '\\') === 0) {
559
            $class = '\\' . $class;
560
            if (\class_exists($class)) {
561
                $this->iteratorClass = $class;
562
563
                return;
564
            }
565
        }
566
567
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
568
    }
569
570
    /**
571
     * Sort the entries with a user-defined comparison function and maintain key association.
572
     *
573
     * @param \callable $function
574
     *
575
     * @throws \InvalidArgumentException
576
     *
577
     * @return static
578
     *                <p>(Mutable) Return this Arrayy object.</p>
579
     */
580 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...
581
    {
582
        if (!\is_callable($function)) {
583
            throw new \InvalidArgumentException(
584
                'Passed function must be callable'
585
            );
586
        }
587
588
        $this->generatorToArray();
589
590
        \uasort($this->array, $function);
591
592
        return $this;
593
    }
594
595
    /**
596
     * Sort the entries by keys using a user-defined comparison function.
597
     *
598
     * @param \callable $function
599
     *
600
     * @throws \InvalidArgumentException
601
     *
602
     * @return static
603
     *                <p>(Mutable) Return this Arrayy object.</p>
604
     */
605 5
    public function uksort($function): self
606
    {
607 5
        return $this->customSortKeys($function);
608
    }
609
610
    /**
611
     * Unserialize an string and return the instance of the "Arrayy"-class.
612
     *
613
     * @param string $string
614
     *
615
     * @return static
616
     */
617 2
    public function unserialize($string): self
618
    {
619 2
        parent::unserialize($string);
620
621 2
        return $this;
622
    }
623
624
    /**
625
     * Append a (key) + values to the current array.
626
     *
627
     * @param array $values
628
     * @param mixed $key
629
     *
630
     * @return static
631
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
632
     */
633 1
    public function appendArrayValues(array $values, $key = null): self
634
    {
635 1
        $this->generatorToArray();
636
637 1
        if ($key !== null) {
638
            if (
639 1
                isset($this->array[$key])
640
                &&
641 1
                \is_array($this->array[$key])
642
            ) {
643 1
                foreach ($values as $value) {
644 1
                    $this->array[$key][] = $value;
645
                }
646
            } else {
647
                foreach ($values as $value) {
648 1
                    $this->array[$key] = $value;
649
                }
650
            }
651
        } else {
652
            foreach ($values as $value) {
653
                $this->array[] = $value;
654
            }
655
        }
656
657 1
        return $this;
658
    }
659
660
    /**
661
     * Add a suffix to each key.
662
     *
663
     * @param mixed $prefix
664
     *
665
     * @return static
666
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
667
     */
668 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...
669
    {
670
        // init
671 10
        $result = [];
672
673 10
        foreach ($this->getGenerator() as $key => $item) {
674 9
            if ($item instanceof self) {
675
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
676 9
            } elseif (\is_array($item)) {
677
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
678
                    ->appendToEachKey($prefix)
679
                    ->toArray();
680
            } else {
681 9
                $result[$prefix . $key] = $item;
682
            }
683
        }
684
685 10
        return self::create($result, $this->iteratorClass, false);
686
    }
687
688
    /**
689
     * Add a prefix to each value.
690
     *
691
     * @param mixed $prefix
692
     *
693
     * @return static
694
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
695
     */
696 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...
697
    {
698
        // init
699 10
        $result = [];
700
701 10
        foreach ($this->getGenerator() as $key => $item) {
702 9
            if ($item instanceof self) {
703
                $result[$key] = $item->appendToEachValue($prefix);
704 9
            } elseif (\is_array($item)) {
705
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
706 9
            } elseif (\is_object($item)) {
707 1
                $result[$key] = $item;
708
            } else {
709 9
                $result[$key] = $prefix . $item;
710
            }
711
        }
712
713 10
        return self::create($result, $this->iteratorClass, false);
714
    }
715
716
    /**
717
     * Sort an array in reverse order and maintain index association.
718
     *
719
     * @return static
720
     *                <p>(Mutable) Return this Arrayy object.</p>
721
     */
722 4
    public function arsort(): self
723
    {
724 4
        $this->generatorToArray();
725
726 4
        \arsort($this->array);
727
728 4
        return $this;
729
    }
730
731
    /**
732
     * Iterate over the current array and execute a callback for each loop.
733
     *
734
     * @param \Closure $closure
735
     *
736
     * @return static
737
     *                <p>(Immutable)</p>
738
     */
739 2
    public function at(\Closure $closure): self
740
    {
741 2
        $arrayy = clone $this;
742
743 2
        foreach ($arrayy->getGenerator() as $key => $value) {
744 2
            $closure($value, $key);
745
        }
746
747 2
        return static::create(
748 2
            $arrayy->toArray(),
749 2
            $this->iteratorClass,
750 2
            false
751
        );
752
    }
753
754
    /**
755
     * Returns the average value of the current array.
756
     *
757
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
758
     *
759
     * @return float|int
760
     *                   <p>The average value.</p>
761
     */
762 10
    public function average($decimals = 0)
763
    {
764 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
765
766 10
        if (!$count) {
767 2
            return 0;
768
        }
769
770 8
        if (!\is_int($decimals)) {
771 3
            $decimals = 0;
772
        }
773
774 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
775
    }
776
777
    /**
778
     * Changes all keys in an array.
779
     *
780
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
781
     *                  or <strong>CASE_LOWER</strong> (default)</p>
782
     *
783
     * @return static
784
     *                <p>(Immutable)</p>
785
     */
786 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
787
    {
788
        if (
789 1
            $case !== \CASE_LOWER
790
            &&
791 1
            $case !== \CASE_UPPER
792
        ) {
793
            $case = \CASE_LOWER;
794
        }
795
796 1
        $return = [];
797 1
        foreach ($this->getGenerator() as $key => $value) {
798 1
            if ($case === \CASE_LOWER) {
799
                /** @noinspection PhpComposerExtensionStubsInspection */
800 1
                $key = \mb_strtolower((string) $key);
801
            } else {
802
                /** @noinspection PhpComposerExtensionStubsInspection */
803 1
                $key = \mb_strtoupper((string) $key);
804
            }
805
806 1
            $return[$key] = $value;
807
        }
808
809 1
        return static::create(
810 1
            $return,
811 1
            $this->iteratorClass,
812 1
            false
813
        );
814
    }
815
816
    /**
817
     * Change the path separator of the array wrapper.
818
     *
819
     * By default, the separator is: "."
820
     *
821
     * @param string $separator <p>Separator to set.</p>
822
     *
823
     * @return static
824
     *                <p>Mutable</p>
825
     */
826 8
    public function changeSeparator($separator): self
827
    {
828 8
        $this->pathSeparator = $separator;
829
830 8
        return $this;
831
    }
832
833
    /**
834
     * Create a chunked version of the current array.
835
     *
836
     * @param int  $size         <p>Size of each chunk.</p>
837
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
838
     *
839
     * @return static
840
     *                <p>(Immutable) A new array of chunks from the original array.</p>
841
     */
842 4
    public function chunk($size, $preserveKeys = false): self
843
    {
844 4
        return static::create(
845 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
846 4
            $this->iteratorClass,
847 4
            false
848
        );
849
    }
850
851
    /**
852
     * Clean all falsy values from the current array.
853
     *
854
     * @return static
855
     *                <p>(Immutable)</p>
856
     */
857 8
    public function clean(): self
858
    {
859 8
        return $this->filter(
860
            static function ($value) {
861 7
                return (bool) $value;
862 8
            }
863
        );
864
    }
865
866
    /**
867
     * WARNING!!! -> Clear the current array.
868
     *
869
     * @return static
870
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
871
     */
872 4
    public function clear(): self
873
    {
874 4
        $this->array = [];
875 4
        $this->generator = null;
876
877 4
        return $this;
878
    }
879
880
    /**
881
     * Check if an item is in the current array.
882
     *
883
     * @param float|int|string $value
884
     * @param bool             $recursive
885
     * @param bool             $strict
886
     *
887
     * @return bool
888
     */
889 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
890
    {
891 22
        if ($recursive === true) {
892 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
893
        }
894
895 13
        return \in_array($value, $this->getArray(), $strict);
896
    }
897
898
    /**
899
     * Check if an (case-insensitive) string is in the current array.
900
     *
901
     * @param string $value
902
     * @param bool   $recursive
903
     *
904
     * @return bool
905
     */
906 26
    public function containsCaseInsensitive($value, $recursive = false): bool
907
    {
908 26
        if ($recursive === true) {
909
            /** @noinspection PhpComposerExtensionStubsInspection */
910 26
            return $this->in_array_recursive(
911 26
                \mb_strtoupper((string) $value),
912 26
                $this->walk(
913
                    static function (&$val) {
914
                        /** @noinspection PhpComposerExtensionStubsInspection */
915 22
                        $val = \mb_strtoupper((string) $val);
916 26
                    },
917 26
                    true
918 26
                )->getArray(),
919 26
                true
920
            );
921
        }
922
923
        /** @noinspection PhpComposerExtensionStubsInspection */
924 13
        return \in_array(
925 13
            \mb_strtoupper((string) $value),
926 13
            $this->walk(
927
                static function (&$val) {
928
                    /** @noinspection PhpComposerExtensionStubsInspection */
929 11
                    $val = \mb_strtoupper((string) $val);
930 13
                },
931 13
                false
932 13
            )->getArray(),
933 13
            true
934
        );
935
    }
936
937
    /**
938
     * Check if the given key/index exists in the array.
939
     *
940
     * @param float|int|string $key <p>key/index to search for</p>
941
     *
942
     * @return bool
943
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
944
     */
945 4
    public function containsKey($key): bool
946
    {
947 4
        return $this->offsetExists($key);
948
    }
949
950
    /**
951
     * Check if all given needles are present in the array as key/index.
952
     *
953
     * @param array $needles   <p>The keys you are searching for.</p>
954
     * @param bool  $recursive
955
     *
956
     * @return bool
957
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
958
     */
959 2
    public function containsKeys(array $needles, $recursive = false): bool
960
    {
961 2
        if ($recursive === true) {
962 2
            return \count(
963 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
964 2
                       \COUNT_RECURSIVE
965
                   )
966
                   ===
967 2
                   \count(
968 2
                       $needles,
969 2
                       \COUNT_RECURSIVE
970
                   );
971
        }
972
973 1
        return \count(
974 1
                   \array_intersect($needles, $this->keys()->getArray()),
975 1
                   \COUNT_NORMAL
976
               )
977
               ===
978 1
               \count(
979 1
                   $needles,
980 1
                   \COUNT_NORMAL
981
               );
982
    }
983
984
    /**
985
     * Check if all given needles are present in the array as key/index.
986
     *
987
     * @param array $needles <p>The keys you are searching for.</p>
988
     *
989
     * @return bool
990
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
991
     */
992 1
    public function containsKeysRecursive(array $needles): bool
993
    {
994 1
        return $this->containsKeys($needles, true);
995
    }
996
997
    /**
998
     * alias: for "Arrayy->contains()"
999
     *
1000
     * @param float|int|string $value
1001
     *
1002
     * @return bool
1003
     *
1004
     * @see Arrayy::contains()
1005
     */
1006 9
    public function containsValue($value): bool
1007
    {
1008 9
        return $this->contains($value);
1009
    }
1010
1011
    /**
1012
     * alias: for "Arrayy->contains($value, true)"
1013
     *
1014
     * @param float|int|string $value
1015
     *
1016
     * @return bool
1017
     *
1018
     * @see Arrayy::contains()
1019
     */
1020 18
    public function containsValueRecursive($value): bool
1021
    {
1022 18
        return $this->contains($value, true);
1023
    }
1024
1025
    /**
1026
     * Check if all given needles are present in the array.
1027
     *
1028
     * @param array $needles
1029
     *
1030
     * @return bool
1031
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1032
     */
1033 1
    public function containsValues(array $needles): bool
1034
    {
1035 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1036
               ===
1037 1
               \count($needles, \COUNT_NORMAL);
1038
    }
1039
1040
    /**
1041
     * Counts all the values of an array
1042
     *
1043
     * @see http://php.net/manual/en/function.array-count-values.php
1044
     *
1045
     * @return static
1046
     *                <p>
1047
     *                (Immutable)
1048
     *                An associative Arrayy-object of values from input as
1049
     *                keys and their count as value.
1050
     *                </p>
1051
     */
1052 1
    public function countValues(): self
1053
    {
1054 1
        return new static(\array_count_values($this->getArray()));
1055
    }
1056
1057
    /**
1058
     * Creates an Arrayy object.
1059
     *
1060
     * @param mixed  $array
1061
     * @param string $iteratorClass
1062
     * @param bool   $checkForMissingPropertiesInConstructor
1063
     *
1064
     * @return static
1065
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1066
     */
1067 541
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1068
    {
1069 541
        return new static(
1070 541
            $array,
1071 541
            $iteratorClass,
1072 541
            $checkForMissingPropertiesInConstructor
1073
        );
1074
    }
1075
1076
    /**
1077
     * WARNING: Creates an Arrayy object by reference.
1078
     *
1079
     * @param array $array
1080
     *
1081
     * @return static
1082
     *                <p>(Mutable) Return this Arrayy object.</p>
1083
     */
1084 1
    public function createByReference(array &$array = []): self
1085
    {
1086 1
        $array = $this->fallbackForArray($array);
1087
1088 1
        $this->array = &$array;
1089
1090 1
        return $this;
1091
    }
1092
1093
    /**
1094
     * Create an new instance from a callable function which will return an Generator.
1095
     *
1096
     * @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...
1097
     *
1098
     * @return static
1099
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1100
     */
1101 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1102
    {
1103 5
        $arrayy = new static($generatorFunction);
1104
1105 5
        return $arrayy;
1106
    }
1107
1108
    /**
1109
     * Create an new instance filled with a copy of values from a "Generator"-object.
1110
     *
1111
     * @param \Generator $generator
1112
     *
1113
     * @return static
1114
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1115
     */
1116 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1117
    {
1118 4
        return new static(\iterator_to_array($generator, true));
1119
    }
1120
1121
    /**
1122
     * Create an new Arrayy object via JSON.
1123
     *
1124
     * @param string $json
1125
     *
1126
     * @return static
1127
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1128
     */
1129 5
    public static function createFromJson(string $json): self
1130
    {
1131
        /** @noinspection PhpComposerExtensionStubsInspection */
1132 5
        return static::create(\json_decode($json, true));
1133
    }
1134
1135
    /**
1136
     * Create an new instance filled with values from an object that have implemented ArrayAccess.
1137
     *
1138
     * @param \ArrayAccess $object <p>Object that implements ArrayAccess</p>
1139
     *
1140
     * @return static
1141
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1142
     */
1143 4
    public static function createFromObject(\ArrayAccess $object): self
1144
    {
1145
        // init
1146 4
        $array = new static();
1147
1148 4
        if ($object instanceof self) {
1149 4
            $objectArray = $object->getGenerator();
1150
        } else {
1151
            $objectArray = $object;
1152
        }
1153
1154 4
        foreach ($objectArray as $key => $value) {
1155 3
            $array[$key] = $value;
1156
        }
1157
1158 4
        return $array;
1159
    }
1160
1161
    /**
1162
     * Create an new instance filled with values from an object.
1163
     *
1164
     * @param object $object
1165
     *
1166
     * @return static
1167
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1168
     */
1169 5
    public static function createFromObjectVars($object): self
1170
    {
1171 5
        return new static(self::objectToArray($object));
1172
    }
1173
1174
    /**
1175
     * Create an new Arrayy object via string.
1176
     *
1177
     * @param string      $str       <p>The input string.</p>
1178
     * @param string|null $delimiter <p>The boundary string.</p>
1179
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1180
     *                               used.</p>
1181
     *
1182
     * @return static
1183
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1184
     */
1185 8
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1186
    {
1187 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...
1188 1
            \preg_match_all($regEx, $str, $array);
1189
1190 1
            if (!empty($array)) {
1191 1
                $array = $array[0];
1192
            }
1193
        } else {
1194 7
            $array = \explode($delimiter, $str);
1195
        }
1196
1197
        // trim all string in the array
1198 8
        \array_walk(
1199
            $array,
1200
            static function (&$val) {
1201 8
                if (\is_string($val)) {
1202 8
                    $val = \trim($val);
1203
                }
1204 8
            }
1205
        );
1206
1207 8
        return static::create($array);
1208
    }
1209
1210
    /**
1211
     * Create an new instance containing a range of elements.
1212
     *
1213
     * @param mixed $low  <p>First value of the sequence.</p>
1214
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1215
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1219
     */
1220 2
    public static function createWithRange($low, $high, int $step = 1): self
1221
    {
1222 2
        return static::create(\range($low, $high, $step));
1223
    }
1224
1225
    /**
1226
     * Custom sort by index via "uksort".
1227
     *
1228
     * @see http://php.net/manual/en/function.uksort.php
1229
     *
1230
     * @param \callable $function
1231
     *
1232
     * @throws \InvalidArgumentException
1233
     *
1234
     * @return static
1235
     *                <p>(Mutable) Return this Arrayy object.</p>
1236
     */
1237 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...
1238
    {
1239 5
        if (!\is_callable($function)) {
1240
            throw new \InvalidArgumentException(
1241
                'Passed function must be callable'
1242
            );
1243
        }
1244
1245 5
        $this->generatorToArray();
1246
1247 5
        \uksort($this->array, $function);
1248
1249 5
        return $this;
1250
    }
1251
1252
    /**
1253
     * Custom sort by value via "usort".
1254
     *
1255
     * @see http://php.net/manual/en/function.usort.php
1256
     *
1257
     * @param \callable $function
1258
     *
1259
     * @throws \InvalidArgumentException
1260
     *
1261
     * @return static
1262
     *                <p>(Mutable) Return this Arrayy object.</p>
1263
     */
1264 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...
1265
    {
1266 5
        if (!\is_callable($function)) {
1267
            throw new \InvalidArgumentException(
1268
                'Passed function must be callable'
1269
            );
1270
        }
1271
1272 5
        $this->generatorToArray();
1273
1274 5
        \usort($this->array, $function);
1275
1276 5
        return $this;
1277
    }
1278
1279
    /**
1280
     * Return values that are only in the current array.
1281
     *
1282
     * @param array $array
1283
     *
1284
     * @return static
1285
     *                <p>(Immutable)</p>
1286
     */
1287 12
    public function diff(array $array = []): self
1288
    {
1289 12
        return static::create(
1290 12
            \array_diff($this->getArray(), $array),
1291 12
            $this->iteratorClass,
1292 12
            false
1293
        );
1294
    }
1295
1296
    /**
1297
     * Return values that are only in the current multi-dimensional array.
1298
     *
1299
     * @param array      $array
1300
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1301
     *
1302
     * @return static
1303
     *                <p>(Immutable)</p>
1304
     */
1305 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1306
    {
1307
        // init
1308 1
        $result = [];
1309
1310
        if (
1311 1
            $helperVariableForRecursion !== null
1312
            &&
1313 1
            \is_array($helperVariableForRecursion)
1314
        ) {
1315
            $arrayForTheLoop = $helperVariableForRecursion;
1316
        } else {
1317 1
            $arrayForTheLoop = $this->getGenerator();
1318
        }
1319
1320 1
        foreach ($arrayForTheLoop as $key => $value) {
1321 1
            if ($value instanceof self) {
1322
                $value = $value->getArray();
1323
            }
1324
1325 1
            if (\array_key_exists($key, $array)) {
1326 1
                if ($value !== $array[$key]) {
1327 1
                    $result[$key] = $value;
1328
                }
1329
            } else {
1330 1
                $result[$key] = $value;
1331
            }
1332
        }
1333
1334 1
        return static::create(
1335 1
            $result,
1336 1
            $this->iteratorClass,
1337 1
            false
1338
        );
1339
    }
1340
1341
    /**
1342
     * Return values that are only in the new $array.
1343
     *
1344
     * @param array $array
1345
     *
1346
     * @return static
1347
     *                <p>(Immutable)</p>
1348
     */
1349 8
    public function diffReverse(array $array = []): self
1350
    {
1351 8
        return static::create(
1352 8
            \array_diff($array, $this->getArray()),
1353 8
            $this->iteratorClass,
1354 8
            false
1355
        );
1356
    }
1357
1358
    /**
1359
     * Divide an array into two arrays. One with keys and the other with values.
1360
     *
1361
     * @return static
1362
     *                <p>(Immutable)</p>
1363
     */
1364 1
    public function divide(): self
1365
    {
1366 1
        return static::create(
1367
            [
1368 1
                $this->keys(),
1369 1
                $this->values(),
1370
            ],
1371 1
            $this->iteratorClass,
1372 1
            false
1373
        );
1374
    }
1375
1376
    /**
1377
     * Iterate over the current array and modify the array's value.
1378
     *
1379
     * @param \Closure $closure
1380
     *
1381
     * @return static
1382
     *                <p>(Immutable)</p>
1383
     */
1384 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...
1385
    {
1386
        // init
1387 4
        $array = [];
1388
1389 4
        foreach ($this->getGenerator() as $key => $value) {
1390 4
            $array[$key] = $closure($value, $key);
1391
        }
1392
1393 4
        return static::create(
1394 4
            $array,
1395 4
            $this->iteratorClass,
1396 4
            false
1397
        );
1398
    }
1399
1400
    /**
1401
     * Check if a value is in the current array using a closure.
1402
     *
1403
     * @param \Closure $closure
1404
     *
1405
     * @return bool
1406
     *              <p>Returns true if the given value is found, false otherwise.</p>
1407
     */
1408 4
    public function exists(\Closure $closure): bool
1409
    {
1410
        // init
1411 4
        $isExists = false;
1412
1413 4
        foreach ($this->getGenerator() as $key => $value) {
1414 3
            if ($closure($value, $key)) {
1415 1
                $isExists = true;
1416
1417 3
                break;
1418
            }
1419
        }
1420
1421 4
        return $isExists;
1422
    }
1423
1424
    /**
1425
     * Fill the array until "$num" with "$default" values.
1426
     *
1427
     * @param int   $num
1428
     * @param mixed $default
1429
     *
1430
     * @return static
1431
     *                <p>(Immutable)</p>
1432
     */
1433 8
    public function fillWithDefaults(int $num, $default = null): self
1434
    {
1435 8
        if ($num < 0) {
1436 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1437
        }
1438
1439 7
        $this->generatorToArray();
1440
1441 7
        $tmpArray = $this->array;
1442
1443 7
        $count = \count($tmpArray);
1444
1445 7
        while ($count < $num) {
1446 4
            $tmpArray[] = $default;
1447 4
            ++$count;
1448
        }
1449
1450 7
        return static::create(
1451 7
            $tmpArray,
1452 7
            $this->iteratorClass,
1453 7
            false
1454
        );
1455
    }
1456
1457
    /**
1458
     * Find all items in an array that pass the truth test.
1459
     *
1460
     * @param \Closure|null $closure [optional] <p>
1461
     *                               The callback function to use
1462
     *                               </p>
1463
     *                               <p>
1464
     *                               If no callback is supplied, all entries of
1465
     *                               input equal to false (see
1466
     *                               converting to
1467
     *                               boolean) will be removed.
1468
     *                               </p>
1469
     *                               * @param int $flag [optional] <p>
1470
     *                               Flag determining what arguments are sent to <i>callback</i>:
1471
     *                               </p><ul>
1472
     *                               <li>
1473
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1474
     *                               to <i>callback</i> instead of the value</span>
1475
     *                               </li>
1476
     *                               <li>
1477
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1478
     *                               arguments to <i>callback</i> instead of the value</span>
1479
     *                               </li>
1480
     *                               </ul>
1481
     *
1482
     * @return static
1483
     *                <p>(Immutable)</p>
1484
     */
1485 10
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1486
    {
1487 10
        if (!$closure) {
1488 1
            return $this->clean();
1489
        }
1490
1491 10
        return static::create(
1492 10
            \array_filter($this->getArray(), $closure, $flag),
1493 10
            $this->iteratorClass,
1494 10
            false
1495
        );
1496
    }
1497
1498
    /**
1499
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1500
     * property within that.
1501
     *
1502
     * @param string          $property
1503
     * @param string|string[] $value
1504
     * @param string          $comparisonOp
1505
     *                                      <p>
1506
     *                                      'eq' (equals),<br />
1507
     *                                      'gt' (greater),<br />
1508
     *                                      'gte' || 'ge' (greater or equals),<br />
1509
     *                                      'lt' (less),<br />
1510
     *                                      'lte' || 'le' (less or equals),<br />
1511
     *                                      'ne' (not equals),<br />
1512
     *                                      'contains',<br />
1513
     *                                      'notContains',<br />
1514
     *                                      'newer' (via strtotime),<br />
1515
     *                                      'older' (via strtotime),<br />
1516
     *                                      </p>
1517
     *
1518
     * @return static
1519
     *                <p>(Immutable)</p>
1520
     */
1521 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1522
    {
1523 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...
1524 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1525
        }
1526
1527
        $ops = [
1528
            'eq' => static function ($item, $prop, $value) {
1529 1
                return $item[$prop] === $value;
1530 1
            },
1531
            'gt' => static function ($item, $prop, $value) {
1532
                return $item[$prop] > $value;
1533 1
            },
1534
            'ge' => static function ($item, $prop, $value) {
1535
                return $item[$prop] >= $value;
1536 1
            },
1537
            'gte' => static function ($item, $prop, $value) {
1538
                return $item[$prop] >= $value;
1539 1
            },
1540
            'lt' => static function ($item, $prop, $value) {
1541 1
                return $item[$prop] < $value;
1542 1
            },
1543
            'le' => static function ($item, $prop, $value) {
1544
                return $item[$prop] <= $value;
1545 1
            },
1546
            'lte' => static function ($item, $prop, $value) {
1547
                return $item[$prop] <= $value;
1548 1
            },
1549
            'ne' => static function ($item, $prop, $value) {
1550
                return $item[$prop] !== $value;
1551 1
            },
1552
            'contains' => static function ($item, $prop, $value) {
1553 1
                return \in_array($item[$prop], (array) $value, true);
1554 1
            },
1555
            'notContains' => static function ($item, $prop, $value) {
1556
                return !\in_array($item[$prop], (array) $value, true);
1557 1
            },
1558
            'newer' => static function ($item, $prop, $value) {
1559
                return \strtotime($item[$prop]) > \strtotime($value);
1560 1
            },
1561
            'older' => static function ($item, $prop, $value) {
1562
                return \strtotime($item[$prop]) < \strtotime($value);
1563 1
            },
1564
        ];
1565
1566 1
        $result = \array_values(
1567 1
            \array_filter(
1568 1
                $this->getArray(),
1569
                static function ($item) use (
1570 1
                    $property,
1571 1
                    $value,
1572 1
                    $ops,
1573 1
                    $comparisonOp
1574
                ) {
1575 1
                    $item = (array) $item;
1576 1
                    $itemArrayy = new static($item);
1577 1
                    $item[$property] = $itemArrayy->get($property, []);
1578
1579 1
                    return $ops[$comparisonOp]($item, $property, $value);
1580 1
                }
1581
            )
1582
        );
1583
1584 1
        return static::create(
1585 1
            $result,
1586 1
            $this->iteratorClass,
1587 1
            false
1588
        );
1589
    }
1590
1591
    /**
1592
     * Find the first item in an array that passes the truth test,
1593
     *  otherwise return false
1594
     *
1595
     * @param \Closure $closure
1596
     *
1597
     * @return false|mixed
1598
     *                     <p>Return false if we did not find the value.</p>
1599
     */
1600 8
    public function find(\Closure $closure)
1601
    {
1602 8
        foreach ($this->getGenerator() as $key => $value) {
1603 6
            if ($closure($value, $key)) {
1604 6
                return $value;
1605
            }
1606
        }
1607
1608 3
        return false;
1609
    }
1610
1611
    /**
1612
     * find by ...
1613
     *
1614
     * @param string          $property
1615
     * @param string|string[] $value
1616
     * @param string          $comparisonOp
1617
     *
1618
     * @return static
1619
     *                <p>(Immutable)</p>
1620
     */
1621
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1622
    {
1623
        return $this->filterBy($property, $value, $comparisonOp);
1624
    }
1625
1626
    /**
1627
     * Get the first value from the current array.
1628
     *
1629
     * @return mixed
1630
     *               <p>Return null if there wasn't a element.</p>
1631
     */
1632 13
    public function first()
1633
    {
1634 13
        $tmpArray = $this->getArray();
1635
1636 13
        return \array_shift($tmpArray);
1637
    }
1638
1639
    /**
1640
     * Get the first value(s) from the current array.
1641
     *
1642
     * @param int|null $number <p>How many values you will take?</p>
1643
     *
1644
     * @return static
1645
     *                <p>(Immutable)</p>
1646
     */
1647 29
    public function firstsImmutable(int $number = null): self
1648
    {
1649 29
        $arrayTmp = $this->getArray();
1650
1651 29
        if ($number === null) {
1652 7
            $array = (array) \array_shift($arrayTmp);
1653
        } else {
1654 22
            $number = (int) $number;
1655 22
            $array = \array_splice($arrayTmp, 0, $number);
1656
        }
1657
1658 29
        return static::create(
1659 29
            $array,
1660 29
            $this->iteratorClass,
1661 29
            false
1662
        );
1663
    }
1664
1665
    /**
1666
     * Get the first value(s) from the current array.
1667
     *
1668
     * @param int|null $number <p>How many values you will take?</p>
1669
     *
1670
     * @return static
1671
     *                <p>(Mutable)</p>
1672
     */
1673 26
    public function firstsMutable(int $number = null): self
1674
    {
1675 26
        $this->generatorToArray();
1676
1677 26
        if ($number === null) {
1678 11
            $this->array = (array) \array_shift($this->array);
1679
        } else {
1680 15
            $number = (int) $number;
1681 15
            $this->array = \array_splice($this->array, 0, $number);
1682
        }
1683
1684 26
        return $this;
1685
    }
1686
1687
    /**
1688
     * Exchanges all keys with their associated values in an array.
1689
     *
1690
     * @return static
1691
     *                <p>(Immutable)</p>
1692
     */
1693 1
    public function flip(): self
1694
    {
1695 1
        return static::create(
1696 1
            \array_flip($this->getArray()),
1697 1
            $this->iteratorClass,
1698 1
            false
1699
        );
1700
    }
1701
1702
    /**
1703
     * Get a value from an array (optional using dot-notation).
1704
     *
1705
     * @param mixed $key      <p>The key to look for.</p>
1706
     * @param mixed $fallback <p>Value to fallback to.</p>
1707
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1708
     *                        class.</p>
1709
     *
1710
     * @return mixed|static
1711
     */
1712 70
    public function get($key, $fallback = null, array $array = null)
1713
    {
1714 70
        if ($array !== null) {
1715 4
            $usedArray = $array;
1716
        } else {
1717 66
            $this->generatorToArray();
1718
1719 66
            $usedArray = $this->array;
1720
        }
1721
1722 70
        if ($key === null) {
1723 1
            return static::create(
1724 1
                $usedArray,
1725 1
                $this->iteratorClass,
1726 1
                false
1727
            );
1728
        }
1729
1730
        // php cast "bool"-index into "int"-index
1731 70
        if ((bool) $key === $key) {
1732 3
            $key = (int) $key;
1733
        }
1734
1735 70
        if (\array_key_exists($key, $usedArray) === true) {
1736 60
            if (\is_array($usedArray[$key])) {
1737 8
                return static::create(
1738 8
                    $usedArray[$key],
1739 8
                    $this->iteratorClass,
1740 8
                    false
1741
                );
1742
            }
1743
1744 54
            return $usedArray[$key];
1745
        }
1746
1747
        // crawl through array, get key according to object or not
1748 22
        foreach (\explode($this->pathSeparator, (string) $key) as $segment) {
1749 22
            if (!isset($usedArray[$segment])) {
1750 21
                return $fallback instanceof \Closure ? $fallback() : $fallback;
1751
            }
1752
1753 6
            $usedArray = $usedArray[$segment];
1754
        }
1755
1756 6
        if (\is_array($usedArray)) {
1757 1
            return static::create(
1758 1
                $usedArray,
1759 1
                $this->iteratorClass,
1760 1
                false
1761
            );
1762
        }
1763
1764 6
        return $usedArray;
1765
    }
1766
1767
    /**
1768
     * Get the current array from the "Arrayy"-object.
1769
     *
1770
     * @return array
1771
     */
1772 791
    public function getArray(): array
1773
    {
1774
        // init
1775 791
        $array = [];
1776
1777 791
        foreach ($this->getGenerator() as $key => $value) {
1778 688
            if ($value instanceof self) {
1779 1
                $array[$key] = $value->getArray();
1780
            } else {
1781 688
                $array[$key] = $value;
1782
            }
1783
        }
1784
1785 791
        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 816
    public function getGenerator(): \Generator
1816
    {
1817 816
        if ($this->generator instanceof ArrayyRewindableGenerator) {
1818 5
            yield from $this->generator;
1819
        }
1820
1821 816
        yield from $this->array;
1822 803
    }
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 93
    public function isEmpty(): bool
2167
    {
2168 93
        if ($this->generator) {
2169
            return $this->getArray() === [];
2170
        }
2171
2172 93
        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
                    $temp = $array[$keys[$r]];
3552
                    $array[$keys[$r]] = $array[$keys[$i]];
3553
                    $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 905
    protected function fallbackForArray(&$data): array
4132
    {
4133 905
        if (\is_array($data)) {
4134 902
            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
            $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 793
    protected function internalSet($key, $value, $checkProperties = true): bool
4353
    {
4354
        if (
4355 793
            $checkProperties === true
4356
            &&
4357 793
            $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 792
        if ($key === null) {
4367
            return false;
4368
        }
4369
4370 792
        $this->generatorToArray();
4371
4372
        // init
4373 792
        $array = &$this->array;
4374 792
        $path = \explode($this->pathSeparator, (string) $key);
4375
4376
        // Crawl through the keys
4377 792
        while (\count($path, \COUNT_NORMAL) > 1) {
4378 3
            $key = \array_shift($path);
4379
4380 3
            $array = &$array[$key];
4381
        }
4382
4383 792
        $array[\array_shift($path)] = $value;
4384
4385 792
        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 830
    private function generatorToArray(): bool
4483
    {
4484 830
        if ($this->generator) {
4485 1
            $this->array = $this->getArray();
4486 1
            $this->generator = null;
4487
4488 1
            return true;
4489
        }
4490
4491 830
        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