Completed
Push — master ( ed8812...301019 )
by Lars
01:38
created

Arrayy::findBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
9
/**
10
 * Methods to manage arrays.
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
16
{
17
    /**
18
     * @var array
19
     */
20
    protected $array = [];
21
22
    /**
23
     * @var ArrayyRewindableGenerator|null
24
     */
25
    protected $generator;
26
27
    /**
28
     * @var string
29
     */
30
    protected $iteratorClass = ArrayyIterator::class;
31
32
    /**
33
     * @var string
34
     */
35
    protected $pathSeparator = '.';
36
37
    /**
38
     * @var bool
39
     */
40
    protected $checkPropertyTypes = false;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $checkForMissingPropertiesInConstructor = false;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $checkPropertiesMismatchInConstructor = false;
51
52
    /**
53
     * @var Property[]
54
     */
55
    protected $properties = [];
56
57
    /**
58
     * Initializes
59
     *
60
     * @param mixed  $data                                   <p>
61
     *                                                       Should be an array or a generator, otherwise it will try
62
     *                                                       to convert it into an array.
63
     *                                                       </p>
64
     * @param string $iteratorClass                          optional <p>
65
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
66
     *                                                       need this option.
67
     *                                                       </p>
68
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
69
     *                                                       You need to extend the "Arrayy"-class and you need to set
70
     *                                                       the $checkPropertiesMismatchInConstructor class property
71
     *                                                       to
72
     *                                                       true, otherwise this option didn't not work anyway.
73
     *                                                       </p>
74
     */
75 991
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 991
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 989
        parent::__construct([], 0, $iteratorClass);
84
85 989
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 989
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 989
            $this->checkPropertyTypes === true
91
            ||
92 989
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 16
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 989
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 989
            \count($data) !== 0
101
            &&
102 989
            \count(\array_diff_key($this->properties, $data)) > 0
103
        ) {
104 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($data), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
105
        }
106
107
        /** @noinspection AlterInForeachInspection */
108 988
        foreach ($data as $key => &$value) {
109 860
            $this->internalSet(
110 860
                $key,
111 860
                $value,
112 860
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 984
        $this->setIteratorClass($iteratorClass);
117 984
    }
118
119
    /**
120
     * Call object as function.
121
     *
122
     * @param mixed $key
123
     *
124
     * @return mixed
125
     */
126 1
    public function __invoke($key = null)
127
    {
128 1
        if ($key !== null) {
129 1
            $this->generatorToArray();
130
131 1
            return $this->array[$key] ?? false;
132
        }
133
134
        return $this->getArray();
135
    }
136
137
    /**
138
     * Whether or not an element exists by key.
139
     *
140
     * @param mixed $key
141
     *
142
     * @return bool
143
     *              <p>True is the key/index exists, otherwise false.</p>
144
     */
145
    public function __isset($key): bool
146
    {
147
        return $this->offsetExists($key);
148
    }
149
150
    /**
151
     * Assigns a value to the specified element.
152
     *
153
     * @param mixed $key
154
     * @param mixed $value
155
     */
156 2
    public function __set($key, $value)
157
    {
158 2
        $this->internalSet($key, $value);
159 2
    }
160
161
    /**
162
     * magic to string
163
     *
164
     * @return string
165
     */
166 16
    public function __toString(): string
167
    {
168 16
        return $this->toString();
169
    }
170
171
    /**
172
     * Unset element by key.
173
     *
174
     * @param mixed $key
175
     */
176
    public function __unset($key)
177
    {
178
        $this->internalRemove($key);
179
    }
180
181
    /**
182
     * Get a value by key.
183
     *
184
     * @param mixed $key
185
     *
186
     * @return mixed
187
     *               <p>Get a Value from the current array.</p>
188
     */
189 4
    public function &__get($key)
190
    {
191 4
        $return = $this->get($key);
192
193 4
        if (\is_array($return) === true) {
194
            return static::create($return, $this->iteratorClass, false);
195
        }
196
197 4
        return $return;
198
    }
199
200
    /**
201
     * alias: for "Arrayy->append()"
202
     *
203
     * @param mixed $value
204
     *
205
     * @return static
206
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
207
     *
208
     * @see Arrayy::append()
209
     */
210 3
    public function add($value): self
211
    {
212 3
        return $this->append($value);
213
    }
214
215
    /**
216
     * Append a (key) + value to the current array.
217
     *
218
     * @param mixed $value
219
     * @param mixed $key
220
     *
221
     * @return static
222
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
223
     */
224 12
    public function append($value, $key = null): self
225
    {
226 12
        $this->generatorToArray();
227
228 12
        if ($key !== null) {
229
            if (
230
                isset($this->array[$key])
231
                &&
232
                \is_array($this->array[$key]) === true
233
            ) {
234
                $this->array[$key][] = $value;
235
            } else {
236
                $this->array[$key] = $value;
237
            }
238
        } else {
239 12
            $this->array[] = $value;
240
        }
241
242 12
        return $this;
243
    }
244
245
    /**
246
     * Sort the entries by value.
247
     *
248
     * @param int $sort_flags [optional] <p>
249
     *                        You may modify the behavior of the sort using the optional
250
     *                        parameter sort_flags, for details
251
     *                        see sort.
252
     *                        </p>
253
     *
254
     * @return static
255
     *                <p>(Mutable) Return this Arrayy object.</p>
256
     */
257 4
    public function asort(int $sort_flags = 0): self
258
    {
259 4
        $this->generatorToArray();
260
261 4
        \asort($this->array, $sort_flags);
262
263 4
        return $this;
264
    }
265
266
    /**
267
     * Counts all elements in an array, or something in an object.
268
     *
269
     * <p>
270
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
271
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
272
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
273
     * implemented and used in PHP.
274
     * </p>
275
     *
276
     * @see http://php.net/manual/en/function.count.php
277
     *
278
     * @param int $mode [optional] If the optional mode parameter is set to
279
     *                  COUNT_RECURSIVE (or 1), count
280
     *                  will recursively count the array. This is particularly useful for
281
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
282
     *
283
     * @return int
284
     *             <p>
285
     *             The number of elements in var, which is
286
     *             typically an array, since anything else will have one
287
     *             element.
288
     *             </p>
289
     *             <p>
290
     *             If var is not an array or an object with
291
     *             implemented Countable interface,
292
     *             1 will be returned.
293
     *             There is one exception, if var is &null;,
294
     *             0 will be returned.
295
     *             </p>
296
     *             <p>
297
     *             Caution: count may return 0 for a variable that isn't set,
298
     *             but it may also return 0 for a variable that has been initialized with an
299
     *             empty array. Use isset to test if a variable is set.
300
     *             </p>
301
     */
302 49
    public function count(int $mode = \COUNT_NORMAL): int
303
    {
304 49
        return \count($this->getArray(), $mode);
305
    }
306
307
    /**
308
     * Exchange the array for another one.
309
     *
310
     * @param array|static $data
311
     *
312
     * @return array
313
     */
314 1
    public function exchangeArray($data): array
315
    {
316 1
        $this->array = $this->fallbackForArray($data);
317
318 1
        return $this->array;
319
    }
320
321
    /**
322
     * Creates a copy of the ArrayyObject.
323
     *
324
     * @return array
325
     */
326 3
    public function getArrayCopy(): array
327
    {
328 3
        $this->generatorToArray();
329
330 3
        return $this->array;
331
    }
332
333
    /**
334
     * Returns a new iterator, thus implementing the \Iterator interface.
335
     *
336
     * @return \Iterator
337
     *                   <p>An iterator for the values in the array.</p>
338
     */
339 24
    public function getIterator(): \Iterator
340
    {
341 24
        if ($this->generator instanceof ArrayyRewindableGenerator) {
342 1
            return $this->generator;
343
        }
344
345 23
        $iterator = $this->getIteratorClass();
346
347 23
        if ($iterator === ArrayyIterator::class) {
348 23
            return new $iterator($this->getArray(), 0, static::class);
349
        }
350
351
        return new $iterator($this->getArray());
352
    }
353
354
    /**
355
     * Gets the iterator classname for the ArrayObject.
356
     *
357
     * @return string
358
     */
359 23
    public function getIteratorClass(): string
360
    {
361 23
        return $this->iteratorClass;
362
    }
363
364
    /**
365
     * Sort the entries by key
366
     *
367
     * @param int $sort_flags [optional] <p>
368
     *                        You may modify the behavior of the sort using the optional
369
     *                        parameter sort_flags, for details
370
     *                        see sort.
371
     *                        </p>
372
     *
373
     * @return static
374
     *                <p>(Mutable) Return this Arrayy object.</p>
375
     */
376 4
    public function ksort(int $sort_flags = 0): self
377
    {
378 4
        $this->generatorToArray();
379
380 4
        \ksort($this->array, $sort_flags);
381
382 4
        return $this;
383
    }
384
385
    /**
386
     * Sort an array using a case insensitive "natural order" algorithm
387
     *
388
     * @return static
389
     *                <p>(Mutable) Return this Arrayy object.</p>
390
     */
391
    public function natcasesort(): self
392
    {
393
        $this->generatorToArray();
394
395
        \natcasesort($this->array);
396
397
        return $this;
398
    }
399
400
    /**
401
     * Sort entries using a "natural order" algorithm
402
     *
403
     * @return static
404
     *                <p>(Mutable) Return this Arrayy object.</p>
405
     */
406 1
    public function natsort(): self
407
    {
408 1
        $this->generatorToArray();
409
410 1
        \natsort($this->array);
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Whether or not an offset exists.
417
     *
418
     * @param bool|int|string $offset
419
     *
420
     * @return bool
421
     */
422 61
    public function offsetExists($offset): bool
423
    {
424 61
        $this->generatorToArray();
425
426 61
        if ($this->array === []) {
427 5
            return false;
428
        }
429
430
        // php cast "bool"-index into "int"-index
431 56
        if ((bool) $offset === $offset) {
432 1
            $offset = (int) $offset;
433
        }
434
435 56
        $tmpReturn = $this->keyExists($offset);
436
437
        if (
438 56
            $tmpReturn === true
439
            ||
440
            (
441 21
                $tmpReturn === false
442
                &&
443 56
                \strpos((string) $offset, $this->pathSeparator) === false
444
            )
445
        ) {
446 54
            return $tmpReturn;
447
        }
448
449 3
        $offsetExists = false;
450
451
        if (
452 3
            $this->pathSeparator
453
            &&
454 3
            (string) $offset === $offset
455
            &&
456 3
            \strpos($offset, $this->pathSeparator) !== false
457
        ) {
458 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
459 3
            if ($explodedPath !== false) {
460 3
                $lastOffset = \array_pop($explodedPath);
461 3
                if ($lastOffset !== null) {
462 3
                    $containerPath = \implode($this->pathSeparator, $explodedPath);
463
464 3
                    $this->callAtPath(
465 3
                        $containerPath,
466
                        static function ($container) use ($lastOffset, &$offsetExists) {
467 3
                            $offsetExists = \array_key_exists($lastOffset, $container);
468 3
                        }
469
                    );
470
                }
471
            }
472
        }
473
474 3
        return $offsetExists;
475
    }
476
477
    /**
478
     * Returns the value at specified offset.
479
     *
480
     * @param int|string $offset
481
     *
482
     * @return mixed
483
     *               <p>Will return null if the offset did not exists.</p>
484
     */
485 30
    public function offsetGet($offset)
486
    {
487 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
488
    }
489
490
    /**
491
     * Assigns a value to the specified offset.
492
     *
493
     * @param int|string|null $offset
494
     * @param mixed           $value
495
     */
496 20
    public function offsetSet($offset, $value)
497
    {
498 20
        $this->generatorToArray();
499
500 20
        if ($offset === null) {
501 4
            $this->array[] = $value;
502
        } else {
503 16
            $this->internalSet($offset, $value);
504
        }
505 20
    }
506
507
    /**
508
     * Unset an offset.
509
     *
510
     * @param int|string $offset
511
     */
512 12
    public function offsetUnset($offset)
513
    {
514 12
        $this->generatorToArray();
515
516 12
        if ($this->array === []) {
517 3
            return;
518
        }
519
520 10
        if ($this->keyExists($offset)) {
521 7
            unset($this->array[$offset]);
522
523 7
            return;
524
        }
525
526
        if (
527 5
            $this->pathSeparator
528
            &&
529 5
            (string) $offset === $offset
530
            &&
531 5
            \strpos($offset, $this->pathSeparator) !== false
532
        ) {
533 2
            $path = \explode($this->pathSeparator, (string) $offset);
534
535 2
            if ($path !== false) {
536 2
                $pathToUnset = \array_pop($path);
537
538 2
                $this->callAtPath(
539 2
                    \implode($this->pathSeparator, $path),
540
                    static function (&$offset) use ($pathToUnset) {
541 2
                        unset($offset[$pathToUnset]);
542 2
                    }
543
                );
544
            }
545
        }
546
547 5
        unset($this->array[$offset]);
548 5
    }
549
550
    /**
551
     * Serialize the current "Arrayy"-object.
552
     *
553
     * @return string
554
     */
555 2
    public function serialize(): string
556
    {
557 2
        $this->generatorToArray();
558
559 2
        if (\PHP_VERSION_ID < 70400) {
560 2
            return parent::serialize();
561
        }
562
563
        return \serialize($this);
564
    }
565
566
    /**
567
     * Sets the iterator classname for the current "Arrayy"-object.
568
     *
569
     * @param string $class
570
     *
571
     * @throws \InvalidArgumentException
572
     */
573 984
    public function setIteratorClass($class)
574
    {
575 984
        if (\class_exists($class)) {
576 984
            $this->iteratorClass = $class;
577
578 984
            return;
579
        }
580
581
        if (\strpos($class, '\\') === 0) {
582
            $class = '\\' . $class;
583
            if (\class_exists($class)) {
584
                $this->iteratorClass = $class;
585
586
                return;
587
            }
588
        }
589
590
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
591
    }
592
593
    /**
594
     * Sort the entries with a user-defined comparison function and maintain key association.
595
     *
596
     * @param callable $function
597
     *
598
     * @throws \InvalidArgumentException
599
     *
600
     * @return static
601
     *                <p>(Mutable) Return this Arrayy object.</p>
602
     */
603 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...
604
    {
605
        if (!\is_callable($function)) {
606
            throw new \InvalidArgumentException(
607
                'Passed function must be callable'
608
            );
609
        }
610
611
        $this->generatorToArray();
612
613
        \uasort($this->array, $function);
614
615
        return $this;
616
    }
617
618
    /**
619
     * Sort the entries by keys using a user-defined comparison function.
620
     *
621
     * @param callable $function
622
     *
623
     * @throws \InvalidArgumentException
624
     *
625
     * @return static
626
     *                <p>(Mutable) Return this Arrayy object.</p>
627
     */
628 5
    public function uksort($function): self
629
    {
630 5
        return $this->customSortKeys($function);
631
    }
632
633
    /**
634
     * Unserialize an string and return the instance of the "Arrayy"-class.
635
     *
636
     * @param string $string
637
     *
638
     * @return static
639
     */
640 2
    public function unserialize($string): self
641
    {
642 2
        if (\PHP_VERSION_ID < 70400) {
643 2
            parent::unserialize($string);
644
645 2
            return $this;
646
        }
647
648
        return \unserialize($string, ['allowed_classed' => [__CLASS__]]);
649
    }
650
651
    /**
652
     * Append a (key) + values to the current array.
653
     *
654
     * @param array $values
655
     * @param mixed $key
656
     *
657
     * @return static
658
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
659
     */
660 1
    public function appendArrayValues(array $values, $key = null): self
661
    {
662 1
        $this->generatorToArray();
663
664 1
        if ($key !== null) {
665
            if (
666 1
                isset($this->array[$key])
667
                &&
668 1
                \is_array($this->array[$key]) === true
669
            ) {
670 1
                foreach ($values as $value) {
671 1
                    $this->array[$key][] = $value;
672
                }
673
            } else {
674 1
                foreach ($values as $value) {
675
                    $this->array[$key] = $value;
676
                }
677
            }
678
        } else {
679
            foreach ($values as $value) {
680
                $this->array[] = $value;
681
            }
682
        }
683
684 1
        return $this;
685
    }
686
687
    /**
688
     * Add a suffix to each key.
689
     *
690
     * @param mixed $prefix
691
     *
692
     * @return static
693
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
694
     */
695 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...
696
    {
697
        // init
698 10
        $result = [];
699
700 10
        foreach ($this->getGenerator() as $key => $item) {
701 9
            if ($item instanceof self) {
702
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
703 9
            } elseif (\is_array($item) === true) {
704
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
705
                    ->appendToEachKey($prefix)
706
                    ->toArray();
707
            } else {
708 9
                $result[$prefix . $key] = $item;
709
            }
710
        }
711
712 10
        return self::create($result, $this->iteratorClass, false);
713
    }
714
715
    /**
716
     * Add a prefix to each value.
717
     *
718
     * @param mixed $prefix
719
     *
720
     * @return static
721
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
722
     */
723 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...
724
    {
725
        // init
726 10
        $result = [];
727
728 10
        foreach ($this->getGenerator() as $key => $item) {
729 9
            if ($item instanceof self) {
730
                $result[$key] = $item->appendToEachValue($prefix);
731 9
            } elseif (\is_array($item) === true) {
732
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
733 9
            } elseif (\is_object($item) === true) {
734 1
                $result[$key] = $item;
735
            } else {
736 8
                $result[$key] = $prefix . $item;
737
            }
738
        }
739
740 10
        return self::create($result, $this->iteratorClass, false);
741
    }
742
743
    /**
744
     * Sort an array in reverse order and maintain index association.
745
     *
746
     * @return static
747
     *                <p>(Mutable) Return this Arrayy object.</p>
748
     */
749 10
    public function arsort(): self
750
    {
751 10
        $this->generatorToArray();
752
753 10
        \arsort($this->array);
754
755 10
        return $this;
756
    }
757
758
    /**
759
     * Iterate over the current array and execute a callback for each loop.
760
     *
761
     * @param \Closure $closure
762
     *
763
     * @return static
764
     *                <p>(Immutable)</p>
765
     */
766 2
    public function at(\Closure $closure): self
767
    {
768 2
        $arrayy = clone $this;
769
770 2
        foreach ($arrayy->getGenerator() as $key => $value) {
771 2
            $closure($value, $key);
772
        }
773
774 2
        return static::create(
775 2
            $arrayy->toArray(),
776 2
            $this->iteratorClass,
777 2
            false
778
        );
779
    }
780
781
    /**
782
     * Returns the average value of the current array.
783
     *
784
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
785
     *
786
     * @return float|int
787
     *                   <p>The average value.</p>
788
     */
789 10
    public function average($decimals = 0)
790
    {
791 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
792
793 10
        if (!$count) {
794 2
            return 0;
795
        }
796
797 8
        if ((int) $decimals !== $decimals) {
798 3
            $decimals = 0;
799
        }
800
801 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
802
    }
803
804
    /**
805
     * Changes all keys in an array.
806
     *
807
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
808
     *                  or <strong>CASE_LOWER</strong> (default)</p>
809
     *
810
     * @return static
811
     *                <p>(Immutable)</p>
812
     */
813 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
814
    {
815
        if (
816 1
            $case !== \CASE_LOWER
817
            &&
818 1
            $case !== \CASE_UPPER
819
        ) {
820
            $case = \CASE_LOWER;
821
        }
822
823 1
        $return = [];
824 1
        foreach ($this->getGenerator() as $key => $value) {
825 1
            if ($case === \CASE_LOWER) {
826 1
                $key = \mb_strtolower((string) $key);
827
            } else {
828 1
                $key = \mb_strtoupper((string) $key);
829
            }
830
831 1
            $return[$key] = $value;
832
        }
833
834 1
        return static::create(
835 1
            $return,
836 1
            $this->iteratorClass,
837 1
            false
838
        );
839
    }
840
841
    /**
842
     * Change the path separator of the array wrapper.
843
     *
844
     * By default, the separator is: "."
845
     *
846
     * @param string $separator <p>Separator to set.</p>
847
     *
848
     * @return static
849
     *                <p>Mutable</p>
850
     */
851 10
    public function changeSeparator($separator): self
852
    {
853 10
        $this->pathSeparator = $separator;
854
855 10
        return $this;
856
    }
857
858
    /**
859
     * Create a chunked version of the current array.
860
     *
861
     * @param int  $size         <p>Size of each chunk.</p>
862
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
863
     *
864
     * @return static
865
     *                <p>(Immutable) A new array of chunks from the original array.</p>
866
     */
867 4
    public function chunk($size, $preserveKeys = false): self
868
    {
869 4
        return static::create(
870 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
871 4
            $this->iteratorClass,
872 4
            false
873
        );
874
    }
875
876
    /**
877
     * Clean all falsy values from the current array.
878
     *
879
     * @return static
880
     *                <p>(Immutable)</p>
881
     */
882 8
    public function clean(): self
883
    {
884 8
        return $this->filter(
885
            static function ($value) {
886 7
                return (bool) $value;
887 8
            }
888
        );
889
    }
890
891
    /**
892
     * WARNING!!! -> Clear the current array.
893
     *
894
     * @return static
895
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
896
     */
897 4
    public function clear(): self
898
    {
899 4
        $this->array = [];
900 4
        $this->generator = null;
901
902 4
        return $this;
903
    }
904
905
    /**
906
     * Check if an item is in the current array.
907
     *
908
     * @param float|int|string $value
909
     * @param bool             $recursive
910
     * @param bool             $strict
911
     *
912
     * @return bool
913
     */
914 23
    public function contains($value, bool $recursive = false, bool $strict = true): bool
915
    {
916 23
        if ($recursive === true) {
917 19
            return $this->in_array_recursive($value, $this->getArray(), $strict);
918
        }
919
920 13
        foreach ($this->getGenerator() as $valueFromArray) {
921 10
            if ($strict) {
922 10
                if ($value === $valueFromArray) {
923 10
                    return true;
924
                }
925
            } else {
926
                /** @noinspection NestedPositiveIfStatementsInspection */
927
                if ($value == $valueFromArray) {
928
                    return true;
929
                }
930
            }
931
        }
932
933 6
        return false;
934
    }
935
936
    /**
937
     * Check if an (case-insensitive) string is in the current array.
938
     *
939
     * @param string $value
940
     * @param bool   $recursive
941
     *
942
     * @return bool
943
     */
944 26
    public function containsCaseInsensitive($value, $recursive = false): bool
945
    {
946 26
        if ($recursive === true) {
947 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
948 22
                if (\is_array($valueTmp) === true) {
949 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
950 5
                    if ($return === true) {
951 5
                        return $return;
952
                    }
953 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
954 16
                    return true;
955
                }
956
            }
957
958 10
            return false;
959
        }
960
961 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
962 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
963 8
                return true;
964
            }
965
        }
966
967 5
        return false;
968
    }
969
970
    /**
971
     * Check if the given key/index exists in the array.
972
     *
973
     * @param int|string $key <p>key/index to search for</p>
974
     *
975
     * @return bool
976
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
977
     */
978 4
    public function containsKey($key): bool
979
    {
980 4
        return $this->offsetExists($key);
981
    }
982
983
    /**
984
     * Check if all given needles are present in the array as key/index.
985
     *
986
     * @param array $needles   <p>The keys you are searching for.</p>
987
     * @param bool  $recursive
988
     *
989
     * @return bool
990
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
991
     */
992 2
    public function containsKeys(array $needles, $recursive = false): bool
993
    {
994 2
        if ($recursive === true) {
995
            return
996 2
                \count(
997 2
                    \array_intersect(
998 2
                        $needles,
999 2
                        $this->keys(true)->getArray()
1000
                    ),
1001 2
                    \COUNT_RECURSIVE
1002
                )
1003
                ===
1004 2
                \count(
1005 2
                    $needles,
1006 2
                    \COUNT_RECURSIVE
1007
                );
1008
        }
1009
1010 1
        return \count(
1011 1
            \array_intersect($needles, $this->keys()->getArray()),
1012 1
            \COUNT_NORMAL
1013
        )
1014
               ===
1015 1
               \count(
1016 1
                   $needles,
1017 1
                   \COUNT_NORMAL
1018
               );
1019
    }
1020
1021
    /**
1022
     * Check if all given needles are present in the array as key/index.
1023
     *
1024
     * @param array $needles <p>The keys you are searching for.</p>
1025
     *
1026
     * @return bool
1027
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1028
     */
1029 1
    public function containsKeysRecursive(array $needles): bool
1030
    {
1031 1
        return $this->containsKeys($needles, true);
1032
    }
1033
1034
    /**
1035
     * alias: for "Arrayy->contains()"
1036
     *
1037
     * @param float|int|string $value
1038
     *
1039
     * @return bool
1040
     *
1041
     * @see Arrayy::contains()
1042
     */
1043 9
    public function containsValue($value): bool
1044
    {
1045 9
        return $this->contains($value);
1046
    }
1047
1048
    /**
1049
     * alias: for "Arrayy->contains($value, true)"
1050
     *
1051
     * @param float|int|string $value
1052
     *
1053
     * @return bool
1054
     *
1055
     * @see Arrayy::contains()
1056
     */
1057 18
    public function containsValueRecursive($value): bool
1058
    {
1059 18
        return $this->contains($value, true);
1060
    }
1061
1062
    /**
1063
     * Check if all given needles are present in the array.
1064
     *
1065
     * @param array $needles
1066
     *
1067
     * @return bool
1068
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1069
     */
1070 1
    public function containsValues(array $needles): bool
1071
    {
1072 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1073
               ===
1074 1
               \count($needles, \COUNT_NORMAL);
1075
    }
1076
1077
    /**
1078
     * Counts all the values of an array
1079
     *
1080
     * @see http://php.net/manual/en/function.array-count-values.php
1081
     *
1082
     * @return static
1083
     *                <p>
1084
     *                (Immutable)
1085
     *                An associative Arrayy-object of values from input as
1086
     *                keys and their count as value.
1087
     *                </p>
1088
     */
1089 7
    public function countValues(): self
1090
    {
1091 7
        return new static(\array_count_values($this->getArray()));
1092
    }
1093
1094
    /**
1095
     * Creates an Arrayy object.
1096
     *
1097
     * @param mixed  $array
1098
     * @param string $iteratorClass
1099
     * @param bool   $checkForMissingPropertiesInConstructor
1100
     *
1101
     * @return static
1102
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1103
     */
1104 580
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1105
    {
1106 580
        return new static(
1107 580
            $array,
1108 580
            $iteratorClass,
1109 580
            $checkForMissingPropertiesInConstructor
1110
        );
1111
    }
1112
1113
    /**
1114
     * WARNING: Creates an Arrayy object by reference.
1115
     *
1116
     * @param array $array
1117
     *
1118
     * @return static
1119
     *                <p>(Mutable) Return this Arrayy object.</p>
1120
     */
1121 1
    public function createByReference(array &$array = []): self
1122
    {
1123 1
        $array = $this->fallbackForArray($array);
1124
1125 1
        $this->array = &$array;
1126
1127 1
        return $this;
1128
    }
1129
1130
    /**
1131
     * Create an new instance from a callable function which will return an Generator.
1132
     *
1133
     * @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...
1134
     *
1135
     * @return static
1136
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1137
     */
1138 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1139
    {
1140 5
        return new static($generatorFunction);
1141
    }
1142
1143
    /**
1144
     * Create an new instance filled with a copy of values from a "Generator"-object.
1145
     *
1146
     * @param \Generator $generator
1147
     *
1148
     * @return static
1149
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1150
     */
1151 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1152
    {
1153 4
        return new static(\iterator_to_array($generator, true));
1154
    }
1155
1156
    /**
1157
     * Create an new Arrayy object via JSON.
1158
     *
1159
     * @param string $json
1160
     *
1161
     * @return static
1162
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1163
     */
1164 5
    public static function createFromJson(string $json): self
1165
    {
1166 5
        return static::create(\json_decode($json, true));
1167
    }
1168
1169
    /**
1170
     * Create an new instance filled with values from an object that is iterable.
1171
     *
1172
     * @param \Traversable $object <p>iterable object</p>
1173
     *
1174
     * @return static
1175
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1176
     */
1177 4
    public static function createFromObject(\Traversable $object): self
1178
    {
1179
        // init
1180 4
        $array = new static();
1181
1182 4
        if ($object instanceof self) {
1183 4
            $objectArray = $object->getGenerator();
1184
        } else {
1185
            $objectArray = $object;
1186
        }
1187
1188 4
        foreach ($objectArray as $key => $value) {
1189 3
            $array[$key] = $value;
1190
        }
1191
1192 4
        return $array;
1193
    }
1194
1195
    /**
1196
     * Create an new instance filled with values from an object.
1197
     *
1198
     * @param object $object
1199
     *
1200
     * @return static
1201
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1202
     */
1203 5
    public static function createFromObjectVars($object): self
1204
    {
1205 5
        return new static(self::objectToArray($object));
1206
    }
1207
1208
    /**
1209
     * Create an new Arrayy object via string.
1210
     *
1211
     * @param string      $str       <p>The input string.</p>
1212
     * @param string|null $delimiter <p>The boundary string.</p>
1213
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1214
     *                               used.</p>
1215
     *
1216
     * @return static
1217
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1218
     */
1219 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1220
    {
1221 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1222 1
            \preg_match_all($regEx, $str, $array);
1223
1224 1
            if (!empty($array)) {
1225 1
                $array = $array[0];
1226
            }
1227
        } else {
1228
            /** @noinspection NestedPositiveIfStatementsInspection */
1229 9
            if ($delimiter !== null) {
1230 7
                $array = \explode($delimiter, $str);
1231
            } else {
1232 2
                $array = [$str];
1233
            }
1234
        }
1235
1236
        // trim all string in the array
1237 10
        \array_walk(
1238 10
            $array,
1239
            static function (&$val) {
1240 10
                if ((string) $val === $val) {
1241 10
                    $val = \trim($val);
1242
                }
1243 10
            }
1244
        );
1245
1246 10
        return static::create($array);
1247
    }
1248
1249
    /**
1250
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1251
     *
1252
     * @param \Traversable $traversable
1253
     *
1254
     * @return static
1255
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1256
     */
1257 1
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1258
    {
1259 1
        return new static(\iterator_to_array($traversable, true));
1260
    }
1261
1262
    /**
1263
     * Create an new instance containing a range of elements.
1264
     *
1265
     * @param mixed $low  <p>First value of the sequence.</p>
1266
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1267
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1268
     *
1269
     * @return static
1270
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1271
     */
1272 2
    public static function createWithRange($low, $high, int $step = 1): self
1273
    {
1274 2
        return static::create(\range($low, $high, $step));
1275
    }
1276
1277
    /**
1278
     * Custom sort by index via "uksort".
1279
     *
1280
     * @see http://php.net/manual/en/function.uksort.php
1281
     *
1282
     * @param callable $function
1283
     *
1284
     * @throws \InvalidArgumentException
1285
     *
1286
     * @return static
1287
     *                <p>(Mutable) Return this Arrayy object.</p>
1288
     */
1289 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...
1290
    {
1291 5
        if (\is_callable($function) === false) {
1292
            throw new \InvalidArgumentException(
1293
                'Passed function must be callable'
1294
            );
1295
        }
1296
1297 5
        $this->generatorToArray();
1298
1299 5
        \uksort($this->array, $function);
1300
1301 5
        return $this;
1302
    }
1303
1304
    /**
1305
     * Custom sort by value via "usort".
1306
     *
1307
     * @see http://php.net/manual/en/function.usort.php
1308
     *
1309
     * @param callable $function
1310
     *
1311
     * @throws \InvalidArgumentException
1312
     *
1313
     * @return static
1314
     *                <p>(Mutable) Return this Arrayy object.</p>
1315
     */
1316 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...
1317
    {
1318 5
        if (\is_callable($function) === false) {
1319
            throw new \InvalidArgumentException(
1320
                'Passed function must be callable'
1321
            );
1322
        }
1323
1324 5
        $this->generatorToArray();
1325
1326 5
        \usort($this->array, $function);
1327
1328 5
        return $this;
1329
    }
1330
1331
    /**
1332
     * Delete the given key or keys.
1333
     *
1334
     * @param int|int[]|string|string[] $keyOrKeys
1335
     */
1336 4
    public function delete($keyOrKeys)
1337
    {
1338 4
        $keyOrKeys = (array) $keyOrKeys;
1339
1340 4
        foreach ($keyOrKeys as $key) {
1341 4
            $this->offsetUnset($key);
1342
        }
1343 4
    }
1344
1345
    /**
1346
     * Return values that are only in the current array.
1347
     *
1348
     * @param array $array
1349
     *
1350
     * @return static
1351
     *                <p>(Immutable)</p>
1352
     */
1353 12
    public function diff(array $array = []): self
1354
    {
1355 12
        return static::create(
1356 12
            \array_diff($this->getArray(), $array),
1357 12
            $this->iteratorClass,
1358 12
            false
1359
        );
1360
    }
1361
1362
    /**
1363
     * Return values that are only in the current array.
1364
     *
1365
     * @param array $array
1366
     *
1367
     * @return static
1368
     *                <p>(Immutable)</p>
1369
     */
1370 8
    public function diffKey(array $array = []): self
1371
    {
1372 8
        return static::create(
1373 8
            \array_diff_key($this->getArray(), $array),
1374 8
            $this->iteratorClass,
1375 8
            false
1376
        );
1377
    }
1378
1379
    /**
1380
     * Return values and Keys that are only in the current array.
1381
     *
1382
     * @param array $array
1383
     *
1384
     * @return static
1385
     *                <p>(Immutable)</p>
1386
     */
1387 8
    public function diffKeyAndValue(array $array = []): self
1388
    {
1389 8
        return static::create(
1390 8
            \array_diff_assoc($this->getArray(), $array),
1391 8
            $this->iteratorClass,
1392 8
            false
1393
        );
1394
    }
1395
1396
    /**
1397
     * Return values that are only in the current multi-dimensional array.
1398
     *
1399
     * @param array      $array
1400
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1401
     *
1402
     * @return static
1403
     *                <p>(Immutable)</p>
1404
     */
1405 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1406
    {
1407
        // init
1408 1
        $result = [];
1409
1410
        if (
1411 1
            $helperVariableForRecursion !== null
1412
            &&
1413 1
            \is_array($helperVariableForRecursion) === true
1414
        ) {
1415
            $arrayForTheLoop = $helperVariableForRecursion;
1416
        } else {
1417 1
            $arrayForTheLoop = $this->getGenerator();
1418
        }
1419
1420 1
        foreach ($arrayForTheLoop as $key => $value) {
1421 1
            if ($value instanceof self) {
1422
                $value = $value->getArray();
1423
            }
1424
1425 1
            if (\array_key_exists($key, $array)) {
1426 1
                if ($value !== $array[$key]) {
1427 1
                    $result[$key] = $value;
1428
                }
1429
            } else {
1430 1
                $result[$key] = $value;
1431
            }
1432
        }
1433
1434 1
        return static::create(
1435 1
            $result,
1436 1
            $this->iteratorClass,
1437 1
            false
1438
        );
1439
    }
1440
1441
    /**
1442
     * Return values that are only in the new $array.
1443
     *
1444
     * @param array $array
1445
     *
1446
     * @return static
1447
     *                <p>(Immutable)</p>
1448
     */
1449 8
    public function diffReverse(array $array = []): self
1450
    {
1451 8
        return static::create(
1452 8
            \array_diff($array, $this->getArray()),
1453 8
            $this->iteratorClass,
1454 8
            false
1455
        );
1456
    }
1457
1458
    /**
1459
     * Divide an array into two arrays. One with keys and the other with values.
1460
     *
1461
     * @return static
1462
     *                <p>(Immutable)</p>
1463
     */
1464 1
    public function divide(): self
1465
    {
1466 1
        return static::create(
1467
            [
1468 1
                $this->keys(),
1469 1
                $this->values(),
1470
            ],
1471 1
            $this->iteratorClass,
1472 1
            false
1473
        );
1474
    }
1475
1476
    /**
1477
     * Iterate over the current array and modify the array's value.
1478
     *
1479
     * @param \Closure $closure
1480
     *
1481
     * @return static
1482
     *                <p>(Immutable)</p>
1483
     */
1484 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...
1485
    {
1486
        // init
1487 4
        $array = [];
1488
1489 4
        foreach ($this->getGenerator() as $key => $value) {
1490 4
            $array[$key] = $closure($value, $key);
1491
        }
1492
1493 4
        return static::create(
1494 4
            $array,
1495 4
            $this->iteratorClass,
1496 4
            false
1497
        );
1498
    }
1499
1500
    /**
1501
     * Check if a value is in the current array using a closure.
1502
     *
1503
     * @param \Closure $closure
1504
     *
1505
     * @return bool
1506
     *              <p>Returns true if the given value is found, false otherwise.</p>
1507
     */
1508 4
    public function exists(\Closure $closure): bool
1509
    {
1510
        // init
1511 4
        $isExists = false;
1512
1513 4
        foreach ($this->getGenerator() as $key => $value) {
1514 3
            if ($closure($value, $key)) {
1515 1
                $isExists = true;
1516
1517 1
                break;
1518
            }
1519
        }
1520
1521 4
        return $isExists;
1522
    }
1523
1524
    /**
1525
     * Fill the array until "$num" with "$default" values.
1526
     *
1527
     * @param int   $num
1528
     * @param mixed $default
1529
     *
1530
     * @return static
1531
     *                <p>(Immutable)</p>
1532
     */
1533 8
    public function fillWithDefaults(int $num, $default = null): self
1534
    {
1535 8
        if ($num < 0) {
1536 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1537
        }
1538
1539 7
        $this->generatorToArray();
1540
1541 7
        $tmpArray = $this->array;
1542
1543 7
        $count = \count($tmpArray);
1544
1545 7
        while ($count < $num) {
1546 4
            $tmpArray[] = $default;
1547 4
            ++$count;
1548
        }
1549
1550 7
        return static::create(
1551 7
            $tmpArray,
1552 7
            $this->iteratorClass,
1553 7
            false
1554
        );
1555
    }
1556
1557
    /**
1558
     * Find all items in an array that pass the truth test.
1559
     *
1560
     * @param \Closure|null $closure [optional] <p>
1561
     *                               The callback function to use
1562
     *                               </p>
1563
     *                               <p>
1564
     *                               If no callback is supplied, all entries of
1565
     *                               input equal to false (see
1566
     *                               converting to
1567
     *                               boolean) will be removed.
1568
     *                               </p>
1569
     *                               * @param int $flag [optional] <p>
1570
     *                               Flag determining what arguments are sent to <i>callback</i>:
1571
     *                               </p><ul>
1572
     *                               <li>
1573
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1574
     *                               to <i>callback</i> instead of the value</span>
1575
     *                               </li>
1576
     *                               <li>
1577
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1578
     *                               arguments to <i>callback</i> instead of the value</span>
1579
     *                               </li>
1580
     *                               </ul>
1581
     *
1582
     * @return static
1583
     *                <p>(Immutable)</p>
1584
     */
1585 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1586
    {
1587 11
        if (!$closure) {
1588 1
            return $this->clean();
1589
        }
1590
1591 11
        return static::create(
1592 11
            \array_filter($this->getArray(), $closure, $flag),
1593 11
            $this->iteratorClass,
1594 11
            false
1595
        );
1596
    }
1597
1598
    /**
1599
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1600
     * property within that.
1601
     *
1602
     * @param string          $property
1603
     * @param string|string[] $value
1604
     * @param string          $comparisonOp
1605
     *                                      <p>
1606
     *                                      'eq' (equals),<br />
1607
     *                                      'gt' (greater),<br />
1608
     *                                      'gte' || 'ge' (greater or equals),<br />
1609
     *                                      'lt' (less),<br />
1610
     *                                      'lte' || 'le' (less or equals),<br />
1611
     *                                      'ne' (not equals),<br />
1612
     *                                      'contains',<br />
1613
     *                                      'notContains',<br />
1614
     *                                      'newer' (via strtotime),<br />
1615
     *                                      'older' (via strtotime),<br />
1616
     *                                      </p>
1617
     *
1618
     * @return static
1619
     *                <p>(Immutable)</p>
1620
     */
1621 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1622
    {
1623 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...
1624 1
            $comparisonOp = \is_array($value) === true ? 'contains' : 'eq';
1625
        }
1626
1627
        $ops = [
1628
            'eq' => static function ($item, $prop, $value) {
1629 1
                return $item[$prop] === $value;
1630 1
            },
1631
            'gt' => static function ($item, $prop, $value) {
1632
                return $item[$prop] > $value;
1633 1
            },
1634
            'ge' => static function ($item, $prop, $value) {
1635
                return $item[$prop] >= $value;
1636 1
            },
1637
            'gte' => static function ($item, $prop, $value) {
1638
                return $item[$prop] >= $value;
1639 1
            },
1640
            'lt' => static function ($item, $prop, $value) {
1641 1
                return $item[$prop] < $value;
1642 1
            },
1643
            'le' => static function ($item, $prop, $value) {
1644
                return $item[$prop] <= $value;
1645 1
            },
1646
            'lte' => static function ($item, $prop, $value) {
1647
                return $item[$prop] <= $value;
1648 1
            },
1649
            'ne' => static function ($item, $prop, $value) {
1650
                return $item[$prop] !== $value;
1651 1
            },
1652
            'contains' => static function ($item, $prop, $value) {
1653 1
                return \in_array($item[$prop], (array) $value, true);
1654 1
            },
1655
            'notContains' => static function ($item, $prop, $value) {
1656
                return !\in_array($item[$prop], (array) $value, true);
1657 1
            },
1658
            'newer' => static function ($item, $prop, $value) {
1659
                return \strtotime($item[$prop]) > \strtotime($value);
1660 1
            },
1661
            'older' => static function ($item, $prop, $value) {
1662
                return \strtotime($item[$prop]) < \strtotime($value);
1663 1
            },
1664
        ];
1665
1666 1
        $result = \array_values(
1667 1
            \array_filter(
1668 1
                $this->getArray(),
1669
                static function ($item) use (
1670 1
                    $property,
1671 1
                    $value,
1672 1
                    $ops,
1673 1
                    $comparisonOp
1674
                ) {
1675 1
                    $item = (array) $item;
1676 1
                    $itemArrayy = new static($item);
1677 1
                    $item[$property] = $itemArrayy->get($property, []);
1678
1679 1
                    return $ops[$comparisonOp]($item, $property, $value);
1680 1
                }
1681
            )
1682
        );
1683
1684 1
        return static::create(
1685 1
            $result,
1686 1
            $this->iteratorClass,
1687 1
            false
1688
        );
1689
    }
1690
1691
    /**
1692
     * Find the first item in an array that passes the truth test,
1693
     *  otherwise return false
1694
     *
1695
     * @param \Closure $closure
1696
     *
1697
     * @return false|mixed
1698
     *                     <p>Return false if we did not find the value.</p>
1699
     */
1700 8
    public function find(\Closure $closure)
1701
    {
1702 8
        foreach ($this->getGenerator() as $key => $value) {
1703 6
            if ($closure($value, $key)) {
1704 5
                return $value;
1705
            }
1706
        }
1707
1708 3
        return false;
1709
    }
1710
1711
    /**
1712
     * find by ...
1713
     *
1714
     * @param string          $property
1715
     * @param string|string[] $value
1716
     * @param string          $comparisonOp
1717
     *
1718
     * @return static
1719
     *                <p>(Immutable)</p>
1720
     */
1721 1
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1722
    {
1723 1
        return $this->filterBy($property, $value, $comparisonOp);
1724
    }
1725
1726
    /**
1727
     * Get the first value from the current array.
1728
     *
1729
     * @return mixed
1730
     *               <p>Return null if there wasn't a element.</p>
1731
     */
1732 18
    public function first()
1733
    {
1734 18
        $key_first = $this->firstKey();
1735 18
        if ($key_first === null) {
1736 3
            return null;
1737
        }
1738
1739 15
        return $this->get($key_first);
1740
    }
1741
1742
    /**
1743
     * Get the first key from the current array.
1744
     *
1745
     * @return mixed
1746
     *               <p>Return null if there wasn't a element.</p>
1747
     */
1748 25
    public function firstKey()
1749
    {
1750 25
        $this->generatorToArray();
1751
1752 25
        return \array_key_first($this->array);
1753
    }
1754
1755
    /**
1756
     * Get the first value(s) from the current array.
1757
     * And will return an empty array if there was no first entry.
1758
     *
1759
     * @param int|null $number <p>How many values you will take?</p>
1760
     *
1761
     * @return static
1762
     *                <p>(Immutable)</p>
1763
     */
1764 36 View Code Duplication
    public function firstsImmutable(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1765
    {
1766 36
        $arrayTmp = $this->getArray();
1767
1768 36
        if ($number === null) {
1769 14
            $array = (array) \array_shift($arrayTmp);
1770
        } else {
1771 22
            $number = (int) $number;
1772 22
            $array = \array_splice($arrayTmp, 0, $number);
1773
        }
1774
1775 36
        return static::create(
1776 36
            $array,
1777 36
            $this->iteratorClass,
1778 36
            false
1779
        );
1780
    }
1781
1782
    /**
1783
     * Get the first value(s) from the current array.
1784
     * And will return an empty array if there was no first entry.
1785
     *
1786
     * @param int|null $number <p>How many values you will take?</p>
1787
     *
1788
     * @return static
1789
     *                <p>(Immutable)</p>
1790
     */
1791 3 View Code Duplication
    public function firstsKeys(int $number = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1792
    {
1793 3
        $arrayTmp = $this->keys()->getArray();
1794
1795 3
        if ($number === null) {
1796
            $array = (array) \array_shift($arrayTmp);
1797
        } else {
1798 3
            $number = (int) $number;
1799 3
            $array = \array_splice($arrayTmp, 0, $number);
1800
        }
1801
1802 3
        return static::create(
1803 3
            $array,
1804 3
            $this->iteratorClass,
1805 3
            false
1806
        );
1807
    }
1808
1809
    /**
1810
     * Get and rmove the first value(s) from the current array.
1811
     * And will return an empty array if there was no first entry.
1812
     *
1813
     * @param int|null $number <p>How many values you will take?</p>
1814
     *
1815
     * @return static
1816
     *                <p>(Mutable)</p>
1817
     */
1818 34
    public function firstsMutable(int $number = null): self
1819
    {
1820 34
        $this->generatorToArray();
1821
1822 34
        if ($number === null) {
1823 19
            $this->array = (array) \array_shift($this->array);
1824
        } else {
1825 15
            $number = (int) $number;
1826 15
            $this->array = \array_splice($this->array, 0, $number);
1827
        }
1828
1829 34
        return $this;
1830
    }
1831
1832
    /**
1833
     * Exchanges all keys with their associated values in an array.
1834
     *
1835
     * @return static
1836
     *                <p>(Immutable)</p>
1837
     */
1838 1
    public function flip(): self
1839
    {
1840 1
        return static::create(
1841 1
            \array_flip($this->getArray()),
1842 1
            $this->iteratorClass,
1843 1
            false
1844
        );
1845
    }
1846
1847
    /**
1848
     * Get a value from an array (optional using dot-notation).
1849
     *
1850
     * @param mixed $key      <p>The key to look for.</p>
1851
     * @param mixed $fallback <p>Value to fallback to.</p>
1852
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1853
     *                        class.</p>
1854
     *
1855
     * @return mixed|static
1856
     */
1857 104
    public function get($key, $fallback = null, array $array = null)
1858
    {
1859 104
        if ($array !== null) {
1860 4
            $usedArray = $array;
1861
        } else {
1862 101
            $this->generatorToArray();
1863
1864 101
            $usedArray = $this->array;
1865
        }
1866
1867 104
        if ($key === null) {
1868 1
            return static::create(
1869 1
                $usedArray,
1870 1
                $this->iteratorClass,
1871 1
                false
1872
            );
1873
        }
1874
1875
        // php cast "bool"-index into "int"-index
1876 104
        if ((bool) $key === $key) {
1877 3
            $key = (int) $key;
1878
        }
1879
1880 104
        if (\array_key_exists($key, $usedArray) === true) {
1881 93
            if (\is_array($usedArray[$key]) === true) {
1882 12
                return static::create(
1883 12
                    $usedArray[$key],
1884 12
                    $this->iteratorClass,
1885 12
                    false
1886
                );
1887
            }
1888
1889 84
            return $usedArray[$key];
1890
        }
1891
1892
        // crawl through array, get key according to object or not
1893 24
        $usePath = false;
1894
        if (
1895 24
            $this->pathSeparator
1896
            &&
1897 24
            (string) $key === $key
1898
            &&
1899 24
            \strpos($key, $this->pathSeparator) !== false
1900
        ) {
1901 7
            $segments = \explode($this->pathSeparator, (string) $key);
1902 7
            if ($segments !== false) {
1903 7
                $usePath = true;
1904
1905 7
                foreach ($segments as $segment) {
1906
                    if (
1907
                        (
1908 7
                            \is_array($usedArray) === true
1909
                            ||
1910 7
                            $usedArray instanceof \ArrayAccess
1911
                        )
1912
                        &&
1913 7
                        isset($usedArray[$segment])
1914
                    ) {
1915 7
                        $usedArray = $usedArray[$segment];
1916
1917 7
                        continue;
1918
                    }
1919
1920
                    if (
1921 6
                        \is_object($usedArray) === true
1922
                        &&
1923 6
                        \property_exists($usedArray, $segment)
1924
                    ) {
1925 1
                        $usedArray = $usedArray->{$segment};
1926
1927 1
                        continue;
1928
                    }
1929
1930 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1931
                }
1932
            }
1933
        }
1934
1935 24
        if (!$usePath && !isset($usedArray[$key])) {
1936 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1937
        }
1938
1939 7
        if (\is_array($usedArray) === true) {
1940 1
            return static::create(
1941 1
                $usedArray,
1942 1
                $this->iteratorClass,
1943 1
                false
1944
            );
1945
        }
1946
1947 7
        return $usedArray;
1948
    }
1949
1950
    /**
1951
     * alias: for "Arrayy->getArray()"
1952
     *
1953
     * @return array
1954
     *
1955
     * @see Arrayy::getArray()
1956
     */
1957 1
    public function getAll(): array
1958
    {
1959 1
        return $this->getArray();
1960
    }
1961
1962
    /**
1963
     * Get the current array from the "Arrayy"-object.
1964
     *
1965
     * @return array
1966
     */
1967 800
    public function getArray(): array
1968
    {
1969
        // init
1970 800
        $array = [];
1971
1972 800
        foreach ($this->getGenerator() as $key => $value) {
1973 691
            $array[$key] = $value;
1974
        }
1975
1976 800
        return $array;
1977
    }
1978
1979
    /**
1980
     * Returns the values from a single column of the input array, identified by
1981
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1982
     *
1983
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1984
     * array by the values from the $indexKey column in the input array.
1985
     *
1986
     * @param mixed $columnKey
1987
     * @param mixed $indexKey
1988
     *
1989
     * @return static
1990
     *                <p>(Immutable)</p>
1991
     */
1992 1
    public function getColumn($columnKey = null, $indexKey = null): self
1993
    {
1994 1
        return static::create(
1995 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1996 1
            $this->iteratorClass,
1997 1
            false
1998
        );
1999
    }
2000
2001
    /**
2002
     * Get the current array from the "Arrayy"-object as generator.
2003
     *
2004
     * @return \Generator
2005
     */
2006 876
    public function getGenerator(): \Generator
2007
    {
2008 876
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2009 39
            yield from $this->generator;
2010
        }
2011
2012 876
        yield from $this->array;
2013 833
    }
2014
2015
    /**
2016
     * alias: for "Arrayy->keys()"
2017
     *
2018
     * @return static
2019
     *                <p>(Immutable)</p>
2020
     *
2021
     * @see Arrayy::keys()
2022
     */
2023 2
    public function getKeys(): self
2024
    {
2025 2
        return $this->keys();
2026
    }
2027
2028
    /**
2029
     * Get the current array from the "Arrayy"-object as object.
2030
     *
2031
     * @return \stdClass
2032
     */
2033 4
    public function getObject(): \stdClass
2034
    {
2035 4
        return self::arrayToObject($this->getArray());
2036
    }
2037
2038
    /**
2039
     * alias: for "Arrayy->randomImmutable()"
2040
     *
2041
     * @return static
2042
     *                <p>(Immutable)</p>
2043
     *
2044
     * @see Arrayy::randomImmutable()
2045
     */
2046 4
    public function getRandom(): self
2047
    {
2048 4
        return $this->randomImmutable();
2049
    }
2050
2051
    /**
2052
     * alias: for "Arrayy->randomKey()"
2053
     *
2054
     * @return mixed
2055
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2056
     *
2057
     * @see Arrayy::randomKey()
2058
     */
2059 3
    public function getRandomKey()
2060
    {
2061 3
        return $this->randomKey();
2062
    }
2063
2064
    /**
2065
     * alias: for "Arrayy->randomKeys()"
2066
     *
2067
     * @param int $number
2068
     *
2069
     * @return static
2070
     *                <p>(Immutable)</p>
2071
     *
2072
     * @see Arrayy::randomKeys()
2073
     */
2074 8
    public function getRandomKeys(int $number): self
2075
    {
2076 8
        return $this->randomKeys($number);
2077
    }
2078
2079
    /**
2080
     * alias: for "Arrayy->randomValue()"
2081
     *
2082
     * @return mixed
2083
     *               <p>Get a random value or null if there wasn't a value.</p>
2084
     *
2085
     * @see Arrayy::randomValue()
2086
     */
2087 3
    public function getRandomValue()
2088
    {
2089 3
        return $this->randomValue();
2090
    }
2091
2092
    /**
2093
     * alias: for "Arrayy->randomValues()"
2094
     *
2095
     * @param int $number
2096
     *
2097
     * @return static
2098
     *                <p>(Immutable)</p>
2099
     *
2100
     * @see Arrayy::randomValues()
2101
     */
2102 6
    public function getRandomValues(int $number): self
2103
    {
2104 6
        return $this->randomValues($number);
2105
    }
2106
2107
    /**
2108
     * Group values from a array according to the results of a closure.
2109
     *
2110
     * @param callable|string $grouper  <p>A callable function name.</p>
2111
     * @param bool            $saveKeys
2112
     *
2113
     * @return static
2114
     *                <p>(Immutable)</p>
2115
     */
2116 4
    public function group($grouper, bool $saveKeys = false): self
2117
    {
2118
        // init
2119 4
        $result = [];
2120
2121
        // Iterate over values, group by property/results from closure.
2122 4
        foreach ($this->getGenerator() as $key => $value) {
2123 4
            if (\is_callable($grouper) === true) {
2124 3
                $groupKey = $grouper($value, $key);
2125
            } else {
2126 1
                $groupKey = $this->get($grouper);
2127
            }
2128
2129 4
            $newValue = $this->get($groupKey, null, $result);
2130
2131 4
            if ($groupKey instanceof self) {
2132
                $groupKey = $groupKey->getArray();
2133
            }
2134
2135 4
            if ($newValue instanceof self) {
2136 4
                $newValue = $newValue->getArray();
2137
            }
2138
2139
            // Add to results.
2140 4
            if ($groupKey !== null) {
2141 3
                if ($saveKeys) {
2142 2
                    $result[$groupKey] = $newValue;
2143 2
                    $result[$groupKey][$key] = $value;
2144
                } else {
2145 1
                    $result[$groupKey] = $newValue;
2146 1
                    $result[$groupKey][] = $value;
2147
                }
2148
            }
2149
        }
2150
2151 4
        return static::create(
2152 4
            $result,
2153 4
            $this->iteratorClass,
2154 4
            false
2155
        );
2156
    }
2157
2158
    /**
2159
     * Check if an array has a given key.
2160
     *
2161
     * @param mixed $key
2162
     *
2163
     * @return bool
2164
     */
2165 23
    public function has($key): bool
2166
    {
2167 23
        static $UN_FOUND = null;
2168
2169 23
        if ($UN_FOUND === null) {
2170
            // Generate unique string to use as marker.
2171 1
            $UN_FOUND = \uniqid('arrayy', true);
2172
        }
2173
2174 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2175
    }
2176
2177
    /**
2178
     * Implodes the values of this array.
2179
     *
2180
     * @param string $glue
2181
     *
2182
     * @return string
2183
     */
2184 28
    public function implode(string $glue = ''): string
2185
    {
2186 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2187
    }
2188
2189
    /**
2190
     * Implodes the keys of this array.
2191
     *
2192
     * @param string $glue
2193
     *
2194
     * @return string
2195
     */
2196 8
    public function implodeKeys(string $glue = ''): string
2197
    {
2198 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2199
    }
2200
2201
    /**
2202
     * Given a list and an iterate-function that returns
2203
     * a key for each element in the list (or a property name),
2204
     * returns an object with an index of each item.
2205
     *
2206
     * @param mixed $key
2207
     *
2208
     * @return static
2209
     *                <p>(Immutable)</p>
2210
     */
2211 4
    public function indexBy($key): self
2212
    {
2213
        // init
2214 4
        $results = [];
2215
2216 4
        foreach ($this->getGenerator() as $a) {
2217 4
            if (\array_key_exists($key, $a) === true) {
2218 3
                $results[$a[$key]] = $a;
2219
            }
2220
        }
2221
2222 4
        return static::create(
2223 4
            $results,
2224 4
            $this->iteratorClass,
2225 4
            false
2226
        );
2227
    }
2228
2229
    /**
2230
     * alias: for "Arrayy->searchIndex()"
2231
     *
2232
     * @param mixed $value <p>The value to search for.</p>
2233
     *
2234
     * @return mixed
2235
     *
2236
     * @see Arrayy::searchIndex()
2237
     */
2238 4
    public function indexOf($value)
2239
    {
2240 4
        return $this->searchIndex($value);
2241
    }
2242
2243
    /**
2244
     * Get everything but the last..$to items.
2245
     *
2246
     * @param int $to
2247
     *
2248
     * @return static
2249
     *                <p>(Immutable)</p>
2250
     */
2251 12
    public function initial(int $to = 1): self
2252
    {
2253 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2254
    }
2255
2256
    /**
2257
     * Return an array with all elements found in input array.
2258
     *
2259
     * @param array $search
2260
     * @param bool  $keepKeys
2261
     *
2262
     * @return static
2263
     *                <p>(Immutable)</p>
2264
     */
2265 3
    public function intersection(array $search, bool $keepKeys = false): self
2266
    {
2267 3
        if ($keepKeys) {
2268 1
            return static::create(
2269 1
                \array_uintersect(
2270 1
                    $this->array,
2271 1
                    $search,
2272
                    static function ($a, $b) {
2273 1
                        return $a === $b ? 0 : -1;
2274 1
                    }
2275
                ),
2276 1
                $this->iteratorClass,
2277 1
                false
2278
            );
2279
        }
2280
2281 2
        return static::create(
2282 2
            \array_values(\array_intersect($this->getArray(), $search)),
2283 2
            $this->iteratorClass,
2284 2
            false
2285
        );
2286
    }
2287
2288
    /**
2289
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2290
     *
2291
     * @param array $search
2292
     *
2293
     * @return bool
2294
     */
2295 1
    public function intersects(array $search): bool
2296
    {
2297 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2298
    }
2299
2300
    /**
2301
     * Invoke a function on all of an array's values.
2302
     *
2303
     * @param callable $callable
2304
     * @param mixed    $arguments
2305
     *
2306
     * @return static
2307
     *                <p>(Immutable)</p>
2308
     */
2309 1
    public function invoke($callable, $arguments = []): self
2310
    {
2311
        // If one argument given for each iteration, create an array for it.
2312 1
        if (\is_array($arguments) === false) {
2313 1
            $arguments = \array_fill(
2314 1
                0,
2315 1
                \count($this->getArray(), \COUNT_NORMAL),
2316 1
                $arguments
2317
            );
2318
        }
2319
2320
        // If the callable has arguments, pass them.
2321 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...
2322 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2323
        } else {
2324 1
            $array = $this->map($callable);
2325
        }
2326
2327 1
        return static::create(
2328 1
            $array,
2329 1
            $this->iteratorClass,
2330 1
            false
2331
        );
2332
    }
2333
2334
    /**
2335
     * Check whether array is associative or not.
2336
     *
2337
     * @param bool $recursive
2338
     *
2339
     * @return bool
2340
     *              <p>Returns true if associative, false otherwise.</p>
2341
     */
2342 15 View Code Duplication
    public function isAssoc(bool $recursive = false): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2343
    {
2344 15
        if ($this->isEmpty()) {
2345 3
            return false;
2346
        }
2347
2348 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2349 13
            if ((string) $key !== $key) {
2350 11
                return false;
2351
            }
2352
        }
2353
2354 3
        return true;
2355
    }
2356
2357
    /**
2358
     * Check if a given key or keys are empty.
2359
     *
2360
     * @param int|int[]|string|string[]|null $keys
2361
     *
2362
     * @return bool
2363
     *              <p>Returns true if empty, false otherwise.</p>
2364
     */
2365 38
    public function isEmpty($keys = null): bool
2366
    {
2367 38
        if ($this->generator) {
2368
            return $this->getArray() === [];
2369
        }
2370
2371 38
        if ($keys === null) {
2372 38
            return $this->array === [];
2373
        }
2374
2375
        foreach ((array) $keys as $key) {
2376
            if (!empty($this->get($key))) {
2377
                return false;
2378
            }
2379
        }
2380
2381
        return true;
2382
    }
2383
2384
    /**
2385
     * Check if the current array is equal to the given "$array" or not.
2386
     *
2387
     * @param array $array
2388
     *
2389
     * @return bool
2390
     */
2391 1
    public function isEqual(array $array): bool
2392
    {
2393 1
        return $this->getArray() === $array;
2394
    }
2395
2396
    /**
2397
     * Check if the current array is a multi-array.
2398
     *
2399
     * @return bool
2400
     */
2401 22
    public function isMultiArray(): bool
2402
    {
2403
        return !(
2404 22
            \count($this->getArray(), \COUNT_NORMAL)
2405
            ===
2406 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2407
        );
2408
    }
2409
2410
    /**
2411
     * Check whether array is numeric or not.
2412
     *
2413
     * @return bool
2414
     *              <p>Returns true if numeric, false otherwise.</p>
2415
     */
2416 5 View Code Duplication
    public function isNumeric(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2417
    {
2418 5
        if ($this->isEmpty()) {
2419 2
            return false;
2420
        }
2421
2422 4
        foreach ($this->keys()->getGenerator() as $key) {
2423 4
            if ((int) $key !== $key) {
2424 2
                return false;
2425
            }
2426
        }
2427
2428 2
        return true;
2429
    }
2430
2431
    /**
2432
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2433
     *
2434
     * @param bool $recursive
2435
     *
2436
     * @return bool
2437
     */
2438 1
    public function isSequential(bool $recursive = false): bool
2439
    {
2440
2441
        // recursive
2442
2443 1
        if ($recursive === true) {
2444
            return $this->array_keys_recursive($this->getArray())
2445
                   ===
2446
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2447
        }
2448
2449
        // non recursive
2450
2451 1
        return \array_keys($this->getArray())
2452
               ===
2453 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2454
    }
2455
2456
    /**
2457
     * @return array
2458
     */
2459
    public function jsonSerialize(): array
2460
    {
2461
        return $this->getArray();
2462
    }
2463
2464
    /**
2465
     * Checks if the given key exists in the provided array.
2466
     *
2467
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2468
     *       then you need to use "Arrayy->offsetExists()".
2469
     *
2470
     * @param int|string $key the key to look for
2471
     *
2472
     * @return bool
2473
     */
2474 58
    public function keyExists($key): bool
2475
    {
2476 58
        return \array_key_exists($key, $this->array);
2477
    }
2478
2479
    /**
2480
     * Get all keys from the current array.
2481
     *
2482
     * @param bool       $recursive     [optional] <p>
2483
     *                                  Get all keys, also from all sub-arrays from an multi-dimensional array.
2484
     *                                  </p>
2485
     * @param mixed|null $search_values [optional] <p>
2486
     *                                  If specified, then only keys containing these values are returned.
2487
     *                                  </p>
2488
     * @param bool       $strict        [optional] <p>
2489
     *                                  Determines if strict comparison (===) should be used during the search.
2490
     *                                  </p>
2491
     *
2492
     * @return static
2493
     *                <p>(Immutable) An array of all the keys in input.</p>
2494
     */
2495 29
    public function keys(
2496
        bool $recursive = false,
2497
        $search_values = null,
2498
        bool $strict = true
2499
    ): self {
2500
2501
        // recursive
2502
2503 29
        if ($recursive === true) {
2504 4
            $array = $this->array_keys_recursive(
2505 4
                null,
2506 4
                $search_values,
2507 4
                $strict
2508
            );
2509
2510 4
            return static::create(
2511 4
                $array,
2512 4
                $this->iteratorClass,
2513 4
                false
2514
            );
2515
        }
2516
2517
        // non recursive
2518
2519 28
        if ($search_values === null) {
2520
            $arrayFunction = function () {
2521 28
                foreach ($this->getGenerator() as $key => $value) {
2522 26
                    yield $key;
2523
                }
2524 28
            };
2525
        } else {
2526
            $arrayFunction = function () use ($search_values, $strict) {
2527 1
                $is_array_tmp = \is_array($search_values);
2528
2529 1
                foreach ($this->getGenerator() as $key => $value) {
2530 View Code Duplication
                    if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2531
                        (
2532 1
                            $is_array_tmp === false
2533
                            &&
2534 1
                            $strict === true
2535
                            &&
2536 1
                            $search_values === $value
2537
                        )
2538
                        ||
2539
                        (
2540 1
                            $is_array_tmp === false
2541
                            &&
2542 1
                            $strict === false
2543
                            &&
2544 1
                            $search_values == $value
2545
                        )
2546
                        ||
2547
                        (
2548 1
                            $is_array_tmp === true
2549
                            &&
2550 1
                            \in_array($value, $search_values, $strict)
2551
                        )
2552
                    ) {
2553 1
                        yield $key;
2554
                    }
2555
                }
2556 1
            };
2557
        }
2558
2559 28
        return static::create(
2560 28
            $arrayFunction,
2561 28
            $this->iteratorClass,
2562 28
            false
2563
        );
2564
    }
2565
2566
    /**
2567
     * Sort an array by key in reverse order.
2568
     *
2569
     * @param int $sort_flags [optional] <p>
2570
     *                        You may modify the behavior of the sort using the optional
2571
     *                        parameter sort_flags, for details
2572
     *                        see sort.
2573
     *                        </p>
2574
     *
2575
     * @return static
2576
     *                <p>(Mutable) Return this Arrayy object.</p>
2577
     */
2578 4
    public function krsort(int $sort_flags = 0): self
2579
    {
2580 4
        $this->generatorToArray();
2581
2582 4
        \krsort($this->array, $sort_flags);
2583
2584 4
        return $this;
2585
    }
2586
2587
    /**
2588
     * Get the last value from the current array.
2589
     *
2590
     * @return mixed
2591
     *               <p>Return null if there wasn't a element.</p>
2592
     */
2593 16
    public function last()
2594
    {
2595 16
        $key_last = $this->lastKey();
2596 16
        if ($key_last === null) {
2597 2
            return null;
2598
        }
2599
2600 14
        return $this->get($key_last);
2601
    }
2602
2603
    /**
2604
     * Get the last key from the current array.
2605
     *
2606
     * @return mixed
2607
     *               <p>Return null if there wasn't a element.</p>
2608
     */
2609 20
    public function lastKey()
2610
    {
2611 20
        $this->generatorToArray();
2612
2613 20
        return \array_key_last($this->array);
2614
    }
2615
2616
    /**
2617
     * Get the last value(s) from the current array.
2618
     *
2619
     * @param int|null $number
2620
     *
2621
     * @return static
2622
     *                <p>(Immutable)</p>
2623
     */
2624 13
    public function lastsImmutable(int $number = null): self
2625
    {
2626 13
        if ($this->isEmpty()) {
2627 1
            return static::create(
2628 1
                [],
2629 1
                $this->iteratorClass,
2630 1
                false
2631
            );
2632
        }
2633
2634 12
        if ($number === null) {
2635 8
            $poppedValue = $this->last();
2636
2637 8
            if ($poppedValue === null) {
2638 1
                $poppedValue = [$poppedValue];
2639
            } else {
2640 7
                $poppedValue = (array) $poppedValue;
2641
            }
2642
2643 8
            $arrayy = static::create(
2644 8
                $poppedValue,
2645 8
                $this->iteratorClass,
2646 8
                false
2647
            );
2648
        } else {
2649 4
            $number = (int) $number;
2650 4
            $arrayy = $this->rest(-$number);
2651
        }
2652
2653 12
        return $arrayy;
2654
    }
2655
2656
    /**
2657
     * Get the last value(s) from the current array.
2658
     *
2659
     * @param int|null $number
2660
     *
2661
     * @return static
2662
     *                <p>(Mutable)</p>
2663
     */
2664 13
    public function lastsMutable(int $number = null): self
2665
    {
2666 13
        if ($this->isEmpty()) {
2667 1
            return $this;
2668
        }
2669
2670 12
        if ($number === null) {
2671 8
            $poppedValue = $this->last();
2672
2673 8
            if ($poppedValue === null) {
2674 1
                $poppedValue = [$poppedValue];
2675
            } else {
2676 7
                $poppedValue = (array) $poppedValue;
2677
            }
2678
2679 8
            $this->array = static::create(
2680 8
                $poppedValue,
2681 8
                $this->iteratorClass,
2682 8
                false
2683 8
            )->getArray();
2684
        } else {
2685 4
            $number = (int) $number;
2686 4
            $this->array = $this->rest(-$number)->getArray();
2687
        }
2688
2689 12
        $this->generator = null;
2690
2691 12
        return $this;
2692
    }
2693
2694
    /**
2695
     * Count the values from the current array.
2696
     *
2697
     * alias: for "Arrayy->count()"
2698
     *
2699
     * @param int $mode
2700
     *
2701
     * @return int
2702
     *
2703
     * @see Arrayy::count()
2704
     */
2705 20
    public function length(int $mode = \COUNT_NORMAL): int
2706
    {
2707 20
        return $this->count($mode);
2708
    }
2709
2710
    /**
2711
     * Apply the given function to the every element of the array,
2712
     * collecting the results.
2713
     *
2714
     * @param callable $callable
2715
     * @param bool     $useKeyAsSecondParameter
2716
     * @param mixed    ...$arguments
2717
     *
2718
     * @return static
2719
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2720
     */
2721 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2722
    {
2723 5
        $useArguments = \func_num_args() > 2;
2724
2725 5
        return static::create(
2726
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2727 5
                foreach ($this->getGenerator() as $key => $value) {
2728 4
                    if ($useArguments) {
2729 3
                        if ($useKeyAsSecondParameter) {
2730
                            yield $key => $callable($value, $key, ...$arguments);
2731
                        } else {
2732 3
                            yield $key => $callable($value, ...$arguments);
2733
                        }
2734
                    } else {
2735
                        /** @noinspection NestedPositiveIfStatementsInspection */
2736 4
                        if ($useKeyAsSecondParameter) {
2737
                            yield $key => $callable($value, $key);
2738
                        } else {
2739 4
                            yield $key => $callable($value);
2740
                        }
2741
                    }
2742
                }
2743 5
            },
2744 5
            $this->iteratorClass,
2745 5
            false
2746
        );
2747
    }
2748
2749
    /**
2750
     * Check if all items in current array match a truth test.
2751
     *
2752
     * @param \Closure $closure
2753
     *
2754
     * @return bool
2755
     */
2756 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...
2757
    {
2758 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2759 2
            return false;
2760
        }
2761
2762 13
        foreach ($this->getGenerator() as $key => $value) {
2763 13
            $value = $closure($value, $key);
2764
2765 13
            if ($value === false) {
2766 7
                return false;
2767
            }
2768
        }
2769
2770 7
        return true;
2771
    }
2772
2773
    /**
2774
     * Check if any item in the current array matches a truth test.
2775
     *
2776
     * @param \Closure $closure
2777
     *
2778
     * @return bool
2779
     */
2780 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...
2781
    {
2782 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2783 2
            return false;
2784
        }
2785
2786 12
        foreach ($this->getGenerator() as $key => $value) {
2787 12
            $value = $closure($value, $key);
2788
2789 12
            if ($value === true) {
2790 9
                return true;
2791
            }
2792
        }
2793
2794 4
        return false;
2795
    }
2796
2797
    /**
2798
     * Get the max value from an array.
2799
     *
2800
     * @return mixed
2801
     */
2802 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...
2803
    {
2804 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2805 1
            return false;
2806
        }
2807
2808 9
        return \max($this->getArray());
2809
    }
2810
2811
    /**
2812
     * Merge the new $array into the current array.
2813
     *
2814
     * - keep key,value from the current array, also if the index is in the new $array
2815
     *
2816
     * @param array $array
2817
     * @param bool  $recursive
2818
     *
2819
     * @return static
2820
     *                <p>(Immutable)</p>
2821
     */
2822 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...
2823
    {
2824 25
        if ($recursive === true) {
2825 4
            $result = \array_replace_recursive($this->getArray(), $array);
2826
        } else {
2827 21
            $result = \array_replace($this->getArray(), $array);
2828
        }
2829
2830 25
        return static::create(
2831 25
            $result,
2832 25
            $this->iteratorClass,
2833 25
            false
2834
        );
2835
    }
2836
2837
    /**
2838
     * Merge the new $array into the current array.
2839
     *
2840
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2841
     * - create new indexes
2842
     *
2843
     * @param array $array
2844
     * @param bool  $recursive
2845
     *
2846
     * @return static
2847
     *                <p>(Immutable)</p>
2848
     */
2849 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...
2850
    {
2851 16
        if ($recursive === true) {
2852 4
            $result = \array_merge_recursive($this->getArray(), $array);
2853
        } else {
2854 12
            $result = \array_merge($this->getArray(), $array);
2855
        }
2856
2857 16
        return static::create(
2858 16
            $result,
2859 16
            $this->iteratorClass,
2860 16
            false
2861
        );
2862
    }
2863
2864
    /**
2865
     * Merge the the current array into the $array.
2866
     *
2867
     * - use key,value from the new $array, also if the index is in the current array
2868
     *
2869
     * @param array $array
2870
     * @param bool  $recursive
2871
     *
2872
     * @return static
2873
     *                <p>(Immutable)</p>
2874
     */
2875 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...
2876
    {
2877 16
        if ($recursive === true) {
2878 4
            $result = \array_replace_recursive($array, $this->getArray());
2879
        } else {
2880 12
            $result = \array_replace($array, $this->getArray());
2881
        }
2882
2883 16
        return static::create(
2884 16
            $result,
2885 16
            $this->iteratorClass,
2886 16
            false
2887
        );
2888
    }
2889
2890
    /**
2891
     * Merge the current array into the new $array.
2892
     *
2893
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2894
     * - create new indexes
2895
     *
2896
     * @param array $array
2897
     * @param bool  $recursive
2898
     *
2899
     * @return static
2900
     *                <p>(Immutable)</p>
2901
     */
2902 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...
2903
    {
2904 17
        if ($recursive === true) {
2905 4
            $result = \array_merge_recursive($array, $this->getArray());
2906
        } else {
2907 13
            $result = \array_merge($array, $this->getArray());
2908
        }
2909
2910 17
        return static::create(
2911 17
            $result,
2912 17
            $this->iteratorClass,
2913 17
            false
2914
        );
2915
    }
2916
2917
    /**
2918
     * @return ArrayyMeta|static
2919
     */
2920 15
    public static function meta()
2921
    {
2922 15
        return (new ArrayyMeta())->getMetaObject(static::class);
2923
    }
2924
2925
    /**
2926
     * Get the min value from an array.
2927
     *
2928
     * @return mixed
2929
     */
2930 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...
2931
    {
2932 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2933 1
            return false;
2934
        }
2935
2936 9
        return \min($this->getArray());
2937
    }
2938
2939
    /**
2940
     * Get the most used value from the array.
2941
     *
2942
     * @return mixed
2943
     *               <p>Return null if there wasn't a element.</p>
2944
     */
2945 3
    public function mostUsedValue()
2946
    {
2947 3
        return $this->countValues()->arsort()->firstKey();
2948
    }
2949
2950
    /**
2951
     * Get the most used value from the array.
2952
     *
2953
     * @param int|null $number <p>How many values you will take?</p>
2954
     *
2955
     * @return static
2956
     *                <p>(Immutable)</p>
2957
     */
2958 3
    public function mostUsedValues(int $number = null): self
2959
    {
2960 3
        return $this->countValues()->arsort()->firstsKeys($number);
2961
    }
2962
2963
    /**
2964
     * Move an array element to a new index.
2965
     *
2966
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2967
     *
2968
     * @param int|string $from
2969
     * @param int        $to
2970
     *
2971
     * @return static
2972
     *                <p>(Immutable)</p>
2973
     */
2974 1
    public function moveElement($from, $to): self
2975
    {
2976 1
        $array = $this->getArray();
2977
2978 1
        if ((int) $from === $from) {
2979 1
            $tmp = \array_splice($array, $from, 1);
2980 1
            \array_splice($array, (int) $to, 0, $tmp);
2981 1
            $output = $array;
2982 1
        } elseif ((string) $from === $from) {
2983 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2984 1
            $itemToMove = $array[$from];
2985 1
            if ($indexToMove !== false) {
2986 1
                \array_splice($array, $indexToMove, 1);
2987
            }
2988 1
            $i = 0;
2989 1
            $output = [];
2990 1
            foreach ($array as $key => $item) {
2991 1
                if ($i === $to) {
2992 1
                    $output[$from] = $itemToMove;
2993
                }
2994 1
                $output[$key] = $item;
2995 1
                ++$i;
2996
            }
2997
        } else {
2998
            $output = [];
2999
        }
3000
3001 1
        return static::create(
3002 1
            $output,
3003 1
            $this->iteratorClass,
3004 1
            false
3005
        );
3006
    }
3007
3008
    /**
3009
     * Move an array element to the first place.
3010
     *
3011
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3012
     *       loss the keys of an indexed array.
3013
     *
3014
     * @param int|string $key
3015
     *
3016
     * @return static
3017
     *                <p>(Immutable)</p>
3018
     */
3019 1 View Code Duplication
    public function moveElementToFirstPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3020
    {
3021 1
        $array = $this->getArray();
3022
3023 1
        if ($this->offsetExists($key)) {
3024 1
            $tmpValue = $this->get($key);
3025 1
            unset($array[$key]);
3026 1
            $array = [$key => $tmpValue] + $array;
3027
        }
3028
3029 1
        return static::create(
3030 1
            $array,
3031 1
            $this->iteratorClass,
3032 1
            false
3033
        );
3034
    }
3035
3036
    /**
3037
     * Move an array element to the last place.
3038
     *
3039
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
3040
     *       loss the keys of an indexed array.
3041
     *
3042
     * @param int|string $key
3043
     *
3044
     * @return static
3045
     *                <p>(Immutable)</p>
3046
     */
3047 1 View Code Duplication
    public function moveElementToLastPlace($key): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3048
    {
3049 1
        $array = $this->getArray();
3050
3051 1
        if ($this->offsetExists($key)) {
3052 1
            $tmpValue = $this->get($key);
3053 1
            unset($array[$key]);
3054 1
            $array += [$key => $tmpValue];
3055
        }
3056
3057 1
        return static::create(
3058 1
            $array,
3059 1
            $this->iteratorClass,
3060 1
            false
3061
        );
3062
    }
3063
3064
    /**
3065
     * Get a subset of the items from the given array.
3066
     *
3067
     * @param mixed[] $keys
3068
     *
3069
     * @return static
3070
     *                <p>(Immutable)</p>
3071
     */
3072
    public function only(array $keys): self
3073
    {
3074
        $array = $this->getArray();
3075
3076
        return static::create(
3077
            \array_intersect_key($array, \array_flip($keys)),
3078
            $this->iteratorClass,
3079
            false
3080
        );
3081
    }
3082
3083
    /**
3084
     * Pad array to the specified size with a given value.
3085
     *
3086
     * @param int   $size  <p>Size of the result array.</p>
3087
     * @param mixed $value <p>Empty value by default.</p>
3088
     *
3089
     * @return static
3090
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3091
     */
3092 4
    public function pad(int $size, $value): self
3093
    {
3094 4
        return static::create(
3095 4
            \array_pad($this->getArray(), $size, $value),
3096 4
            $this->iteratorClass,
3097 4
            false
3098
        );
3099
    }
3100
3101
    /**
3102
     * Pop a specified value off the end of the current array.
3103
     *
3104
     * @return mixed
3105
     *               <p>(Mutable) The popped element from the current array.</p>
3106
     */
3107 4
    public function pop()
3108
    {
3109 4
        $this->generatorToArray();
3110
3111 4
        return \array_pop($this->array);
3112
    }
3113
3114
    /**
3115
     * Prepend a (key) + value to the current array.
3116
     *
3117
     * @param mixed $value
3118
     * @param mixed $key
3119
     *
3120
     * @return static
3121
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3122
     */
3123 9
    public function prepend($value, $key = null): self
3124
    {
3125 9
        $this->generatorToArray();
3126
3127 9
        if ($key === null) {
3128 8
            \array_unshift($this->array, $value);
3129
        } else {
3130 2
            $this->array = [$key => $value] + $this->array;
3131
        }
3132
3133 9
        return $this;
3134
    }
3135
3136
    /**
3137
     * Add a suffix to each key.
3138
     *
3139
     * @param mixed $suffix
3140
     *
3141
     * @return static
3142
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3143
     */
3144 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...
3145
    {
3146
        // init
3147 10
        $result = [];
3148
3149 10
        foreach ($this->getGenerator() as $key => $item) {
3150 9
            if ($item instanceof self) {
3151
                $result[$key] = $item->prependToEachKey($suffix);
3152 9
            } elseif (\is_array($item) === true) {
3153
                $result[$key] = self::create(
3154
                    $item,
3155
                    $this->iteratorClass,
3156
                    false
3157
                )->prependToEachKey($suffix)
3158
                    ->toArray();
3159
            } else {
3160 9
                $result[$key . $suffix] = $item;
3161
            }
3162
        }
3163
3164 10
        return self::create(
3165 10
            $result,
3166 10
            $this->iteratorClass,
3167 10
            false
3168
        );
3169
    }
3170
3171
    /**
3172
     * Add a suffix to each value.
3173
     *
3174
     * @param mixed $suffix
3175
     *
3176
     * @return static
3177
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3178
     */
3179 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...
3180
    {
3181
        // init
3182 10
        $result = [];
3183
3184 10
        foreach ($this->getGenerator() as $key => $item) {
3185 9
            if ($item instanceof self) {
3186
                $result[$key] = $item->prependToEachValue($suffix);
3187 9
            } elseif (\is_array($item) === true) {
3188
                $result[$key] = self::create(
3189
                    $item,
3190
                    $this->iteratorClass,
3191
                    false
3192
                )->prependToEachValue($suffix)
3193
                    ->toArray();
3194 9
            } elseif (\is_object($item) === true) {
3195 1
                $result[$key] = $item;
3196
            } else {
3197 8
                $result[$key] = $item . $suffix;
3198
            }
3199
        }
3200
3201 10
        return self::create(
3202 10
            $result,
3203 10
            $this->iteratorClass,
3204 10
            false
3205
        );
3206
    }
3207
3208
    /**
3209
     * Return the value of a given key and
3210
     * delete the key.
3211
     *
3212
     * @param int|int[]|string|string[]|null $keyOrKeys
3213
     * @param mixed                          $fallback
3214
     *
3215
     * @return mixed
3216
     */
3217 1
    public function pull($keyOrKeys = null, $fallback = null)
3218
    {
3219 1
        if ($keyOrKeys === null) {
3220
            $array = $this->getArray();
3221
            $this->clear();
3222
3223
            return $array;
3224
        }
3225
3226 1
        if (\is_array($keyOrKeys) === true) {
3227 1
            $valueOrValues = [];
3228 1
            foreach ($keyOrKeys as $key) {
3229 1
                $valueOrValues[] = $this->get($key, $fallback);
3230 1
                $this->offsetUnset($key);
3231
            }
3232
        } else {
3233 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3234 1
            $this->offsetUnset($keyOrKeys);
3235
        }
3236
3237 1
        return $valueOrValues;
3238
    }
3239
3240
    /**
3241
     * Push one or more values onto the end of array at once.
3242
     *
3243
     * @return static
3244
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3245
     */
3246 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...
3247
    {
3248 4
        $this->generatorToArray();
3249
3250 4
        if (\func_num_args()) {
3251 4
            $args = \func_get_args();
3252 4
            \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
3253
        }
3254
3255 4
        return $this;
3256
    }
3257
3258
    /**
3259
     * Get a random value from the current array.
3260
     *
3261
     * @param int|null $number <p>How many values you will take?</p>
3262
     *
3263
     * @return static
3264
     *                <p>(Immutable)</p>
3265
     */
3266 18
    public function randomImmutable(int $number = null): self
3267
    {
3268 18
        $this->generatorToArray();
3269
3270 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...
3271 1
            return static::create(
3272 1
                [],
3273 1
                $this->iteratorClass,
3274 1
                false
3275
            );
3276
        }
3277
3278 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...
3279
            /** @noinspection NonSecureArrayRandUsageInspection */
3280 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3281
3282 13
            return static::create(
3283 13
                $arrayRandValue,
3284 13
                $this->iteratorClass,
3285 13
                false
3286
            );
3287
        }
3288
3289 5
        $arrayTmp = $this->array;
3290
        /** @noinspection NonSecureShuffleUsageInspection */
3291 5
        \shuffle($arrayTmp);
3292
3293 5
        return static::create(
3294 5
            $arrayTmp,
3295 5
            $this->iteratorClass,
3296 5
            false
3297 5
        )->firstsImmutable($number);
3298
    }
3299
3300
    /**
3301
     * Pick a random key/index from the keys of this array.
3302
     *
3303
     * @throws \RangeException If array is empty
3304
     *
3305
     * @return mixed
3306
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3307
     */
3308 4
    public function randomKey()
3309
    {
3310 4
        $result = $this->randomKeys(1);
3311
3312 4
        if (!isset($result[0])) {
3313
            $result[0] = null;
3314
        }
3315
3316 4
        return $result[0];
3317
    }
3318
3319
    /**
3320
     * Pick a given number of random keys/indexes out of this array.
3321
     *
3322
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3323
     *
3324
     * @throws \RangeException If array is empty
3325
     *
3326
     * @return static
3327
     *                <p>(Immutable)</p>
3328
     */
3329 13
    public function randomKeys(int $number): self
3330
    {
3331 13
        $this->generatorToArray();
3332
3333 13
        $count = \count($this->array, \COUNT_NORMAL);
3334
3335 13
        if ($number === 0 || $number > $count) {
3336 2
            throw new \RangeException(
3337 2
                \sprintf(
3338 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3339 2
                    $number,
3340 2
                    $count
3341
                )
3342
            );
3343
        }
3344
3345 11
        $result = (array) \array_rand($this->array, $number);
3346
3347 11
        return static::create(
3348 11
            $result,
3349 11
            $this->iteratorClass,
3350 11
            false
3351
        );
3352
    }
3353
3354
    /**
3355
     * Get a random value from the current array.
3356
     *
3357
     * @param int|null $number <p>How many values you will take?</p>
3358
     *
3359
     * @return static
3360
     *                <p>(Mutable)</p>
3361
     */
3362 17
    public function randomMutable(int $number = null): self
3363
    {
3364 17
        $this->generatorToArray();
3365
3366 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...
3367
            return static::create(
3368
                [],
3369
                $this->iteratorClass,
3370
                false
3371
            );
3372
        }
3373
3374 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...
3375
            /** @noinspection NonSecureArrayRandUsageInspection */
3376 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3377 7
            $this->array = $arrayRandValue;
3378
3379 7
            return $this;
3380
        }
3381
3382
        /** @noinspection NonSecureShuffleUsageInspection */
3383 11
        \shuffle($this->array);
3384
3385 11
        return $this->firstsMutable($number);
3386
    }
3387
3388
    /**
3389
     * Pick a random value from the values of this array.
3390
     *
3391
     * @return mixed
3392
     *               <p>Get a random value or null if there wasn't a value.</p>
3393
     */
3394 4
    public function randomValue()
3395
    {
3396 4
        $result = $this->randomImmutable();
3397
3398 4
        if (!isset($result[0])) {
3399
            $result[0] = null;
3400
        }
3401
3402 4
        return $result[0];
3403
    }
3404
3405
    /**
3406
     * Pick a given number of random values out of this array.
3407
     *
3408
     * @param int $number
3409
     *
3410
     * @return static
3411
     *                <p>(Mutable)</p>
3412
     */
3413 7
    public function randomValues(int $number): self
3414
    {
3415 7
        return $this->randomMutable($number);
3416
    }
3417
3418
    /**
3419
     * Get a random value from an array, with the ability to skew the results.
3420
     *
3421
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3422
     *
3423
     * @param array    $array
3424
     * @param int|null $number <p>How many values you will take?</p>
3425
     *
3426
     * @return static
3427
     *                <p>(Immutable)</p>
3428
     */
3429 9
    public function randomWeighted(array $array, int $number = null): self
3430
    {
3431
        // init
3432 9
        $options = [];
3433
3434 9
        foreach ($array as $option => $weight) {
3435 9
            if ($this->searchIndex($option) !== false) {
3436 2
                for ($i = 0; $i < $weight; ++$i) {
3437 1
                    $options[] = $option;
3438
                }
3439
            }
3440
        }
3441
3442 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3443
    }
3444
3445
    /**
3446
     * Reduce the current array via callable e.g. anonymous-function.
3447
     *
3448
     * @param callable $callable
3449
     * @param array    $init
3450
     *
3451
     * @return static
3452
     *                <p>(Immutable)</p>
3453
     */
3454 16
    public function reduce($callable, array $init = []): self
3455
    {
3456 16
        if ($this->generator) {
3457 1
            $result = $init;
3458
3459 1
            foreach ($this->getGenerator() as $value) {
3460 1
                $result = $callable($result, $value);
3461
            }
3462
3463 1
            return static::create(
3464 1
                $result,
3465 1
                $this->iteratorClass,
3466 1
                false
3467
            );
3468
        }
3469
3470 16
        $result = \array_reduce($this->array, $callable, $init);
3471
3472 16
        if ($result === null) {
3473
            $this->array = [];
3474
        } else {
3475 16
            $this->array = (array) $result;
3476
        }
3477
3478 16
        return static::create(
3479 16
            $this->array,
3480 16
            $this->iteratorClass,
3481 16
            false
3482
        );
3483
    }
3484
3485
    /**
3486
     * @param bool $unique
3487
     *
3488
     * @return static
3489
     *                <p>(Immutable)</p>
3490
     */
3491 14
    public function reduce_dimension(bool $unique = true): self
3492
    {
3493
        // init
3494 14
        $result = [];
3495
3496 14
        foreach ($this->getGenerator() as $val) {
3497 12
            if (\is_array($val) === true) {
3498 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3499
            } else {
3500 12
                $result[] = [$val];
3501
            }
3502
        }
3503
3504 14
        $result = $result === [] ? [] : \array_merge(...$result);
3505
3506 14
        $resultArrayy = new self($result);
3507
3508 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3509
    }
3510
3511
    /**
3512
     * Create a numerically re-indexed Arrayy object.
3513
     *
3514
     * @return static
3515
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3516
     */
3517 9
    public function reindex(): self
3518
    {
3519 9
        $this->generatorToArray();
3520
3521 9
        $this->array = \array_values($this->array);
3522
3523 9
        return $this;
3524
    }
3525
3526
    /**
3527
     * Return all items that fail the truth test.
3528
     *
3529
     * @param \Closure $closure
3530
     *
3531
     * @return static
3532
     *                <p>(Immutable)</p>
3533
     */
3534 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...
3535
    {
3536
        // init
3537 1
        $filtered = [];
3538
3539 1
        foreach ($this->getGenerator() as $key => $value) {
3540 1
            if (!$closure($value, $key)) {
3541 1
                $filtered[$key] = $value;
3542
            }
3543
        }
3544
3545 1
        return static::create(
3546 1
            $filtered,
3547 1
            $this->iteratorClass,
3548 1
            false
3549
        );
3550
    }
3551
3552
    /**
3553
     * Remove a value from the current array (optional using dot-notation).
3554
     *
3555
     * @param mixed $key
3556
     *
3557
     * @return static
3558
     *                <p>(Mutable)</p>
3559
     */
3560 18
    public function remove($key): self
3561
    {
3562
        // recursive call
3563 18
        if (\is_array($key) === true) {
3564
            foreach ($key as $k) {
3565
                $this->internalRemove($k);
3566
            }
3567
3568
            return static::create(
3569
                $this->getArray(),
3570
                $this->iteratorClass,
3571
                false
3572
            );
3573
        }
3574
3575 18
        $this->internalRemove($key);
3576
3577 18
        return static::create(
3578 18
            $this->getArray(),
3579 18
            $this->iteratorClass,
3580 18
            false
3581
        );
3582
    }
3583
3584
    /**
3585
     * Remove the first value from the current array.
3586
     *
3587
     * @return static
3588
     *                <p>(Immutable)</p>
3589
     */
3590 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...
3591
    {
3592 7
        $tmpArray = $this->getArray();
3593
3594 7
        \array_shift($tmpArray);
3595
3596 7
        return static::create(
3597 7
            $tmpArray,
3598 7
            $this->iteratorClass,
3599 7
            false
3600
        );
3601
    }
3602
3603
    /**
3604
     * Remove the last value from the current array.
3605
     *
3606
     * @return static
3607
     *                <p>(Immutable)</p>
3608
     */
3609 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...
3610
    {
3611 7
        $tmpArray = $this->getArray();
3612
3613 7
        \array_pop($tmpArray);
3614
3615 7
        return static::create(
3616 7
            $tmpArray,
3617 7
            $this->iteratorClass,
3618 7
            false
3619
        );
3620
    }
3621
3622
    /**
3623
     * Removes a particular value from an array (numeric or associative).
3624
     *
3625
     * @param mixed $value
3626
     *
3627
     * @return static
3628
     *                <p>(Immutable)</p>
3629
     */
3630 7
    public function removeValue($value): self
3631
    {
3632 7
        $this->generatorToArray();
3633
3634
        // init
3635 7
        $isNumericArray = true;
3636
3637 7
        foreach ($this->getGenerator() as $key => $item) {
3638 6
            if ($item === $value) {
3639 6
                if ((int) $key !== $key) {
3640
                    $isNumericArray = false;
3641
                }
3642 6
                unset($this->array[$key]);
3643
            }
3644
        }
3645
3646 7
        if ($isNumericArray) {
3647 7
            $this->array = \array_values($this->array);
3648
        }
3649
3650 7
        return static::create(
3651 7
            $this->array,
3652 7
            $this->iteratorClass,
3653 7
            false
3654
        );
3655
    }
3656
3657
    /**
3658
     * Generate array of repeated arrays.
3659
     *
3660
     * @param int $times <p>How many times has to be repeated.</p>
3661
     *
3662
     * @return static
3663
     *                <p>(Immutable)</p>
3664
     */
3665 1
    public function repeat($times): self
3666
    {
3667 1
        if ($times === 0) {
3668 1
            return new static();
3669
        }
3670
3671 1
        return static::create(
3672 1
            \array_fill(0, (int) $times, $this->getArray()),
3673 1
            $this->iteratorClass,
3674 1
            false
3675
        );
3676
    }
3677
3678
    /**
3679
     * Replace a key with a new key/value pair.
3680
     *
3681
     * @param mixed $replace
3682
     * @param mixed $key
3683
     * @param mixed $value
3684
     *
3685
     * @return static
3686
     *                <p>(Immutable)</p>
3687
     */
3688 2
    public function replace($replace, $key, $value): self
3689
    {
3690 2
        $that = clone $this;
3691
3692 2
        return $that->remove($replace)
3693 2
            ->set($key, $value);
3694
    }
3695
3696
    /**
3697
     * Create an array using the current array as values and the other array as keys.
3698
     *
3699
     * @param array $keys <p>An array of keys.</p>
3700
     *
3701
     * @return static
3702
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3703
     */
3704 2
    public function replaceAllKeys(array $keys): self
3705
    {
3706 2
        return static::create(
3707 2
            \array_combine($keys, $this->getArray()),
3708 2
            $this->iteratorClass,
3709 2
            false
3710
        );
3711
    }
3712
3713
    /**
3714
     * Create an array using the current array as keys and the other array as values.
3715
     *
3716
     * @param array $array <p>An array o values.</p>
3717
     *
3718
     * @return static
3719
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3720
     */
3721 2
    public function replaceAllValues(array $array): self
3722
    {
3723 2
        return static::create(
3724 2
            \array_combine($this->array, $array),
3725 2
            $this->iteratorClass,
3726 2
            false
3727
        );
3728
    }
3729
3730
    /**
3731
     * Replace the keys in an array with another set.
3732
     *
3733
     * @param array $keys <p>An array of keys matching the array's size</p>
3734
     *
3735
     * @return static
3736
     *                <p>(Immutable)</p>
3737
     */
3738 1
    public function replaceKeys(array $keys): self
3739
    {
3740 1
        $values = \array_values($this->getArray());
3741 1
        $result = \array_combine($keys, $values);
3742
3743 1
        return static::create(
3744 1
            $result,
3745 1
            $this->iteratorClass,
3746 1
            false
3747
        );
3748
    }
3749
3750
    /**
3751
     * Replace the first matched value in an array.
3752
     *
3753
     * @param mixed $search      <p>The value to replace.</p>
3754
     * @param mixed $replacement <p>The value to replace.</p>
3755
     *
3756
     * @return static
3757
     *                <p>(Immutable)</p>
3758
     */
3759 3
    public function replaceOneValue($search, $replacement = ''): self
3760
    {
3761 3
        $array = $this->getArray();
3762 3
        $key = \array_search($search, $array, true);
3763
3764 3
        if ($key !== false) {
3765 3
            $array[$key] = $replacement;
3766
        }
3767
3768 3
        return static::create(
3769 3
            $array,
3770 3
            $this->iteratorClass,
3771 3
            false
3772
        );
3773
    }
3774
3775
    /**
3776
     * Replace values in the current array.
3777
     *
3778
     * @param mixed $search      <p>The value to replace.</p>
3779
     * @param mixed $replacement <p>What to replace it with.</p>
3780
     *
3781
     * @return static
3782
     *                <p>(Immutable)</p>
3783
     */
3784 1
    public function replaceValues($search, $replacement = ''): self
3785
    {
3786 1
        return $this->each(
3787
            static function ($value) use ($search, $replacement) {
3788 1
                return \str_replace($search, $replacement, $value);
3789 1
            }
3790
        );
3791
    }
3792
3793
    /**
3794
     * Get the last elements from index $from until the end of this array.
3795
     *
3796
     * @param int $from
3797
     *
3798
     * @return static
3799
     *                <p>(Immutable)</p>
3800
     */
3801 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...
3802
    {
3803 15
        $tmpArray = $this->getArray();
3804
3805 15
        return static::create(
3806 15
            \array_splice($tmpArray, $from),
3807 15
            $this->iteratorClass,
3808 15
            false
3809
        );
3810
    }
3811
3812
    /**
3813
     * Return the array in the reverse order.
3814
     *
3815
     * @return static
3816
     *                <p>(Mutable) Return this Arrayy object.</p>
3817
     */
3818 8
    public function reverse(): self
3819
    {
3820 8
        $this->generatorToArray();
3821
3822 8
        $this->array = \array_reverse($this->array);
3823
3824 8
        return $this;
3825
    }
3826
3827
    /**
3828
     * Sort an array in reverse order.
3829
     *
3830
     * @param int $sort_flags [optional] <p>
3831
     *                        You may modify the behavior of the sort using the optional
3832
     *                        parameter sort_flags, for details
3833
     *                        see sort.
3834
     *                        </p>
3835
     *
3836
     * @return static
3837
     *                <p>(Mutable) Return this Arrayy object.</p>
3838
     */
3839 4
    public function rsort(int $sort_flags = 0): self
3840
    {
3841 4
        $this->generatorToArray();
3842
3843 4
        \rsort($this->array, $sort_flags);
3844
3845 4
        return $this;
3846
    }
3847
3848
    /**
3849
     * Search for the first index of the current array via $value.
3850
     *
3851
     * @param mixed $value
3852
     *
3853
     * @return false|float|int|string
3854
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3855
     */
3856 20
    public function searchIndex($value)
3857
    {
3858 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3859 19
            if ($value === $valueFromArray) {
3860 9
                return $keyFromArray;
3861
            }
3862
        }
3863
3864 11
        return false;
3865
    }
3866
3867
    /**
3868
     * Search for the value of the current array via $index.
3869
     *
3870
     * @param mixed $index
3871
     *
3872
     * @return static
3873
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3874
     */
3875 9
    public function searchValue($index): self
3876
    {
3877 9
        $this->generatorToArray();
3878
3879
        // init
3880 9
        $return = [];
3881
3882 9
        if ($this->array === []) {
3883
            return static::create(
3884
                [],
3885
                $this->iteratorClass,
3886
                false
3887
            );
3888
        }
3889
3890
        // php cast "bool"-index into "int"-index
3891 9
        if ((bool) $index === $index) {
3892 1
            $index = (int) $index;
3893
        }
3894
3895 9
        if ($this->offsetExists($index)) {
3896 7
            $return = [$this->array[$index]];
3897
        }
3898
3899 9
        return static::create(
3900 9
            $return,
3901 9
            $this->iteratorClass,
3902 9
            false
3903
        );
3904
    }
3905
3906
    /**
3907
     * Set a value for the current array (optional using dot-notation).
3908
     *
3909
     * @param string $key   <p>The key to set.</p>
3910
     * @param mixed  $value <p>Its value.</p>
3911
     *
3912
     * @return static
3913
     *                <p>(Mutable)</p>
3914
     */
3915 18
    public function set($key, $value): self
3916
    {
3917 18
        $this->generatorToArray();
3918
3919 18
        $this->internalSet($key, $value);
3920
3921 18
        return $this;
3922
    }
3923
3924
    /**
3925
     * Get a value from a array and set it if it was not.
3926
     *
3927
     * WARNING: this method only set the value, if the $key is not already set
3928
     *
3929
     * @param mixed $key      <p>The key</p>
3930
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3931
     *
3932
     * @return mixed
3933
     *               <p>(Mutable)</p>
3934
     */
3935 11
    public function setAndGet($key, $fallback = null)
3936
    {
3937 11
        $this->generatorToArray();
3938
3939
        // If the key doesn't exist, set it.
3940 11
        if (!$this->has($key)) {
3941 4
            $this->array = $this->set($key, $fallback)->getArray();
3942
        }
3943
3944 11
        return $this->get($key);
3945
    }
3946
3947
    /**
3948
     * Shifts a specified value off the beginning of array.
3949
     *
3950
     * @return mixed
3951
     *               <p>(Mutable) A shifted element from the current array.</p>
3952
     */
3953 4
    public function shift()
3954
    {
3955 4
        $this->generatorToArray();
3956
3957 4
        return \array_shift($this->array);
3958
    }
3959
3960
    /**
3961
     * Shuffle the current array.
3962
     *
3963
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3964
     * @param array $array  [optional]
3965
     *
3966
     * @return static
3967
     *                <p>(Immutable)</p>
3968
     */
3969 1
    public function shuffle(bool $secure = false, array $array = null): self
3970
    {
3971 1
        if ($array === null) {
3972 1
            $array = $this->getArray();
3973
        }
3974
3975 1
        if ($secure !== true) {
3976
            /** @noinspection NonSecureShuffleUsageInspection */
3977 1
            \shuffle($array);
3978
        } else {
3979 1
            $size = \count($array, \COUNT_NORMAL);
3980 1
            $keys = \array_keys($array);
3981 1
            for ($i = $size - 1; $i > 0; --$i) {
3982
                try {
3983 1
                    $r = \random_int(0, $i);
3984
                } catch (\Exception $e) {
3985
                    /** @noinspection RandomApiMigrationInspection */
3986
                    $r = \mt_rand(0, $i);
3987
                }
3988 1
                if ($r !== $i) {
3989 1
                    $temp = $array[$keys[$r]];
3990 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3991 1
                    $array[$keys[$i]] = $temp;
3992
                }
3993
            }
3994
3995
            // reset indices
3996 1
            $array = \array_values($array);
3997
        }
3998
3999 1
        foreach ($array as $key => $value) {
4000
            // check if recursive is needed
4001 1
            if (\is_array($value) === true) {
4002
                $array[$key] = $this->shuffle($secure, $value);
4003
            }
4004
        }
4005
4006 1
        return static::create(
4007 1
            $array,
4008 1
            $this->iteratorClass,
4009 1
            false
4010
        );
4011
    }
4012
4013
    /**
4014
     * Count the values from the current array.
4015
     *
4016
     * alias: for "Arrayy->count()"
4017
     *
4018
     * @param int $mode
4019
     *
4020
     * @return int
4021
     */
4022 20
    public function size(int $mode = \COUNT_NORMAL): int
4023
    {
4024 20
        return $this->count($mode);
4025
    }
4026
4027
    /**
4028
     * Checks whether array has exactly $size items.
4029
     *
4030
     * @param int $size
4031
     *
4032
     * @return bool
4033
     */
4034 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4035
    {
4036
        // init
4037 1
        $itemsTempCount = 0;
4038
4039 1
        foreach ($this->getGenerator() as $key => $value) {
4040 1
            ++$itemsTempCount;
4041 1
            if ($itemsTempCount > $size) {
4042 1
                return false;
4043
            }
4044
        }
4045
4046 1
        return $itemsTempCount === $size;
4047
    }
4048
4049
    /**
4050
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
4051
     * smaller than $fromSize.
4052
     *
4053
     * @param int $fromSize
4054
     * @param int $toSize
4055
     *
4056
     * @return bool
4057
     */
4058 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4059
    {
4060 1
        if ($fromSize > $toSize) {
4061 1
            $tmp = $toSize;
4062 1
            $toSize = $fromSize;
4063 1
            $fromSize = $tmp;
4064
        }
4065
4066
        // init
4067 1
        $itemsTempCount = 0;
4068
4069 1
        foreach ($this->getGenerator() as $key => $value) {
4070 1
            ++$itemsTempCount;
4071 1
            if ($itemsTempCount > $toSize) {
4072 1
                return false;
4073
            }
4074
        }
4075
4076 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4077
    }
4078
4079
    /**
4080
     * Checks whether array has more than $size items.
4081
     *
4082
     * @param int $size
4083
     *
4084
     * @return bool
4085
     */
4086 1 View Code Duplication
    public function sizeIsGreaterThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4087
    {
4088
        // init
4089 1
        $itemsTempCount = 0;
4090
4091 1
        foreach ($this->getGenerator() as $key => $value) {
4092 1
            ++$itemsTempCount;
4093 1
            if ($itemsTempCount > $size) {
4094 1
                return true;
4095
            }
4096
        }
4097
4098 1
        return $itemsTempCount > $size;
4099
    }
4100
4101
    /**
4102
     * Checks whether array has less than $size items.
4103
     *
4104
     * @param int $size
4105
     *
4106
     * @return bool
4107
     */
4108 1 View Code Duplication
    public function sizeIsLessThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4109
    {
4110
        // init
4111 1
        $itemsTempCount = 0;
4112
4113 1
        foreach ($this->getGenerator() as $key => $value) {
4114 1
            ++$itemsTempCount;
4115 1
            if ($itemsTempCount > $size) {
4116 1
                return false;
4117
            }
4118
        }
4119
4120 1
        return $itemsTempCount < $size;
4121
    }
4122
4123
    /**
4124
     * Counts all elements in an array, or something in an object.
4125
     *
4126
     * <p>
4127
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4128
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4129
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4130
     * implemented and used in PHP.
4131
     * </p>
4132
     *
4133
     * @return int
4134
     *             <p>
4135
     *             The number of elements in var, which is
4136
     *             typically an array, since anything else will have one
4137
     *             element.
4138
     *             </p>
4139
     *             <p>
4140
     *             If var is not an array or an object with
4141
     *             implemented Countable interface,
4142
     *             1 will be returned.
4143
     *             There is one exception, if var is &null;,
4144
     *             0 will be returned.
4145
     *             </p>
4146
     *             <p>
4147
     *             Caution: count may return 0 for a variable that isn't set,
4148
     *             but it may also return 0 for a variable that has been initialized with an
4149
     *             empty array. Use isset to test if a variable is set.
4150
     *             </p>
4151
     */
4152 10
    public function sizeRecursive(): int
4153
    {
4154 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4155
    }
4156
4157
    /**
4158
     * Extract a slice of the array.
4159
     *
4160
     * @param int      $offset       <p>Slice begin index.</p>
4161
     * @param int|null $length       <p>Length of the slice.</p>
4162
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4163
     *
4164
     * @return static
4165
     *                <p>A slice of the original array with length $length.</p>
4166
     */
4167 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4168
    {
4169 4
        return static::create(
4170 4
            \array_slice(
4171 4
                $this->getArray(),
4172 4
                $offset,
4173 4
                $length,
4174 4
                $preserveKeys
4175
            ),
4176 4
            $this->iteratorClass,
4177 4
            false
4178
        );
4179
    }
4180
4181
    /**
4182
     * Sort the current array and optional you can keep the keys.
4183
     *
4184
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4185
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4186
     *                              <strong>SORT_NATURAL</strong></p>
4187
     * @param bool       $keepKeys
4188
     *
4189
     * @return static
4190
     *                <p>(Mutable) Return this Arrayy object.</p>
4191
     */
4192 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4193
    {
4194 20
        $this->generatorToArray();
4195
4196 20
        return $this->sorting(
4197 20
            $this->array,
4198 20
            $direction,
4199 20
            $strategy,
4200 20
            $keepKeys
4201
        );
4202
    }
4203
4204
    /**
4205
     * Sort the current array by key.
4206
     *
4207
     * @see http://php.net/manual/en/function.ksort.php
4208
     * @see http://php.net/manual/en/function.krsort.php
4209
     *
4210
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4211
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4212
     *                              <strong>SORT_NATURAL</strong></p>
4213
     *
4214
     * @return static
4215
     *                <p>(Mutable) Return this Arrayy object.</p>
4216
     */
4217 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4218
    {
4219 18
        $this->generatorToArray();
4220
4221 18
        $this->sorterKeys($this->array, $direction, $strategy);
4222
4223 18
        return $this;
4224
    }
4225
4226
    /**
4227
     * Sort the current array by value.
4228
     *
4229
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4230
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4231
     *                              <strong>SORT_NATURAL</strong></p>
4232
     *
4233
     * @return static
4234
     *                <p>(Mutable)</p>
4235
     */
4236 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4237
    {
4238 1
        return $this->sort($direction, $strategy, true);
4239
    }
4240
4241
    /**
4242
     * Sort the current array by value.
4243
     *
4244
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4245
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4246
     *                              <strong>SORT_NATURAL</strong></p>
4247
     *
4248
     * @return static
4249
     *                <p>(Mutable)</p>
4250
     */
4251 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4252
    {
4253 1
        return $this->sort($direction, $strategy, false);
4254
    }
4255
4256
    /**
4257
     * Sort a array by value, by a closure or by a property.
4258
     *
4259
     * - If the sorter is null, the array is sorted naturally.
4260
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4261
     *
4262
     * @param callable|string|null $sorter
4263
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4264
     *                                        <strong>SORT_DESC</strong></p>
4265
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4266
     *                                        <strong>SORT_NATURAL</strong></p>
4267
     *
4268
     * @return static
4269
     *                <p>(Immutable)</p>
4270
     */
4271 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4272
    {
4273 1
        $array = $this->getArray();
4274 1
        $direction = $this->getDirection($direction);
4275
4276
        // Transform all values into their results.
4277 1
        if ($sorter) {
4278 1
            $arrayy = static::create(
4279 1
                $array,
4280 1
                $this->iteratorClass,
4281 1
                false
4282
            );
4283
4284 1
            $results = $arrayy->each(
4285
                function ($value) use ($sorter) {
4286 1
                    if (\is_callable($sorter) === true) {
4287 1
                        return $sorter($value);
4288
                    }
4289
4290 1
                    return $this->get($sorter);
4291 1
                }
4292
            );
4293
4294 1
            $results = $results->getArray();
4295
        } else {
4296 1
            $results = $array;
4297
        }
4298
4299
        // Sort by the results and replace by original values
4300 1
        \array_multisort($results, $direction, $strategy, $array);
4301
4302 1
        return static::create(
4303 1
            $array,
4304 1
            $this->iteratorClass,
4305 1
            false
4306
        );
4307
    }
4308
4309
    /**
4310
     * Split an array in the given amount of pieces.
4311
     *
4312
     * @param int  $numberOfPieces
4313
     * @param bool $keepKeys
4314
     *
4315
     * @return static
4316
     *                <p>(Immutable)</p>
4317
     */
4318 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4319
    {
4320 1
        $this->generatorToArray();
4321
4322 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4323
4324 1
        if ($arrayCount === 0) {
4325 1
            $result = [];
4326
        } else {
4327 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4328 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4329
        }
4330
4331 1
        return static::create(
4332 1
            $result,
4333 1
            $this->iteratorClass,
4334 1
            false
4335
        );
4336
    }
4337
4338
    /**
4339
     * Stripe all empty items.
4340
     *
4341
     * @return static
4342
     *                <p>(Immutable)</p>
4343
     */
4344 1
    public function stripEmpty(): self
4345
    {
4346 1
        return $this->filter(
4347
            static function ($item) {
4348 1
                if ($item === null) {
4349 1
                    return false;
4350
                }
4351
4352 1
                return (bool) \trim((string) $item);
4353 1
            }
4354
        );
4355
    }
4356
4357
    /**
4358
     * Swap two values between positions by key.
4359
     *
4360
     * @param int|string $swapA <p>a key in the array</p>
4361
     * @param int|string $swapB <p>a key in the array</p>
4362
     *
4363
     * @return static
4364
     *                <p>(Immutable)</p>
4365
     */
4366 1
    public function swap($swapA, $swapB): self
4367
    {
4368 1
        $array = $this->getArray();
4369
4370 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4371
4372 1
        return static::create(
4373 1
            $array,
4374 1
            $this->iteratorClass,
4375 1
            false
4376
        );
4377
    }
4378
4379
    /**
4380
     * alias: for "Arrayy->getArray()"
4381
     *
4382
     * @see Arrayy::getArray()
4383
     */
4384 205
    public function toArray()
4385
    {
4386 205
        return $this->getArray();
4387
    }
4388
4389
    /**
4390
     * Convert the current array to JSON.
4391
     *
4392
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4393
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4394
     *
4395
     * @return string
4396
     */
4397 6
    public function toJson(int $options = 0, int $depth = 512): string
4398
    {
4399 6
        $return = \json_encode($this->getArray(), $options, $depth);
4400 6
        if ($return === false) {
4401
            return '';
4402
        }
4403
4404 6
        return $return;
4405
    }
4406
4407
    /**
4408
     * Implodes array to a string with specified separator.
4409
     *
4410
     * @param string $separator [optional] <p>The element's separator.</p>
4411
     *
4412
     * @return string
4413
     *                <p>The string representation of array, separated by ",".</p>
4414
     */
4415 20
    public function toString(string $separator = ','): string
4416
    {
4417 20
        return $this->implode($separator);
4418
    }
4419
4420
    /**
4421
     * Return a duplicate free copy of the current array.
4422
     *
4423
     * @return static
4424
     *                <p>(Mutable)</p>
4425
     */
4426 12
    public function unique(): self
4427
    {
4428
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4429
4430 12
        $this->array = $this->reduce(
4431
            static function ($resultArray, $value) {
4432 11
                if (!\in_array($value, $resultArray, true)) {
4433 11
                    $resultArray[] = $value;
4434
                }
4435
4436 11
                return $resultArray;
4437 12
            },
4438 12
            []
4439 12
        )->getArray();
4440 12
        $this->generator = null;
4441
4442 12
        return $this;
4443
    }
4444
4445
    /**
4446
     * Return a duplicate free copy of the current array. (with the old keys)
4447
     *
4448
     * @return static
4449
     *                <p>(Mutable)</p>
4450
     */
4451 11
    public function uniqueKeepIndex(): self
4452
    {
4453
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4454
4455
        // init
4456 11
        $array = $this->getArray();
4457
4458 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...
4459 11
            \array_keys($array),
4460
            static function ($resultArray, $key) use ($array) {
4461 10
                if (!\in_array($array[$key], $resultArray, true)) {
4462 10
                    $resultArray[$key] = $array[$key];
4463
                }
4464
4465 10
                return $resultArray;
4466 11
            },
4467 11
            []
4468
        );
4469 11
        $this->generator = null;
4470
4471 11
        if ($this->array === null) {
4472
            $this->array = [];
4473
        } else {
4474 11
            $this->array = (array) $this->array;
4475
        }
4476
4477 11
        return $this;
4478
    }
4479
4480
    /**
4481
     * alias: for "Arrayy->unique()"
4482
     *
4483
     * @return static
4484
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4485
     *
4486
     * @see Arrayy::unique()
4487
     */
4488 10
    public function uniqueNewIndex(): self
4489
    {
4490 10
        return $this->unique();
4491
    }
4492
4493
    /**
4494
     * Prepends one or more values to the beginning of array at once.
4495
     *
4496
     * @return static
4497
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4498
     */
4499 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...
4500
    {
4501 4
        $this->generatorToArray();
4502
4503 4
        if (\func_num_args()) {
4504 4
            $args = \func_get_args();
4505 4
            \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4506
        }
4507
4508 4
        return $this;
4509
    }
4510
4511
    /**
4512
     * Get all values from a array.
4513
     *
4514
     * @return static
4515
     *                <p>(Immutable)</p>
4516
     */
4517 2
    public function values(): self
4518
    {
4519 2
        return static::create(
4520
            function () {
4521
                /** @noinspection YieldFromCanBeUsedInspection */
4522 2
                foreach ($this->getGenerator() as $value) {
4523 2
                    yield $value;
4524
                }
4525 2
            },
4526 2
            $this->iteratorClass,
4527 2
            false
4528
        );
4529
    }
4530
4531
    /**
4532
     * Apply the given function to every element in the array, discarding the results.
4533
     *
4534
     * @param callable $callable
4535
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4536
     *
4537
     * @return static
4538
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4539
     */
4540 11
    public function walk($callable, bool $recursive = false): self
4541
    {
4542 11
        $this->generatorToArray();
4543
4544 11
        if ($recursive === true) {
4545 6
            \array_walk_recursive($this->array, $callable);
4546
        } else {
4547 5
            \array_walk($this->array, $callable);
4548
        }
4549
4550 11
        return $this;
4551
    }
4552
4553
    /**
4554
     * Convert an array into a object.
4555
     *
4556
     * @param array $array PHP array
4557
     *
4558
     * @return \stdClass
4559
     */
4560 4
    protected static function arrayToObject(array $array = []): \stdClass
4561
    {
4562
        // init
4563 4
        $object = new \stdClass();
4564
4565 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4566 1
            return $object;
4567
        }
4568
4569 3
        foreach ($array as $name => $value) {
4570 3
            if (\is_array($value) === true) {
4571 1
                $object->{$name} = self::arrayToObject($value);
4572
            } else {
4573 3
                $object->{$name} = $value;
4574
            }
4575
        }
4576
4577 3
        return $object;
4578
    }
4579
4580
    /**
4581
     * @param array|\Generator|null $input         <p>
4582
     *                                             An array containing keys to return.
4583
     *                                             </p>
4584
     * @param mixed|null            $search_values [optional] <p>
4585
     *                                             If specified, then only keys containing these values are returned.
4586
     *                                             </p>
4587
     * @param bool                  $strict        [optional] <p>
4588
     *                                             Determines if strict comparison (===) should be used during the
4589
     *                                             search.
4590
     *                                             </p>
4591
     *
4592
     * @return array
4593
     *               <p>an array of all the keys in input</p>
4594
     */
4595 11
    protected function array_keys_recursive(
4596
        $input = null,
4597
        $search_values = null,
4598
        bool $strict = true
4599
    ): array {
4600
        // init
4601 11
        $keys = [];
4602 11
        $keysTmp = [];
4603
4604 11
        if ($input === null) {
4605 4
            $input = $this->getGenerator();
4606
        }
4607
4608 11
        if ($search_values === null) {
4609 11
            foreach ($input as $key => $value) {
4610 11
                $keys[] = $key;
4611
4612
                // check if recursive is needed
4613 11
                if (\is_array($value) === true) {
4614 4
                    $keysTmp[] = $this->array_keys_recursive($value);
4615
                }
4616
            }
4617
        } else {
4618 1
            $is_array_tmp = \is_array($search_values);
4619
4620 1
            foreach ($input as $key => $value) {
4621 View Code Duplication
                if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4622
                    (
4623 1
                        $is_array_tmp === false
4624
                        &&
4625 1
                        $strict === true
4626
                        &&
4627 1
                        $search_values === $value
4628
                    )
4629
                    ||
4630
                    (
4631 1
                        $is_array_tmp === false
4632
                        &&
4633 1
                        $strict === false
4634
                        &&
4635 1
                        $search_values == $value
4636
                    )
4637
                    ||
4638
                    (
4639 1
                        $is_array_tmp === true
4640
                        &&
4641 1
                        \in_array($value, $search_values, $strict)
4642
                    )
4643
                ) {
4644 1
                    $keys[] = $key;
4645
                }
4646
4647
                // check if recursive is needed
4648 1
                if (\is_array($value) === true) {
4649 1
                    $keysTmp[] = $this->array_keys_recursive($value);
4650
                }
4651
            }
4652
        }
4653
4654 11
        return $keysTmp === [] ? $keys : \array_merge($keys, ...$keysTmp);
4655
    }
4656
4657
    /**
4658
     * @param mixed      $path
4659
     * @param callable   $callable
4660
     * @param array|null $currentOffset
4661
     */
4662 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4663
    {
4664 4
        $this->generatorToArray();
4665
4666 4
        if ($currentOffset === null) {
4667 4
            $currentOffset = &$this->array;
4668
        }
4669
4670 4
        $explodedPath = \explode($this->pathSeparator, $path);
4671 4
        if ($explodedPath === false) {
4672
            return;
4673
        }
4674
4675 4
        $nextPath = \array_shift($explodedPath);
4676
4677 4
        if (!isset($currentOffset[$nextPath])) {
4678
            return;
4679
        }
4680
4681 4
        if (!empty($explodedPath)) {
4682 1
            $this->callAtPath(
4683 1
                \implode($this->pathSeparator, $explodedPath),
4684 1
                $callable,
4685 1
                $currentOffset[$nextPath]
4686
            );
4687
        } else {
4688 4
            $callable($currentOffset[$nextPath]);
4689
        }
4690 4
    }
4691
4692
    /**
4693
     * create a fallback for array
4694
     *
4695
     * 1. use the current array, if it's a array
4696
     * 2. fallback to empty array, if there is nothing
4697
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4698
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4699
     * 5. call "__toArray()" on object, if the method exists
4700
     * 6. cast a string or object with "__toString()" into an array
4701
     * 7. throw a "InvalidArgumentException"-Exception
4702
     *
4703
     * @param mixed $data
4704
     *
4705
     * @throws \InvalidArgumentException
4706
     *
4707
     * @return array
4708
     */
4709 991
    protected function fallbackForArray(&$data): array
4710
    {
4711 991
        $data = $this->internalGetArray($data);
4712
4713 991
        if ($data === null) {
4714 2
            throw new \InvalidArgumentException(
4715 2
                'Passed value should be a array'
4716
            );
4717
        }
4718
4719 989
        return $data;
4720
    }
4721
4722
    /**
4723
     * Get correct PHP constant for direction.
4724
     *
4725
     * @param int|string $direction
4726
     *
4727
     * @return int
4728
     */
4729 39
    protected function getDirection($direction): int
4730
    {
4731 39
        if ((string) $direction === $direction) {
4732 10
            $direction = \strtolower($direction);
4733
4734 10
            if ($direction === 'desc') {
4735 2
                $direction = \SORT_DESC;
4736
            } else {
4737 8
                $direction = \SORT_ASC;
4738
            }
4739
        }
4740
4741
        if (
4742 39
            $direction !== \SORT_DESC
4743
            &&
4744 39
            $direction !== \SORT_ASC
4745
        ) {
4746
            $direction = \SORT_ASC;
4747
        }
4748
4749 39
        return $direction;
4750
    }
4751
4752
    /**
4753
     * @param mixed $glue
4754
     * @param mixed $pieces
4755
     * @param bool  $useKeys
4756
     *
4757
     * @return string
4758
     */
4759 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4760
    {
4761 36
        if ($pieces instanceof self) {
4762 1
            $pieces = $pieces->getArray();
4763
        }
4764
4765 36
        if (\is_array($pieces) === true) {
4766 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4767 36
            $pieces_count_not_zero = $pieces_count > 0;
4768
4769 36
            return \implode(
4770 36
                $glue,
4771 36
                \array_map(
4772 36
                    [$this, 'implode_recursive'],
4773 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4774 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4775
                )
4776
            );
4777
        }
4778
4779
        if (
4780 36
            \is_scalar($pieces) === true
4781
            ||
4782 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4783
        ) {
4784 31
            return (string) $pieces;
4785
        }
4786
4787 9
        return '';
4788
    }
4789
4790
    /**
4791
     * @param mixed                 $needle   <p>
4792
     *                                        The searched value.
4793
     *                                        </p>
4794
     *                                        <p>
4795
     *                                        If needle is a string, the comparison is done
4796
     *                                        in a case-sensitive manner.
4797
     *                                        </p>
4798
     * @param array|\Generator|null $haystack <p>
4799
     *                                        The array.
4800
     *                                        </p>
4801
     * @param bool                  $strict   [optional] <p>
4802
     *                                        If the third parameter strict is set to true
4803
     *                                        then the in_array function will also check the
4804
     *                                        types of the
4805
     *                                        needle in the haystack.
4806
     *                                        </p>
4807
     *
4808
     * @return bool
4809
     *              <p>true if needle is found in the array, false otherwise</p>
4810
     */
4811 19
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4812
    {
4813 19
        if ($haystack === null) {
4814
            $haystack = $this->getGenerator();
4815
        }
4816
4817 19
        foreach ($haystack as $item) {
4818 15
            if (\is_array($item) === true) {
4819 4
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4820
            } else {
4821
                /** @noinspection NestedPositiveIfStatementsInspection */
4822 15
                if ($strict === true) {
4823 15
                    $returnTmp = $item === $needle;
4824
                } else {
4825
                    $returnTmp = $item == $needle;
4826
                }
4827
            }
4828
4829 15
            if ($returnTmp === true) {
4830 11
                return true;
4831
            }
4832
        }
4833
4834 8
        return false;
4835
    }
4836
4837
    /**
4838
     * @param mixed $data
4839
     *
4840
     * @return array|null
4841
     */
4842 991
    protected function internalGetArray(&$data)
4843
    {
4844 991
        if (\is_array($data) === true) {
4845 988
            return $data;
4846
        }
4847
4848 49
        if (!$data) {
4849 6
            return [];
4850
        }
4851
4852 48
        if (\is_object($data) === true) {
4853 43
            if ($data instanceof \ArrayObject) {
4854 2
                return $data->getArrayCopy();
4855
            }
4856
4857 42
            if ($data instanceof \Generator) {
4858
                return static::createFromGeneratorImmutable($data)->getArray();
4859
            }
4860
4861 42
            if ($data instanceof \Traversable) {
4862
                return static::createFromObject($data)->getArray();
4863
            }
4864
4865 42
            if ($data instanceof \JsonSerializable) {
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
4866
                return (array) $data->jsonSerialize();
4867
            }
4868
4869 42
            if (\method_exists($data, '__toArray')) {
4870
                return (array) $data->__toArray();
4871
            }
4872
4873 42
            if (\method_exists($data, '__toString')) {
4874
                return [(string) $data];
4875
            }
4876
        }
4877
4878 47
        if (\is_callable($data)) {
4879 40
            $this->generator = new ArrayyRewindableGenerator($data);
4880
4881 40
            return [];
4882
        }
4883
4884 9
        if (\is_scalar($data)) {
4885 7
            return [$data];
4886
        }
4887
4888 2
        return null;
4889
    }
4890
4891
    /**
4892
     * Internal mechanics of remove method.
4893
     *
4894
     * @param mixed $key
4895
     *
4896
     * @return bool
4897
     */
4898 18
    protected function internalRemove($key): bool
4899
    {
4900 18
        $this->generatorToArray();
4901
4902
        if (
4903 18
            $this->pathSeparator
4904
            &&
4905 18
            (string) $key === $key
4906
            &&
4907 18
            \strpos($key, $this->pathSeparator) !== false
4908
        ) {
4909
            $path = \explode($this->pathSeparator, (string) $key);
4910
4911
            if ($path !== false) {
4912
                // crawl though the keys
4913
                while (\count($path, \COUNT_NORMAL) > 1) {
4914
                    $key = \array_shift($path);
4915
4916
                    if (!$this->has($key)) {
4917
                        return false;
4918
                    }
4919
4920
                    $this->array = &$this->array[$key];
4921
                }
4922
4923
                $key = \array_shift($path);
4924
            }
4925
        }
4926
4927 18
        unset($this->array[$key]);
4928
4929 18
        return true;
4930
    }
4931
4932
    /**
4933
     * Internal mechanic of set method.
4934
     *
4935
     * @param int|string|null $key
4936
     * @param mixed           $value
4937
     * @param bool            $checkProperties
4938
     *
4939
     * @return bool
4940
     */
4941 865
    protected function internalSet($key, $value, $checkProperties = true): bool
4942
    {
4943
        if (
4944 865
            $checkProperties === true
4945
            &&
4946 865
            $this->properties !== []
4947
        ) {
4948 14
            if (isset($this->properties[$key]) === false) {
4949
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4950
            }
4951
4952 14
            $this->properties[$key]->checkType($value);
4953
        }
4954
4955 864
        if ($key === null) {
4956
            return false;
4957
        }
4958
4959 864
        $this->generatorToArray();
4960
4961 864
        $array = &$this->array;
4962
4963
        if (
4964 864
            $this->pathSeparator
4965
            &&
4966 864
            (string) $key === $key
4967
            &&
4968 864
            \strpos($key, $this->pathSeparator) !== false
4969
        ) {
4970 3
            $path = \explode($this->pathSeparator, (string) $key);
4971
4972 3
            if ($path !== false) {
4973
                // crawl through the keys
4974 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4975 3
                    $key = \array_shift($path);
4976
4977 3
                    $array = &$array[$key];
4978
                }
4979
4980 3
                $key = \array_shift($path);
4981
            }
4982
        }
4983
4984 864
        $array[$key] = $value;
4985
4986 864
        return true;
4987
    }
4988
4989
    /**
4990
     * Convert a object into an array.
4991
     *
4992
     * @param object $object
4993
     *
4994
     * @return mixed
4995
     */
4996 5
    protected static function objectToArray($object)
4997
    {
4998 5
        if (!\is_object($object)) {
4999 4
            return $object;
5000
        }
5001
5002 5
        if (\is_object($object)) {
5003 5
            $object = \get_object_vars($object);
5004
        }
5005
5006 5
        return \array_map(['static', 'objectToArray'], $object);
5007
    }
5008
5009
    /**
5010
     * sorting keys
5011
     *
5012
     * @param array      $elements
5013
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5014
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5015
     *                              <strong>SORT_NATURAL</strong></p>
5016
     *
5017
     * @return static
5018
     *                <p>(Mutable) Return this Arrayy object.</p>
5019
     */
5020 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
5021
    {
5022 18
        $direction = $this->getDirection($direction);
5023
5024 18
        switch ($direction) {
5025 18
            case 'desc':
5026
            case \SORT_DESC:
5027 6
                \krsort($elements, $strategy);
5028
5029 6
                break;
5030 13
            case 'asc':
5031 13
            case \SORT_ASC:
5032
            default:
5033 13
                \ksort($elements, $strategy);
5034
        }
5035
5036 18
        return $this;
5037
    }
5038
5039
    /**
5040
     * @param array      $elements  <p>Warning: used as reference</p>
5041
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
5042
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
5043
     *                              <strong>SORT_NATURAL</strong></p>
5044
     * @param bool       $keepKeys
5045
     *
5046
     * @return static
5047
     *                <p>(Mutable) Return this Arrayy object.</p>
5048
     */
5049 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
5050
    {
5051 20
        $direction = $this->getDirection($direction);
5052
5053 20
        if (!$strategy) {
5054 20
            $strategy = \SORT_REGULAR;
5055
        }
5056
5057 20
        switch ($direction) {
5058 20
            case 'desc':
5059
            case \SORT_DESC:
5060 9
                if ($keepKeys) {
5061 5
                    \arsort($elements, $strategy);
5062
                } else {
5063 4
                    \rsort($elements, $strategy);
5064
                }
5065
5066 9
                break;
5067 11
            case 'asc':
5068 11
            case \SORT_ASC:
5069
            default:
5070 11
                if ($keepKeys) {
5071 4
                    \asort($elements, $strategy);
5072
                } else {
5073 7
                    \sort($elements, $strategy);
5074
                }
5075
        }
5076
5077 20
        return $this;
5078
    }
5079
5080
    /**
5081
     * @return bool
5082
     *
5083
     * @noinspection ReturnTypeCanBeDeclaredInspection
5084
     */
5085 909
    private function generatorToArray()
5086
    {
5087 909
        if ($this->generator) {
5088 2
            $this->array = $this->getArray();
5089 2
            $this->generator = null;
5090
5091 2
            return true;
5092
        }
5093
5094 909
        return false;
5095
    }
5096
5097
    /**
5098
     * @return Property[]
5099
     *
5100
     * @noinspection ReturnTypeCanBeDeclaredInspection
5101
     */
5102 16
    private function getPropertiesFromPhpDoc()
5103
    {
5104 16
        static $PROPERTY_CACHE = [];
5105 16
        $cacheKey = 'Class::' . static::class;
5106
5107 16
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5108 15
            return $PROPERTY_CACHE[$cacheKey];
5109
        }
5110
5111
        // init
5112 2
        $properties = [];
5113
5114 2
        $reflector = new \ReflectionClass($this);
5115 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5116 2
        $docComment = $reflector->getDocComment();
5117 2
        if ($docComment) {
5118 2
            $docblock = $factory->create($docComment);
5119
            /** @var \phpDocumentor\Reflection\DocBlock\Tags\Property $tag */
5120 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5121 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
5122
            }
5123
        }
5124
5125 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5126
    }
5127
}
5128