Completed
Push — master ( c5400d...90bacc )
by Lars
03:23 queued 01:12
created

Arrayy::map()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.0729

Importance

Changes 0
Metric Value
cc 5
nc 1
nop 3
dl 0
loc 27
ccs 12
cts 14
cp 0.8571
crap 5.0729
rs 9.1768
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 969
    public function __construct(
76
        $data = [],
77
        string $iteratorClass = ArrayyIterator::class,
78
        bool $checkForMissingPropertiesInConstructor = true
79
    ) {
80 969
        $data = $this->fallbackForArray($data);
81
82
        // used only for serialize + unserialize, all other methods are overwritten
83 967
        parent::__construct([], 0, $iteratorClass);
84
85 967
        $checkForMissingPropertiesInConstructor = $this->checkForMissingPropertiesInConstructor === true
86
                                                  &&
87 967
                                                  $checkForMissingPropertiesInConstructor === true;
88
89
        if (
90 967
            $this->checkPropertyTypes === true
91
            ||
92 967
            $checkForMissingPropertiesInConstructor === true
93
        ) {
94 15
            $this->properties = $this->getPropertiesFromPhpDoc();
95
        }
96
97
        if (
98 967
            $this->checkPropertiesMismatchInConstructor === true
99
            &&
100 967
            \count($data) !== 0
101
            &&
102 967
            \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 966
        foreach ($data as $key => &$value) {
109 841
            $this->internalSet(
110 841
                $key,
111 841
                $value,
112 841
                $checkForMissingPropertiesInConstructor
113
            );
114
        }
115
116 962
        $this->setIteratorClass($iteratorClass);
117 962
    }
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)) {
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])
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 1
    public function getArrayCopy(): array
327
    {
328 1
        $this->generatorToArray();
329
330 1
        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 (\is_bool($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
            \is_string($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
            \is_string($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
        return parent::serialize();
560
    }
561
562
    /**
563
     * Sets the iterator classname for the current "Arrayy"-object.
564
     *
565
     * @param string $class
566
     *
567
     * @throws \InvalidArgumentException
568
     */
569 962
    public function setIteratorClass($class)
570
    {
571 962
        if (\class_exists($class)) {
572 962
            $this->iteratorClass = $class;
573
574 962
            return;
575
        }
576
577
        if (\strpos($class, '\\') === 0) {
578
            $class = '\\' . $class;
579
            if (\class_exists($class)) {
580
                $this->iteratorClass = $class;
581
582
                return;
583
            }
584
        }
585
586
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
587
    }
588
589
    /**
590
     * Sort the entries with a user-defined comparison function and maintain key association.
591
     *
592
     * @param callable $function
593
     *
594
     * @throws \InvalidArgumentException
595
     *
596
     * @return static
597
     *                <p>(Mutable) Return this Arrayy object.</p>
598
     */
599 View Code Duplication
    public function uasort($function): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
600
    {
601
        if (!\is_callable($function)) {
602
            throw new \InvalidArgumentException(
603
                'Passed function must be callable'
604
            );
605
        }
606
607
        $this->generatorToArray();
608
609
        \uasort($this->array, $function);
610
611
        return $this;
612
    }
613
614
    /**
615
     * Sort the entries by keys using a user-defined comparison function.
616
     *
617
     * @param callable $function
618
     *
619
     * @throws \InvalidArgumentException
620
     *
621
     * @return static
622
     *                <p>(Mutable) Return this Arrayy object.</p>
623
     */
624 5
    public function uksort($function): self
625
    {
626 5
        return $this->customSortKeys($function);
627
    }
628
629
    /**
630
     * Unserialize an string and return the instance of the "Arrayy"-class.
631
     *
632
     * @param string $string
633
     *
634
     * @return static
635
     */
636 2
    public function unserialize($string): self
637
    {
638 2
        parent::unserialize($string);
639
640 2
        return $this;
641
    }
642
643
    /**
644
     * Append a (key) + values to the current array.
645
     *
646
     * @param array $values
647
     * @param mixed $key
648
     *
649
     * @return static
650
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
651
     */
652 1
    public function appendArrayValues(array $values, $key = null): self
653
    {
654 1
        $this->generatorToArray();
655
656 1
        if ($key !== null) {
657
            if (
658 1
                isset($this->array[$key])
659
                &&
660 1
                \is_array($this->array[$key])
661
            ) {
662 1
                foreach ($values as $value) {
663 1
                    $this->array[$key][] = $value;
664
                }
665
            } else {
666
                foreach ($values as $value) {
667 1
                    $this->array[$key] = $value;
668
                }
669
            }
670
        } else {
671
            foreach ($values as $value) {
672
                $this->array[] = $value;
673
            }
674
        }
675
676 1
        return $this;
677
    }
678
679
    /**
680
     * Add a suffix to each key.
681
     *
682
     * @param mixed $prefix
683
     *
684
     * @return static
685
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
686
     */
687 10 View Code Duplication
    public function appendToEachKey($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
688
    {
689
        // init
690 10
        $result = [];
691
692 10
        foreach ($this->getGenerator() as $key => $item) {
693 9
            if ($item instanceof self) {
694
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
695 9
            } elseif (\is_array($item)) {
696
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
697
                    ->appendToEachKey($prefix)
698
                    ->toArray();
699
            } else {
700 9
                $result[$prefix . $key] = $item;
701
            }
702
        }
703
704 10
        return self::create($result, $this->iteratorClass, false);
705
    }
706
707
    /**
708
     * Add a prefix to each value.
709
     *
710
     * @param mixed $prefix
711
     *
712
     * @return static
713
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
714
     */
715 10 View Code Duplication
    public function appendToEachValue($prefix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
716
    {
717
        // init
718 10
        $result = [];
719
720 10
        foreach ($this->getGenerator() as $key => $item) {
721 9
            if ($item instanceof self) {
722
                $result[$key] = $item->appendToEachValue($prefix);
723 9
            } elseif (\is_array($item)) {
724
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
725 9
            } elseif (\is_object($item)) {
726 1
                $result[$key] = $item;
727
            } else {
728 9
                $result[$key] = $prefix . $item;
729
            }
730
        }
731
732 10
        return self::create($result, $this->iteratorClass, false);
733
    }
734
735
    /**
736
     * Sort an array in reverse order and maintain index association.
737
     *
738
     * @return static
739
     *                <p>(Mutable) Return this Arrayy object.</p>
740
     */
741 10
    public function arsort(): self
742
    {
743 10
        $this->generatorToArray();
744
745 10
        \arsort($this->array);
746
747 10
        return $this;
748
    }
749
750
    /**
751
     * Iterate over the current array and execute a callback for each loop.
752
     *
753
     * @param \Closure $closure
754
     *
755
     * @return static
756
     *                <p>(Immutable)</p>
757
     */
758 2
    public function at(\Closure $closure): self
759
    {
760 2
        $arrayy = clone $this;
761
762 2
        foreach ($arrayy->getGenerator() as $key => $value) {
763 2
            $closure($value, $key);
764
        }
765
766 2
        return static::create(
767 2
            $arrayy->toArray(),
768 2
            $this->iteratorClass,
769 2
            false
770
        );
771
    }
772
773
    /**
774
     * Returns the average value of the current array.
775
     *
776
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
777
     *
778
     * @return float|int
779
     *                   <p>The average value.</p>
780
     */
781 10
    public function average($decimals = 0)
782
    {
783 10
        $count = \count($this->getArray(), \COUNT_NORMAL);
784
785 10
        if (!$count) {
786 2
            return 0;
787
        }
788
789 8
        if (!\is_int($decimals)) {
790 3
            $decimals = 0;
791
        }
792
793 8
        return \round(\array_sum($this->getArray()) / $count, $decimals);
794
    }
795
796
    /**
797
     * Changes all keys in an array.
798
     *
799
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
800
     *                  or <strong>CASE_LOWER</strong> (default)</p>
801
     *
802
     * @return static
803
     *                <p>(Immutable)</p>
804
     */
805 1
    public function changeKeyCase(int $case = \CASE_LOWER): self
806
    {
807
        if (
808 1
            $case !== \CASE_LOWER
809
            &&
810 1
            $case !== \CASE_UPPER
811
        ) {
812
            $case = \CASE_LOWER;
813
        }
814
815 1
        $return = [];
816 1
        foreach ($this->getGenerator() as $key => $value) {
817 1
            if ($case === \CASE_LOWER) {
818 1
                $key = \mb_strtolower((string) $key);
819
            } else {
820 1
                $key = \mb_strtoupper((string) $key);
821
            }
822
823 1
            $return[$key] = $value;
824
        }
825
826 1
        return static::create(
827 1
            $return,
828 1
            $this->iteratorClass,
829 1
            false
830
        );
831
    }
832
833
    /**
834
     * Change the path separator of the array wrapper.
835
     *
836
     * By default, the separator is: "."
837
     *
838
     * @param string $separator <p>Separator to set.</p>
839
     *
840
     * @return static
841
     *                <p>Mutable</p>
842
     */
843 10
    public function changeSeparator($separator): self
844
    {
845 10
        $this->pathSeparator = $separator;
846
847 10
        return $this;
848
    }
849
850
    /**
851
     * Create a chunked version of the current array.
852
     *
853
     * @param int  $size         <p>Size of each chunk.</p>
854
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
855
     *
856
     * @return static
857
     *                <p>(Immutable) A new array of chunks from the original array.</p>
858
     */
859 4
    public function chunk($size, $preserveKeys = false): self
860
    {
861 4
        return static::create(
862 4
            \array_chunk($this->getArray(), $size, $preserveKeys),
863 4
            $this->iteratorClass,
864 4
            false
865
        );
866
    }
867
868
    /**
869
     * Clean all falsy values from the current array.
870
     *
871
     * @return static
872
     *                <p>(Immutable)</p>
873
     */
874 8
    public function clean(): self
875
    {
876 8
        return $this->filter(
877
            static function ($value) {
878 7
                return (bool) $value;
879 8
            }
880
        );
881
    }
882
883
    /**
884
     * WARNING!!! -> Clear the current array.
885
     *
886
     * @return static
887
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
888
     */
889 4
    public function clear(): self
890
    {
891 4
        $this->array = [];
892 4
        $this->generator = null;
893
894 4
        return $this;
895
    }
896
897
    /**
898
     * Check if an item is in the current array.
899
     *
900
     * @param float|int|string $value
901
     * @param bool             $recursive
902
     * @param bool             $strict
903
     *
904
     * @return bool
905
     */
906 22
    public function contains($value, bool $recursive = false, bool $strict = true): bool
907
    {
908 22
        if ($recursive === true) {
909 18
            return $this->in_array_recursive($value, $this->getArray(), $strict);
910
        }
911
912 13
        foreach ($this->getGenerator() as $valueFromArray) {
913 10
            if ($strict) {
914 10
                if ($value === $valueFromArray) {
915 10
                    return true;
916
                }
917
            } else {
918
                /** @noinspection NestedPositiveIfStatementsInspection */
919
                if ($value == $valueFromArray) {
920 6
                    return true;
921
                }
922
            }
923
        }
924
925 6
        return false;
926
    }
927
928
    /**
929
     * Check if an (case-insensitive) string is in the current array.
930
     *
931
     * @param string $value
932
     * @param bool   $recursive
933
     *
934
     * @return bool
935
     */
936 26
    public function containsCaseInsensitive($value, $recursive = false): bool
937
    {
938 26
        if ($recursive === true) {
939 26
            foreach ($this->getGenerator() as $key => $valueTmp) {
940 22
                if (\is_array($valueTmp)) {
941 5
                    $return = (new self($valueTmp))->containsCaseInsensitive($value, $recursive);
942 5
                    if ($return === true) {
943 5
                        return $return;
944
                    }
945 22
                } elseif (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
946 22
                    return true;
947
                }
948
            }
949
950 10
            return false;
951
        }
952
953 13
        foreach ($this->getGenerator() as $key => $valueTmp) {
954 11
            if (\mb_strtoupper((string) $valueTmp) === \mb_strtoupper((string) $value)) {
955 11
                return true;
956
            }
957
        }
958
959 5
        return false;
960
    }
961
962
    /**
963
     * Check if the given key/index exists in the array.
964
     *
965
     * @param int|string $key <p>key/index to search for</p>
966
     *
967
     * @return bool
968
     *              <p>Returns true if the given key/index exists in the array, false otherwise.</p>
969
     */
970 4
    public function containsKey($key): bool
971
    {
972 4
        return $this->offsetExists($key);
973
    }
974
975
    /**
976
     * Check if all given needles are present in the array as key/index.
977
     *
978
     * @param array $needles   <p>The keys you are searching for.</p>
979
     * @param bool  $recursive
980
     *
981
     * @return bool
982
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
983
     */
984 2
    public function containsKeys(array $needles, $recursive = false): bool
985
    {
986 2
        if ($recursive === true) {
987
            return
988 2
                \count(
989 2
                    \array_intersect(
990 2
                        $needles,
991 2
                        $this->keys(true)->getArray()
992
                    ),
993 2
                    \COUNT_RECURSIVE
994
                )
995
                ===
996 2
                \count(
997 2
                    $needles,
998 2
                    \COUNT_RECURSIVE
999
                );
1000
        }
1001
1002 1
        return \count(
1003 1
            \array_intersect($needles, $this->keys()->getArray()),
1004 1
            \COUNT_NORMAL
1005
               )
1006
               ===
1007 1
               \count(
1008 1
                   $needles,
1009 1
                   \COUNT_NORMAL
1010
               );
1011
    }
1012
1013
    /**
1014
     * Check if all given needles are present in the array as key/index.
1015
     *
1016
     * @param array $needles <p>The keys you are searching for.</p>
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 1
    public function containsKeysRecursive(array $needles): bool
1022
    {
1023 1
        return $this->containsKeys($needles, true);
1024
    }
1025
1026
    /**
1027
     * alias: for "Arrayy->contains()"
1028
     *
1029
     * @param float|int|string $value
1030
     *
1031
     * @return bool
1032
     *
1033
     * @see Arrayy::contains()
1034
     */
1035 9
    public function containsValue($value): bool
1036
    {
1037 9
        return $this->contains($value);
1038
    }
1039
1040
    /**
1041
     * alias: for "Arrayy->contains($value, true)"
1042
     *
1043
     * @param float|int|string $value
1044
     *
1045
     * @return bool
1046
     *
1047
     * @see Arrayy::contains()
1048
     */
1049 18
    public function containsValueRecursive($value): bool
1050
    {
1051 18
        return $this->contains($value, true);
1052
    }
1053
1054
    /**
1055
     * Check if all given needles are present in the array.
1056
     *
1057
     * @param array $needles
1058
     *
1059
     * @return bool
1060
     *              <p>Returns true if all the given values exists in the array, false otherwise.</p>
1061
     */
1062 1
    public function containsValues(array $needles): bool
1063
    {
1064 1
        return \count(\array_intersect($needles, $this->getArray()), \COUNT_NORMAL)
1065
               ===
1066 1
               \count($needles, \COUNT_NORMAL);
1067
    }
1068
1069
    /**
1070
     * Counts all the values of an array
1071
     *
1072
     * @see http://php.net/manual/en/function.array-count-values.php
1073
     *
1074
     * @return static
1075
     *                <p>
1076
     *                (Immutable)
1077
     *                An associative Arrayy-object of values from input as
1078
     *                keys and their count as value.
1079
     *                </p>
1080
     */
1081 7
    public function countValues(): self
1082
    {
1083 7
        return new static(\array_count_values($this->getArray()));
1084
    }
1085
1086
    /**
1087
     * Creates an Arrayy object.
1088
     *
1089
     * @param mixed  $array
1090
     * @param string $iteratorClass
1091
     * @param bool   $checkForMissingPropertiesInConstructor
1092
     *
1093
     * @return static
1094
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1095
     */
1096 564
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1097
    {
1098 564
        return new static(
1099 564
            $array,
1100 564
            $iteratorClass,
1101 564
            $checkForMissingPropertiesInConstructor
1102
        );
1103
    }
1104
1105
    /**
1106
     * WARNING: Creates an Arrayy object by reference.
1107
     *
1108
     * @param array $array
1109
     *
1110
     * @return static
1111
     *                <p>(Mutable) Return this Arrayy object.</p>
1112
     */
1113 1
    public function createByReference(array &$array = []): self
1114
    {
1115 1
        $array = $this->fallbackForArray($array);
1116
1117 1
        $this->array = &$array;
1118
1119 1
        return $this;
1120
    }
1121
1122
    /**
1123
     * Create an new instance from a callable function which will return an Generator.
1124
     *
1125
     * @param callable():Generator $generatorFunction
0 ignored issues
show
Documentation introduced by
The doc-type callable():Generator could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1126
     *
1127
     * @return static
1128
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1129
     */
1130 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1131
    {
1132 5
        $arrayy = new static($generatorFunction);
1133
1134 5
        return $arrayy;
1135
    }
1136
1137
    /**
1138
     * Create an new instance filled with a copy of values from a "Generator"-object.
1139
     *
1140
     * @param \Generator $generator
1141
     *
1142
     * @return static
1143
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1144
     */
1145 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1146
    {
1147 4
        return new static(\iterator_to_array($generator, true));
1148
    }
1149
1150
    /**
1151
     * Create an new Arrayy object via JSON.
1152
     *
1153
     * @param string $json
1154
     *
1155
     * @return static
1156
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1157
     */
1158 5
    public static function createFromJson(string $json): self
1159
    {
1160 5
        return static::create(\json_decode($json, true));
1161
    }
1162
1163
    /**
1164
     * Create an new instance filled with values from an object that is iterable.
1165
     *
1166
     * @param \Traversable $object <p>iterable object</p>
1167
     *
1168
     * @return static
1169
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1170
     */
1171 4
    public static function createFromObject(\Traversable $object): self
1172
    {
1173
        // init
1174 4
        $array = new static();
1175
1176 4
        if ($object instanceof self) {
1177 4
            $objectArray = $object->getGenerator();
1178
        } else {
1179
            $objectArray = $object;
1180
        }
1181
1182 4
        foreach ($objectArray as $key => $value) {
1183 3
            $array[$key] = $value;
1184
        }
1185
1186 4
        return $array;
1187
    }
1188
1189
    /**
1190
     * Create an new instance filled with values from an object.
1191
     *
1192
     * @param object $object
1193
     *
1194
     * @return static
1195
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1196
     */
1197 5
    public static function createFromObjectVars($object): self
1198
    {
1199 5
        return new static(self::objectToArray($object));
1200
    }
1201
1202
    /**
1203
     * Create an new Arrayy object via string.
1204
     *
1205
     * @param string      $str       <p>The input string.</p>
1206
     * @param string|null $delimiter <p>The boundary string.</p>
1207
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1208
     *                               used.</p>
1209
     *
1210
     * @return static
1211
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1212
     */
1213 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1214
    {
1215 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...
1216 1
            \preg_match_all($regEx, $str, $array);
1217
1218 1
            if (!empty($array)) {
1219 1
                $array = $array[0];
1220
            }
1221
        } else {
1222
            /** @noinspection NestedPositiveIfStatementsInspection */
1223 9
            if ($delimiter !== null) {
1224 7
                $array = \explode($delimiter, $str);
1225
            } else {
1226 2
                $array = [$str];
1227
            }
1228
        }
1229
1230
        // trim all string in the array
1231 10
        \array_walk(
1232 10
            $array,
1233
            static function (&$val) {
1234 10
                if (\is_string($val)) {
1235 10
                    $val = \trim($val);
1236
                }
1237 10
            }
1238
        );
1239
1240 10
        return static::create($array);
1241
    }
1242
1243
    /**
1244
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1245
     *
1246
     * @param \Traversable $traversable
1247
     *
1248
     * @return static
1249
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1250
     */
1251
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1252
    {
1253
        return new static(\iterator_to_array($traversable, true));
1254
    }
1255
1256
    /**
1257
     * Create an new instance containing a range of elements.
1258
     *
1259
     * @param mixed $low  <p>First value of the sequence.</p>
1260
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1261
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1262
     *
1263
     * @return static
1264
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1265
     */
1266 2
    public static function createWithRange($low, $high, int $step = 1): self
1267
    {
1268 2
        return static::create(\range($low, $high, $step));
1269
    }
1270
1271
    /**
1272
     * Custom sort by index via "uksort".
1273
     *
1274
     * @see http://php.net/manual/en/function.uksort.php
1275
     *
1276
     * @param callable $function
1277
     *
1278
     * @throws \InvalidArgumentException
1279
     *
1280
     * @return static
1281
     *                <p>(Mutable) Return this Arrayy object.</p>
1282
     */
1283 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...
1284
    {
1285 5
        if (!\is_callable($function)) {
1286
            throw new \InvalidArgumentException(
1287
                'Passed function must be callable'
1288
            );
1289
        }
1290
1291 5
        $this->generatorToArray();
1292
1293 5
        \uksort($this->array, $function);
1294
1295 5
        return $this;
1296
    }
1297
1298
    /**
1299
     * Custom sort by value via "usort".
1300
     *
1301
     * @see http://php.net/manual/en/function.usort.php
1302
     *
1303
     * @param callable $function
1304
     *
1305
     * @throws \InvalidArgumentException
1306
     *
1307
     * @return static
1308
     *                <p>(Mutable) Return this Arrayy object.</p>
1309
     */
1310 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...
1311
    {
1312 5
        if (!\is_callable($function)) {
1313
            throw new \InvalidArgumentException(
1314
                'Passed function must be callable'
1315
            );
1316
        }
1317
1318 5
        $this->generatorToArray();
1319
1320 5
        \usort($this->array, $function);
1321
1322 5
        return $this;
1323
    }
1324
1325
    /**
1326
     * Delete the given key or keys.
1327
     *
1328
     * @param int|int[]|string|string[] $keyOrKeys
1329
     */
1330 4
    public function delete($keyOrKeys)
1331
    {
1332 4
        $keyOrKeys = (array) $keyOrKeys;
1333
1334 4
        foreach ($keyOrKeys as $key) {
1335 4
            $this->offsetUnset($key);
1336
        }
1337 4
    }
1338
1339
    /**
1340
     * Return values that are only in the current array.
1341
     *
1342
     * @param array $array
1343
     *
1344
     * @return static
1345
     *                <p>(Immutable)</p>
1346
     */
1347 12
    public function diff(array $array = []): self
1348
    {
1349 12
        return static::create(
1350 12
            \array_diff($this->getArray(), $array),
1351 12
            $this->iteratorClass,
1352 12
            false
1353
        );
1354
    }
1355
1356
    /**
1357
     * Return values that are only in the current multi-dimensional array.
1358
     *
1359
     * @param array      $array
1360
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1361
     *
1362
     * @return static
1363
     *                <p>(Immutable)</p>
1364
     */
1365 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null): self
1366
    {
1367
        // init
1368 1
        $result = [];
1369
1370
        if (
1371 1
            $helperVariableForRecursion !== null
1372
            &&
1373 1
            \is_array($helperVariableForRecursion)
1374
        ) {
1375
            $arrayForTheLoop = $helperVariableForRecursion;
1376
        } else {
1377 1
            $arrayForTheLoop = $this->getGenerator();
1378
        }
1379
1380 1
        foreach ($arrayForTheLoop as $key => $value) {
1381 1
            if ($value instanceof self) {
1382
                $value = $value->getArray();
1383
            }
1384
1385 1
            if (\array_key_exists($key, $array)) {
1386 1
                if ($value !== $array[$key]) {
1387 1
                    $result[$key] = $value;
1388
                }
1389
            } else {
1390 1
                $result[$key] = $value;
1391
            }
1392
        }
1393
1394 1
        return static::create(
1395 1
            $result,
1396 1
            $this->iteratorClass,
1397 1
            false
1398
        );
1399
    }
1400
1401
    /**
1402
     * Return values that are only in the new $array.
1403
     *
1404
     * @param array $array
1405
     *
1406
     * @return static
1407
     *                <p>(Immutable)</p>
1408
     */
1409 8
    public function diffReverse(array $array = []): self
1410
    {
1411 8
        return static::create(
1412 8
            \array_diff($array, $this->getArray()),
1413 8
            $this->iteratorClass,
1414 8
            false
1415
        );
1416
    }
1417
1418
    /**
1419
     * Divide an array into two arrays. One with keys and the other with values.
1420
     *
1421
     * @return static
1422
     *                <p>(Immutable)</p>
1423
     */
1424 1
    public function divide(): self
1425
    {
1426 1
        return static::create(
1427
            [
1428 1
                $this->keys(),
1429 1
                $this->values(),
1430
            ],
1431 1
            $this->iteratorClass,
1432 1
            false
1433
        );
1434
    }
1435
1436
    /**
1437
     * Iterate over the current array and modify the array's value.
1438
     *
1439
     * @param \Closure $closure
1440
     *
1441
     * @return static
1442
     *                <p>(Immutable)</p>
1443
     */
1444 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...
1445
    {
1446
        // init
1447 4
        $array = [];
1448
1449 4
        foreach ($this->getGenerator() as $key => $value) {
1450 4
            $array[$key] = $closure($value, $key);
1451
        }
1452
1453 4
        return static::create(
1454 4
            $array,
1455 4
            $this->iteratorClass,
1456 4
            false
1457
        );
1458
    }
1459
1460
    /**
1461
     * Check if a value is in the current array using a closure.
1462
     *
1463
     * @param \Closure $closure
1464
     *
1465
     * @return bool
1466
     *              <p>Returns true if the given value is found, false otherwise.</p>
1467
     */
1468 4
    public function exists(\Closure $closure): bool
1469
    {
1470
        // init
1471 4
        $isExists = false;
1472
1473 4
        foreach ($this->getGenerator() as $key => $value) {
1474 3
            if ($closure($value, $key)) {
1475 1
                $isExists = true;
1476
1477 3
                break;
1478
            }
1479
        }
1480
1481 4
        return $isExists;
1482
    }
1483
1484
    /**
1485
     * Fill the array until "$num" with "$default" values.
1486
     *
1487
     * @param int   $num
1488
     * @param mixed $default
1489
     *
1490
     * @return static
1491
     *                <p>(Immutable)</p>
1492
     */
1493 8
    public function fillWithDefaults(int $num, $default = null): self
1494
    {
1495 8
        if ($num < 0) {
1496 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1497
        }
1498
1499 7
        $this->generatorToArray();
1500
1501 7
        $tmpArray = $this->array;
1502
1503 7
        $count = \count($tmpArray);
1504
1505 7
        while ($count < $num) {
1506 4
            $tmpArray[] = $default;
1507 4
            ++$count;
1508
        }
1509
1510 7
        return static::create(
1511 7
            $tmpArray,
1512 7
            $this->iteratorClass,
1513 7
            false
1514
        );
1515
    }
1516
1517
    /**
1518
     * Find all items in an array that pass the truth test.
1519
     *
1520
     * @param \Closure|null $closure [optional] <p>
1521
     *                               The callback function to use
1522
     *                               </p>
1523
     *                               <p>
1524
     *                               If no callback is supplied, all entries of
1525
     *                               input equal to false (see
1526
     *                               converting to
1527
     *                               boolean) will be removed.
1528
     *                               </p>
1529
     *                               * @param int $flag [optional] <p>
1530
     *                               Flag determining what arguments are sent to <i>callback</i>:
1531
     *                               </p><ul>
1532
     *                               <li>
1533
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1534
     *                               to <i>callback</i> instead of the value</span>
1535
     *                               </li>
1536
     *                               <li>
1537
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1538
     *                               arguments to <i>callback</i> instead of the value</span>
1539
     *                               </li>
1540
     *                               </ul>
1541
     *
1542
     * @return static
1543
     *                <p>(Immutable)</p>
1544
     */
1545 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1546
    {
1547 11
        if (!$closure) {
1548 1
            return $this->clean();
1549
        }
1550
1551 11
        return static::create(
1552 11
            \array_filter($this->getArray(), $closure, $flag),
1553 11
            $this->iteratorClass,
1554 11
            false
1555
        );
1556
    }
1557
1558
    /**
1559
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1560
     * property within that.
1561
     *
1562
     * @param string          $property
1563
     * @param string|string[] $value
1564
     * @param string          $comparisonOp
1565
     *                                      <p>
1566
     *                                      'eq' (equals),<br />
1567
     *                                      'gt' (greater),<br />
1568
     *                                      'gte' || 'ge' (greater or equals),<br />
1569
     *                                      'lt' (less),<br />
1570
     *                                      'lte' || 'le' (less or equals),<br />
1571
     *                                      'ne' (not equals),<br />
1572
     *                                      'contains',<br />
1573
     *                                      'notContains',<br />
1574
     *                                      'newer' (via strtotime),<br />
1575
     *                                      'older' (via strtotime),<br />
1576
     *                                      </p>
1577
     *
1578
     * @return static
1579
     *                <p>(Immutable)</p>
1580
     */
1581 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1582
    {
1583 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...
1584 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1585
        }
1586
1587
        $ops = [
1588
            'eq' => static function ($item, $prop, $value) {
1589 1
                return $item[$prop] === $value;
1590 1
            },
1591
            'gt' => static function ($item, $prop, $value) {
1592
                return $item[$prop] > $value;
1593 1
            },
1594
            'ge' => static function ($item, $prop, $value) {
1595
                return $item[$prop] >= $value;
1596 1
            },
1597
            'gte' => static function ($item, $prop, $value) {
1598
                return $item[$prop] >= $value;
1599 1
            },
1600
            'lt' => static function ($item, $prop, $value) {
1601 1
                return $item[$prop] < $value;
1602 1
            },
1603
            'le' => static function ($item, $prop, $value) {
1604
                return $item[$prop] <= $value;
1605 1
            },
1606
            'lte' => static function ($item, $prop, $value) {
1607
                return $item[$prop] <= $value;
1608 1
            },
1609
            'ne' => static function ($item, $prop, $value) {
1610
                return $item[$prop] !== $value;
1611 1
            },
1612
            'contains' => static function ($item, $prop, $value) {
1613 1
                return \in_array($item[$prop], (array) $value, true);
1614 1
            },
1615
            'notContains' => static function ($item, $prop, $value) {
1616
                return !\in_array($item[$prop], (array) $value, true);
1617 1
            },
1618
            'newer' => static function ($item, $prop, $value) {
1619
                return \strtotime($item[$prop]) > \strtotime($value);
1620 1
            },
1621
            'older' => static function ($item, $prop, $value) {
1622
                return \strtotime($item[$prop]) < \strtotime($value);
1623 1
            },
1624
        ];
1625
1626 1
        $result = \array_values(
1627 1
            \array_filter(
1628 1
                $this->getArray(),
1629
                static function ($item) use (
1630 1
                    $property,
1631 1
                    $value,
1632 1
                    $ops,
1633 1
                    $comparisonOp
1634
                ) {
1635 1
                    $item = (array) $item;
1636 1
                    $itemArrayy = new static($item);
1637 1
                    $item[$property] = $itemArrayy->get($property, []);
1638
1639 1
                    return $ops[$comparisonOp]($item, $property, $value);
1640 1
                }
1641
            )
1642
        );
1643
1644 1
        return static::create(
1645 1
            $result,
1646 1
            $this->iteratorClass,
1647 1
            false
1648
        );
1649
    }
1650
1651
    /**
1652
     * Find the first item in an array that passes the truth test,
1653
     *  otherwise return false
1654
     *
1655
     * @param \Closure $closure
1656
     *
1657
     * @return false|mixed
1658
     *                     <p>Return false if we did not find the value.</p>
1659
     */
1660 8
    public function find(\Closure $closure)
1661
    {
1662 8
        foreach ($this->getGenerator() as $key => $value) {
1663 6
            if ($closure($value, $key)) {
1664 6
                return $value;
1665
            }
1666
        }
1667
1668 3
        return false;
1669
    }
1670
1671
    /**
1672
     * find by ...
1673
     *
1674
     * @param string          $property
1675
     * @param string|string[] $value
1676
     * @param string          $comparisonOp
1677
     *
1678
     * @return static
1679
     *                <p>(Immutable)</p>
1680
     */
1681
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1682
    {
1683
        return $this->filterBy($property, $value, $comparisonOp);
1684
    }
1685
1686
    /**
1687
     * Get the first value from the current array.
1688
     *
1689
     * @return mixed
1690
     *               <p>Return null if there wasn't a element.</p>
1691
     */
1692 17
    public function first()
1693
    {
1694 17
        $key_first = $this->firstKey();
1695 17
        if ($key_first === null) {
1696 3
            return null;
1697
        }
1698
1699 14
        return $this->get($key_first);
1700
    }
1701
1702
    /**
1703
     * Move an array element to the first place.
1704
     *
1705
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1706
     *       loss the keys of an indexed array.
1707
     *
1708
     * @param int|string $key
1709
     *
1710
     * @return static
1711
     *                <p>(Immutable)</p>
1712
     */
1713 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...
1714
    {
1715 1
        $array = $this->getArray();
1716
1717 1
        if ($this->offsetExists($key)) {
1718 1
            $tmpValue = $this->get($key);
1719 1
            unset($array[$key]);
1720 1
            $array = [$key => $tmpValue] + $array;
1721
        }
1722
1723 1
        return static::create(
1724 1
            $array,
1725 1
            $this->iteratorClass,
1726 1
            false
1727
        );
1728
    }
1729
1730
    /**
1731
     * Move an array element to the last place.
1732
     *
1733
     * INFO: Instead of "Arrayy->moveElement()" this method will NOT
1734
     *       loss the keys of an indexed array.
1735
     *
1736
     * @param int|string $key
1737
     *
1738
     * @return static
1739
     *                <p>(Immutable)</p>
1740
     */
1741 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...
1742
    {
1743 1
        $array = $this->getArray();
1744
1745 1
        if ($this->offsetExists($key)) {
1746 1
            $tmpValue = $this->get($key);
1747 1
            unset($array[$key]);
1748 1
            $array += [$key => $tmpValue];
1749
        }
1750
1751 1
        return static::create(
1752 1
            $array,
1753 1
            $this->iteratorClass,
1754 1
            false
1755
        );
1756
    }
1757
1758
    /**
1759
     * Get the first key from the current array.
1760
     *
1761
     * @return mixed
1762
     *               <p>Return null if there wasn't a element.</p>
1763
     */
1764 24
    public function firstKey()
1765
    {
1766 24
        $this->generatorToArray();
1767
1768 24
        return \array_key_first($this->array);
1769
    }
1770
1771
    /**
1772
     * Get the most used value from the array.
1773
     *
1774
     * @return mixed
1775
     *               <p>Return null if there wasn't a element.</p>
1776
     */
1777 3
    public function mostUsedValue()
1778
    {
1779 3
        return $this->countValues()->arsort()->firstKey();
1780
    }
1781
1782
    /**
1783
     * Get the most used value from the array.
1784
     *
1785
     * @param int|null $number <p>How many values you will take?</p>
1786
     *
1787
     * @return static
1788
     *                <p>(Immutable)</p>
1789
     */
1790 3
    public function mostUsedValues(int $number = null): self
1791
    {
1792 3
        return $this->countValues()->arsort()->firstsKeys($number);
1793
    }
1794
1795
    /**
1796
     * Get the first value(s) from the current array.
1797
     * And will return an empty array if there was no first entry.
1798
     *
1799
     * @param int|null $number <p>How many values you will take?</p>
1800
     *
1801
     * @return static
1802
     *                <p>(Immutable)</p>
1803
     */
1804 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...
1805
    {
1806 3
        $arrayTmp = $this->keys()->getArray();
1807
1808 3
        if ($number === null) {
1809
            $array = (array) \array_shift($arrayTmp);
1810
        } else {
1811 3
            $number = (int) $number;
1812 3
            $array = \array_splice($arrayTmp, 0, $number);
1813
        }
1814
1815 3
        return static::create(
1816 3
            $array,
1817 3
            $this->iteratorClass,
1818 3
            false
1819
        );
1820
    }
1821
1822
    /**
1823
     * Get the first value(s) from the current array.
1824
     * And will return an empty array if there was no first entry.
1825
     *
1826
     * @param int|null $number <p>How many values you will take?</p>
1827
     *
1828
     * @return static
1829
     *                <p>(Immutable)</p>
1830
     */
1831 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...
1832
    {
1833 36
        $arrayTmp = $this->getArray();
1834
1835 36
        if ($number === null) {
1836 14
            $array = (array) \array_shift($arrayTmp);
1837
        } else {
1838 22
            $number = (int) $number;
1839 22
            $array = \array_splice($arrayTmp, 0, $number);
1840
        }
1841
1842 36
        return static::create(
1843 36
            $array,
1844 36
            $this->iteratorClass,
1845 36
            false
1846
        );
1847
    }
1848
1849
    /**
1850
     * Get and rmove the first value(s) from the current array.
1851
     * And will return an empty array if there was no first entry.
1852
     *
1853
     * @param int|null $number <p>How many values you will take?</p>
1854
     *
1855
     * @return static
1856
     *                <p>(Mutable)</p>
1857
     */
1858 34
    public function firstsMutable(int $number = null): self
1859
    {
1860 34
        $this->generatorToArray();
1861
1862 34
        if ($number === null) {
1863 19
            $this->array = (array) \array_shift($this->array);
1864
        } else {
1865 15
            $number = (int) $number;
1866 15
            $this->array = \array_splice($this->array, 0, $number);
1867
        }
1868
1869 34
        return $this;
1870
    }
1871
1872
    /**
1873
     * Exchanges all keys with their associated values in an array.
1874
     *
1875
     * @return static
1876
     *                <p>(Immutable)</p>
1877
     */
1878 1
    public function flip(): self
1879
    {
1880 1
        return static::create(
1881 1
            \array_flip($this->getArray()),
1882 1
            $this->iteratorClass,
1883 1
            false
1884
        );
1885
    }
1886
1887
    /**
1888
     * Get a value from an array (optional using dot-notation).
1889
     *
1890
     * @param mixed $key      <p>The key to look for.</p>
1891
     * @param mixed $fallback <p>Value to fallback to.</p>
1892
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1893
     *                        class.</p>
1894
     *
1895
     * @return mixed|static
1896
     */
1897 101
    public function get($key, $fallback = null, array $array = null)
1898
    {
1899 101
        if ($array !== null) {
1900 5
            $usedArray = $array;
1901
        } else {
1902 96
            $this->generatorToArray();
1903
1904 96
            $usedArray = $this->array;
1905
        }
1906
1907 101
        if ($key === null) {
1908 1
            return static::create(
1909 1
                $usedArray,
1910 1
                $this->iteratorClass,
1911 1
                false
1912
            );
1913
        }
1914
1915
        // php cast "bool"-index into "int"-index
1916 101
        if ((bool) $key === $key) {
1917 3
            $key = (int) $key;
1918
        }
1919
1920 101
        if (\array_key_exists($key, $usedArray) === true) {
1921 90
            if (\is_array($usedArray[$key])) {
1922 10
                return static::create(
1923 10
                    $usedArray[$key],
1924 10
                    $this->iteratorClass,
1925 10
                    false
1926
                );
1927
            }
1928
1929 82
            return $usedArray[$key];
1930
        }
1931
1932
        // crawl through array, get key according to object or not
1933 24
        $usePath = false;
1934
        if (
1935 24
            $this->pathSeparator
1936
            &&
1937 24
            \is_string($key)
1938
            &&
1939 24
            \strpos($key, $this->pathSeparator) !== false
1940
        ) {
1941 7
            $segments = \explode($this->pathSeparator, (string) $key);
1942 7
            if ($segments !== false) {
1943 7
                $usePath = true;
1944
1945 7
                foreach ($segments as $segment) {
1946
                    if (
1947
                        (
1948 7
                            \is_array($usedArray)
1949
                            ||
1950 7
                            $usedArray instanceof \ArrayAccess
1951
                        )
1952
                        &&
1953 7
                        isset($usedArray[$segment])
1954
                    ) {
1955 7
                        $usedArray = $usedArray[$segment];
1956
1957 7
                        continue;
1958
                    }
1959
1960
                    if (
1961 6
                        \is_object($usedArray)
1962
                        &&
1963 6
                        \property_exists($usedArray, $segment)
1964
                    ) {
1965 1
                        $usedArray = $usedArray->{$segment};
1966
1967 1
                        continue;
1968
                    }
1969
1970 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1971
                }
1972
            }
1973
        }
1974
1975 24
        if (!$usePath && !isset($usedArray[$key])) {
1976 17
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1977
        }
1978
1979 7
        if (\is_array($usedArray)) {
1980 1
            return static::create(
1981 1
                $usedArray,
1982 1
                $this->iteratorClass,
1983 1
                false
1984
            );
1985
        }
1986
1987 7
        return $usedArray;
1988
    }
1989
1990
    /**
1991
     * alias: for "Arrayy->getArray()"
1992
     *
1993
     * @return array
1994
     *
1995
     * @see Arrayy::getArray()
1996
     */
1997
    public function getAll(): array
1998
    {
1999
        return $this->getArray();
2000
    }
2001
2002
    /**
2003
     * Get the current array from the "Arrayy"-object.
2004
     *
2005
     * @return array
2006
     */
2007 781
    public function getArray(): array
2008
    {
2009
        // init
2010 781
        $array = [];
2011
2012 781
        foreach ($this->getGenerator() as $key => $value) {
2013 675
            $array[$key] = $value;
2014
        }
2015
2016 781
        return $array;
2017
    }
2018
2019
    /**
2020
     * Returns the values from a single column of the input array, identified by
2021
     * the $columnKey, can be used to extract data-columns from multi-arrays.
2022
     *
2023
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
2024
     * array by the values from the $indexKey column in the input array.
2025
     *
2026
     * @param mixed $columnKey
2027
     * @param mixed $indexKey
2028
     *
2029
     * @return static
2030
     *                <p>(Immutable)</p>
2031
     */
2032 1
    public function getColumn($columnKey = null, $indexKey = null): self
2033
    {
2034 1
        return static::create(
2035 1
            \array_column($this->getArray(), $columnKey, $indexKey),
2036 1
            $this->iteratorClass,
2037 1
            false
2038
        );
2039
    }
2040
2041
    /**
2042
     * Get the current array from the "Arrayy"-object as generator.
2043
     *
2044
     * @return \Generator
2045
     */
2046 856
    public function getGenerator(): \Generator
2047
    {
2048 856
        if ($this->generator instanceof ArrayyRewindableGenerator) {
2049 39
            yield from $this->generator;
2050
        }
2051
2052 856
        yield from $this->array;
2053 813
    }
2054
2055
    /**
2056
     * alias: for "Arrayy->keys()"
2057
     *
2058
     * @return static
2059
     *                <p>(Immutable)</p>
2060
     *
2061
     * @see Arrayy::keys()
2062
     */
2063 1
    public function getKeys(): self
2064
    {
2065 1
        return $this->keys();
2066
    }
2067
2068
    /**
2069
     * Get the current array from the "Arrayy"-object as object.
2070
     *
2071
     * @return \stdClass
2072
     */
2073 4
    public function getObject(): \stdClass
2074
    {
2075 4
        return self::arrayToObject($this->getArray());
2076
    }
2077
2078
    /**
2079
     * alias: for "Arrayy->randomImmutable()"
2080
     *
2081
     * @return static
2082
     *                <p>(Immutable)</p>
2083
     *
2084
     * @see Arrayy::randomImmutable()
2085
     */
2086 4
    public function getRandom(): self
2087
    {
2088 4
        return $this->randomImmutable();
2089
    }
2090
2091
    /**
2092
     * alias: for "Arrayy->randomKey()"
2093
     *
2094
     * @return mixed
2095
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
2096
     *
2097
     * @see Arrayy::randomKey()
2098
     */
2099 3
    public function getRandomKey()
2100
    {
2101 3
        return $this->randomKey();
2102
    }
2103
2104
    /**
2105
     * alias: for "Arrayy->randomKeys()"
2106
     *
2107
     * @param int $number
2108
     *
2109
     * @return static
2110
     *                <p>(Immutable)</p>
2111
     *
2112
     * @see Arrayy::randomKeys()
2113
     */
2114 8
    public function getRandomKeys(int $number): self
2115
    {
2116 8
        return $this->randomKeys($number);
2117
    }
2118
2119
    /**
2120
     * alias: for "Arrayy->randomValue()"
2121
     *
2122
     * @return mixed
2123
     *               <p>Get a random value or null if there wasn't a value.</p>
2124
     *
2125
     * @see Arrayy::randomValue()
2126
     */
2127 3
    public function getRandomValue()
2128
    {
2129 3
        return $this->randomValue();
2130
    }
2131
2132
    /**
2133
     * alias: for "Arrayy->randomValues()"
2134
     *
2135
     * @param int $number
2136
     *
2137
     * @return static
2138
     *                <p>(Immutable)</p>
2139
     *
2140
     * @see Arrayy::randomValues()
2141
     */
2142 6
    public function getRandomValues(int $number): self
2143
    {
2144 6
        return $this->randomValues($number);
2145
    }
2146
2147
    /**
2148
     * Group values from a array according to the results of a closure.
2149
     *
2150
     * @param callable|string $grouper  <p>A callable function name.</p>
2151
     * @param bool            $saveKeys
2152
     *
2153
     * @return static
2154
     *                <p>(Immutable)</p>
2155
     */
2156 4
    public function group($grouper, bool $saveKeys = false): self
2157
    {
2158
        // init
2159 4
        $result = [];
2160
2161
        // Iterate over values, group by property/results from closure.
2162 4
        foreach ($this->getGenerator() as $key => $value) {
2163 4
            if (\is_callable($grouper)) {
2164 3
                $groupKey = $grouper($value, $key);
2165
            } else {
2166 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2167
            }
2168
2169 4
            $newValue = $this->get($groupKey, null, $result);
2170
2171 4
            if ($groupKey instanceof self) {
2172
                $groupKey = $groupKey->getArray();
2173
            }
2174
2175 4
            if ($newValue instanceof self) {
2176 4
                $newValue = $newValue->getArray();
2177
            }
2178
2179
            // Add to results.
2180 4
            if ($groupKey !== null) {
2181 3
                if ($saveKeys) {
2182 2
                    $result[$groupKey] = $newValue;
2183 2
                    $result[$groupKey][$key] = $value;
2184
                } else {
2185 1
                    $result[$groupKey] = $newValue;
2186 4
                    $result[$groupKey][] = $value;
2187
                }
2188
            }
2189
        }
2190
2191 4
        return static::create(
2192 4
            $result,
2193 4
            $this->iteratorClass,
2194 4
            false
2195
        );
2196
    }
2197
2198
    /**
2199
     * Check if an array has a given key.
2200
     *
2201
     * @param mixed $key
2202
     *
2203
     * @return bool
2204
     */
2205 23
    public function has($key): bool
2206
    {
2207 23
        static $UN_FOUND = null;
2208
2209 23
        if ($UN_FOUND === null) {
2210
            // Generate unique string to use as marker.
2211 1
            $UN_FOUND = \uniqid('arrayy', true);
2212
        }
2213
2214 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2215
    }
2216
2217
    /**
2218
     * Implodes the values of this array.
2219
     *
2220
     * @param string $glue
2221
     *
2222
     * @return string
2223
     */
2224 28
    public function implode(string $glue = ''): string
2225
    {
2226 28
        return $this->implode_recursive($glue, $this->getArray(), false);
2227
    }
2228
2229
    /**
2230
     * Implodes the keys of this array.
2231
     *
2232
     * @param string $glue
2233
     *
2234
     * @return string
2235
     */
2236 8
    public function implodeKeys(string $glue = ''): string
2237
    {
2238 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2239
    }
2240
2241
    /**
2242
     * Given a list and an iterate-function that returns
2243
     * a key for each element in the list (or a property name),
2244
     * returns an object with an index of each item.
2245
     *
2246
     * @param mixed $key
2247
     *
2248
     * @return static
2249
     *                <p>(Immutable)</p>
2250
     */
2251 4
    public function indexBy($key): self
2252
    {
2253
        // init
2254 4
        $results = [];
2255
2256 4
        foreach ($this->getGenerator() as $a) {
2257 4
            if (\array_key_exists($key, $a) === true) {
2258 4
                $results[$a[$key]] = $a;
2259
            }
2260
        }
2261
2262 4
        return static::create(
2263 4
            $results,
2264 4
            $this->iteratorClass,
2265 4
            false
2266
        );
2267
    }
2268
2269
    /**
2270
     * alias: for "Arrayy->searchIndex()"
2271
     *
2272
     * @param mixed $value <p>The value to search for.</p>
2273
     *
2274
     * @return mixed
2275
     *
2276
     * @see Arrayy::searchIndex()
2277
     */
2278 4
    public function indexOf($value)
2279
    {
2280 4
        return $this->searchIndex($value);
2281
    }
2282
2283
    /**
2284
     * Get everything but the last..$to items.
2285
     *
2286
     * @param int $to
2287
     *
2288
     * @return static
2289
     *                <p>(Immutable)</p>
2290
     */
2291 12
    public function initial(int $to = 1): self
2292
    {
2293 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2294
    }
2295
2296
    /**
2297
     * Return an array with all elements found in input array.
2298
     *
2299
     * @param array $search
2300
     * @param bool  $keepKeys
2301
     *
2302
     * @return static
2303
     *                <p>(Immutable)</p>
2304
     */
2305 3
    public function intersection(array $search, bool $keepKeys = false): self
2306
    {
2307 3
        if ($keepKeys) {
2308 1
            return static::create(
2309 1
                \array_uintersect(
2310 1
                    $this->array,
2311 1
                    $search,
2312
                    static function ($a, $b) {
2313 1
                        return $a === $b ? 0 : -1;
2314 1
                    }
2315
                ),
2316 1
                $this->iteratorClass,
2317 1
                false
2318
            );
2319
        }
2320
2321 2
        return static::create(
2322 2
            \array_values(\array_intersect($this->getArray(), $search)),
2323 2
            $this->iteratorClass,
2324 2
            false
2325
        );
2326
    }
2327
2328
    /**
2329
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2330
     *
2331
     * @param array $search
2332
     *
2333
     * @return bool
2334
     */
2335 1
    public function intersects(array $search): bool
2336
    {
2337 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2338
    }
2339
2340
    /**
2341
     * Invoke a function on all of an array's values.
2342
     *
2343
     * @param callable $callable
2344
     * @param mixed    $arguments
2345
     *
2346
     * @return static
2347
     *                <p>(Immutable)</p>
2348
     */
2349 1
    public function invoke($callable, $arguments = []): self
2350
    {
2351
        // If one argument given for each iteration, create an array for it.
2352 1
        if (!\is_array($arguments)) {
2353 1
            $arguments = \array_fill(
2354 1
                0,
2355 1
                \count($this->getArray(), \COUNT_NORMAL),
2356 1
                $arguments
2357
            );
2358
        }
2359
2360
        // If the callable has arguments, pass them.
2361 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...
2362 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2363
        } else {
2364 1
            $array = $this->map($callable);
2365
        }
2366
2367 1
        return static::create(
2368 1
            $array,
2369 1
            $this->iteratorClass,
2370 1
            false
2371
        );
2372
    }
2373
2374
    /**
2375
     * Check whether array is associative or not.
2376
     *
2377
     * @param bool $recursive
2378
     *
2379
     * @return bool
2380
     *              <p>Returns true if associative, false otherwise.</p>
2381
     */
2382 15
    public function isAssoc(bool $recursive = false): bool
2383
    {
2384 15
        if ($this->isEmpty()) {
2385 3
            return false;
2386
        }
2387
2388 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2389 13
            if (!\is_string($key)) {
2390 13
                return false;
2391
            }
2392
        }
2393
2394 3
        return true;
2395
    }
2396
2397
    /**
2398
     * Check if a given key or keys are empty.
2399
     *
2400
     * @param int|int[]|string|string[]|null $keys
2401
     *
2402
     * @return bool
2403
     *              <p>Returns true if empty, false otherwise.</p>
2404
     */
2405 38
    public function isEmpty($keys = null): bool
2406
    {
2407 38
        if ($this->generator) {
2408
            return $this->getArray() === [];
2409
        }
2410
2411 38
        if ($keys === null) {
2412 38
            return $this->array === [];
2413
        }
2414
2415
        foreach ((array) $keys as $key) {
2416
            if (!empty($this->get($key))) {
2417
                return false;
2418
            }
2419
        }
2420
2421
        return true;
2422
    }
2423
2424
    /**
2425
     * Check if the current array is equal to the given "$array" or not.
2426
     *
2427
     * @param array $array
2428
     *
2429
     * @return bool
2430
     */
2431
    public function isEqual(array $array): bool
2432
    {
2433
        return $this->getArray() === $array;
2434
    }
2435
2436
    /**
2437
     * Check if the current array is a multi-array.
2438
     *
2439
     * @return bool
2440
     */
2441 22
    public function isMultiArray(): bool
2442
    {
2443
        return !(
2444 22
            \count($this->getArray(), \COUNT_NORMAL)
2445
            ===
2446 22
            \count($this->getArray(), \COUNT_RECURSIVE)
2447
        );
2448
    }
2449
2450
    /**
2451
     * Check whether array is numeric or not.
2452
     *
2453
     * @return bool
2454
     *              <p>Returns true if numeric, false otherwise.</p>
2455
     */
2456 5
    public function isNumeric(): bool
2457
    {
2458 5
        if ($this->isEmpty()) {
2459 2
            return false;
2460
        }
2461
2462 4
        foreach ($this->keys()->getGenerator() as $key) {
2463 4
            if (!\is_int($key)) {
2464 4
                return false;
2465
            }
2466
        }
2467
2468 2
        return true;
2469
    }
2470
2471
    /**
2472
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2473
     *
2474
     * @param bool $recursive
2475
     *
2476
     * @return bool
2477
     */
2478 1
    public function isSequential(bool $recursive = false): bool
2479
    {
2480
2481
        // recursive
2482
2483 1
        if ($recursive === true) {
2484
            return $this->array_keys_recursive($this->getArray())
2485
                   ===
2486
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2487
        }
2488
2489
        // non recursive
2490
2491 1
        return \array_keys($this->getArray())
2492
               ===
2493 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2494
    }
2495
2496
    /**
2497
     * @return array
2498
     */
2499
    public function jsonSerialize(): array
2500
    {
2501
        return $this->getArray();
2502
    }
2503
2504
    /**
2505
     * Checks if the given key exists in the provided array.
2506
     *
2507
     * INFO: This method only use "array_key_exists()" if you want to use "dot"-notation,
2508
     *       then you need to use "Arrayy->offsetExists()".
2509
     *
2510
     * @param int|string $key the key to look for
2511
     *
2512
     * @return bool
2513
     */
2514 58
    public function keyExists($key): bool
2515
    {
2516 58
        return \array_key_exists($key, $this->array);
2517
    }
2518
2519
    /**
2520
     * Get all keys from the current array.
2521
     *
2522
     * @param bool  $recursive    [optional] <p>
2523
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2524
     *                            </p>
2525
     * @param mixed $search_value [optional] <p>
2526
     *                            If specified, then only keys containing these values are returned.
2527
     *                            </p>
2528
     * @param bool  $strict       [optional] <p>
2529
     *                            Determines if strict comparison (===) should be used during the search.
2530
     *                            </p>
2531
     *
2532
     * @return static
2533
     *                <p>(Immutable) An array of all the keys in input.</p>
2534
     */
2535 29
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2536
    {
2537
2538
        // recursive
2539
2540 29
        if ($recursive === true) {
2541 3
            if ($search_value === null) {
2542 3
                $array = $this->array_keys_recursive($this->getArray());
2543
            } else {
2544
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2545
            }
2546
2547 3
            return static::create(
2548 3
                $array,
2549 3
                $this->iteratorClass,
2550 3
                false
2551
            );
2552
        }
2553
2554
        // non recursive
2555
2556 28
        if ($search_value === null) {
2557
            $arrayFunction = function () {
2558 28
                foreach ($this->getGenerator() as $key => $value) {
2559 26
                    yield $key;
2560
                }
2561 28
            };
2562
        } else {
2563
            $arrayFunction = function () use ($search_value, $strict) {
2564
                foreach ($this->getGenerator() as $key => $value) {
2565
                    if ($strict) {
2566
                        if ($search_value === $value) {
2567
                            yield $key;
2568
                        }
2569
                    } else {
2570
                        /** @noinspection NestedPositiveIfStatementsInspection */
2571
                        if ($search_value == $value) {
2572
                            yield $key;
2573
                        }
2574
                    }
2575
                }
2576
            };
2577
        }
2578
2579 28
        return static::create(
2580 28
            $arrayFunction,
2581 28
            $this->iteratorClass,
2582 28
            false
2583
        );
2584
    }
2585
2586
    /**
2587
     * Sort an array by key in reverse order.
2588
     *
2589
     * @param int $sort_flags [optional] <p>
2590
     *                        You may modify the behavior of the sort using the optional
2591
     *                        parameter sort_flags, for details
2592
     *                        see sort.
2593
     *                        </p>
2594
     *
2595
     * @return static
2596
     *                <p>(Mutable) Return this Arrayy object.</p>
2597
     */
2598 4
    public function krsort(int $sort_flags = 0): self
2599
    {
2600 4
        $this->generatorToArray();
2601
2602 4
        \krsort($this->array, $sort_flags);
2603
2604 4
        return $this;
2605
    }
2606
2607
    /**
2608
     * Get the last value from the current array.
2609
     *
2610
     * @return mixed
2611
     *               <p>Return null if there wasn't a element.</p>
2612
     */
2613 12
    public function last()
2614
    {
2615 12
        $key_last = $this->lastKey();
2616 12
        if ($key_last === null) {
2617 1
            return null;
2618
        }
2619
2620 11
        return $this->get($key_last);
2621
    }
2622
2623
    /**
2624
     * Get the last key from the current array.
2625
     *
2626
     * @return mixed
2627
     *               <p>Return null if there wasn't a element.</p>
2628
     */
2629 16
    public function lastKey()
2630
    {
2631 16
        $this->generatorToArray();
2632
2633 16
        return \array_key_last($this->array);
2634
    }
2635
2636
    /**
2637
     * Get the last value(s) from the current array.
2638
     *
2639
     * @param int|null $number
2640
     *
2641
     * @return static
2642
     *                <p>(Immutable)</p>
2643
     */
2644 13
    public function lastsImmutable(int $number = null): self
2645
    {
2646 13
        if ($this->isEmpty()) {
2647 1
            return static::create(
2648 1
                [],
2649 1
                $this->iteratorClass,
2650 1
                false
2651
            );
2652
        }
2653
2654 12
        if ($number === null) {
2655 8
            $poppedValue = $this->last();
2656
2657 8
            if ($poppedValue === null) {
2658 1
                $poppedValue = [$poppedValue];
2659
            } else {
2660 7
                $poppedValue = (array) $poppedValue;
2661
            }
2662
2663 8
            $arrayy = static::create(
2664 8
                $poppedValue,
2665 8
                $this->iteratorClass,
2666 8
                false
2667
            );
2668
        } else {
2669 4
            $number = (int) $number;
2670 4
            $arrayy = $this->rest(-$number);
2671
        }
2672
2673 12
        return $arrayy;
2674
    }
2675
2676
    /**
2677
     * Get the last value(s) from the current array.
2678
     *
2679
     * @param int|null $number
2680
     *
2681
     * @return static
2682
     *                <p>(Mutable)</p>
2683
     */
2684 13
    public function lastsMutable(int $number = null): self
2685
    {
2686 13
        if ($this->isEmpty()) {
2687 1
            return $this;
2688
        }
2689
2690 12
        if ($number === null) {
2691 8
            $poppedValue = $this->last();
2692
2693 8
            if ($poppedValue === null) {
2694 1
                $poppedValue = [$poppedValue];
2695
            } else {
2696 7
                $poppedValue = (array) $poppedValue;
2697
            }
2698
2699 8
            $this->array = static::create(
2700 8
                $poppedValue,
2701 8
                $this->iteratorClass,
2702 8
                false
2703 8
            )->getArray();
2704
        } else {
2705 4
            $number = (int) $number;
2706 4
            $this->array = $this->rest(-$number)->getArray();
2707
        }
2708
2709 12
        $this->generator = null;
2710
2711 12
        return $this;
2712
    }
2713
2714
    /**
2715
     * Count the values from the current array.
2716
     *
2717
     * alias: for "Arrayy->count()"
2718
     *
2719
     * @param int $mode
2720
     *
2721
     * @return int
2722
     *
2723
     * @see Arrayy::count()
2724
     */
2725 20
    public function length(int $mode = \COUNT_NORMAL): int
2726
    {
2727 20
        return $this->count($mode);
2728
    }
2729
2730
    /**
2731
     * Apply the given function to the every element of the array,
2732
     * collecting the results.
2733
     *
2734
     * @param callable $callable
2735
     * @param bool     $useKeyAsSecondParameter
2736
     * @param mixed    ...$arguments
2737
     *
2738
     * @return static
2739
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2740
     */
2741 5
    public function map(callable $callable, bool $useKeyAsSecondParameter = false, ...$arguments): self
2742
    {
2743 5
        $useArguments = \func_num_args() > 2;
2744
2745 5
        return static::create(
2746
            function () use ($useArguments, $callable, $useKeyAsSecondParameter, $arguments) {
2747 5
                foreach ($this->getGenerator() as $key => $value) {
2748 4
                    if ($useArguments) {
2749 3
                        if ($useKeyAsSecondParameter) {
2750
                            yield $key => $callable($value, $key, ...$arguments);
2751
                        } else {
2752 3
                            yield $key => $callable($value, ...$arguments);
2753
                        }
2754
                    } else {
2755
                        /** @noinspection NestedPositiveIfStatementsInspection */
2756 4
                        if ($useKeyAsSecondParameter) {
2757
                            yield $key => $callable($value, $key);
2758
                        } else {
2759 4
                            yield $key => $callable($value);
2760
                        }
2761
                    }
2762
                }
2763 5
            },
2764 5
            $this->iteratorClass,
2765 5
            false
2766
        );
2767
    }
2768
2769
    /**
2770
     * Check if all items in current array match a truth test.
2771
     *
2772
     * @param \Closure $closure
2773
     *
2774
     * @return bool
2775
     */
2776 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...
2777
    {
2778 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2779 2
            return false;
2780
        }
2781
2782 13
        foreach ($this->getGenerator() as $key => $value) {
2783 13
            $value = $closure($value, $key);
2784
2785 13
            if ($value === false) {
2786 13
                return false;
2787
            }
2788
        }
2789
2790 7
        return true;
2791
    }
2792
2793
    /**
2794
     * Check if any item in the current array matches a truth test.
2795
     *
2796
     * @param \Closure $closure
2797
     *
2798
     * @return bool
2799
     */
2800 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...
2801
    {
2802 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2803 2
            return false;
2804
        }
2805
2806 12
        foreach ($this->getGenerator() as $key => $value) {
2807 12
            $value = $closure($value, $key);
2808
2809 12
            if ($value === true) {
2810 12
                return true;
2811
            }
2812
        }
2813
2814 4
        return false;
2815
    }
2816
2817
    /**
2818
     * Get the max value from an array.
2819
     *
2820
     * @return mixed
2821
     */
2822 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...
2823
    {
2824 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2825 1
            return false;
2826
        }
2827
2828 9
        return \max($this->getArray());
2829
    }
2830
2831
    /**
2832
     * Merge the new $array into the current array.
2833
     *
2834
     * - keep key,value from the current array, also if the index is in the new $array
2835
     *
2836
     * @param array $array
2837
     * @param bool  $recursive
2838
     *
2839
     * @return static
2840
     *                <p>(Immutable)</p>
2841
     */
2842 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...
2843
    {
2844 25
        if ($recursive === true) {
2845 4
            $result = \array_replace_recursive($this->getArray(), $array);
2846
        } else {
2847 21
            $result = \array_replace($this->getArray(), $array);
2848
        }
2849
2850 25
        return static::create(
2851 25
            $result,
2852 25
            $this->iteratorClass,
2853 25
            false
2854
        );
2855
    }
2856
2857
    /**
2858
     * Merge the new $array into the current array.
2859
     *
2860
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2861
     * - create new indexes
2862
     *
2863
     * @param array $array
2864
     * @param bool  $recursive
2865
     *
2866
     * @return static
2867
     *                <p>(Immutable)</p>
2868
     */
2869 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...
2870
    {
2871 16
        if ($recursive === true) {
2872 4
            $result = \array_merge_recursive($this->getArray(), $array);
2873
        } else {
2874 12
            $result = \array_merge($this->getArray(), $array);
2875
        }
2876
2877 16
        return static::create(
2878 16
            $result,
2879 16
            $this->iteratorClass,
2880 16
            false
2881
        );
2882
    }
2883
2884
    /**
2885
     * Merge the the current array into the $array.
2886
     *
2887
     * - use key,value from the new $array, also if the index is in the current array
2888
     *
2889
     * @param array $array
2890
     * @param bool  $recursive
2891
     *
2892
     * @return static
2893
     *                <p>(Immutable)</p>
2894
     */
2895 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...
2896
    {
2897 16
        if ($recursive === true) {
2898 4
            $result = \array_replace_recursive($array, $this->getArray());
2899
        } else {
2900 12
            $result = \array_replace($array, $this->getArray());
2901
        }
2902
2903 16
        return static::create(
2904 16
            $result,
2905 16
            $this->iteratorClass,
2906 16
            false
2907
        );
2908
    }
2909
2910
    /**
2911
     * Merge the current array into the new $array.
2912
     *
2913
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2914
     * - create new indexes
2915
     *
2916
     * @param array $array
2917
     * @param bool  $recursive
2918
     *
2919
     * @return static
2920
     *                <p>(Immutable)</p>
2921
     */
2922 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...
2923
    {
2924 17
        if ($recursive === true) {
2925 4
            $result = \array_merge_recursive($array, $this->getArray());
2926
        } else {
2927 13
            $result = \array_merge($array, $this->getArray());
2928
        }
2929
2930 17
        return static::create(
2931 17
            $result,
2932 17
            $this->iteratorClass,
2933 17
            false
2934
        );
2935
    }
2936
2937
    /**
2938
     * @return ArrayyMeta|static
2939
     */
2940 14
    public static function meta()
2941
    {
2942 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2943
    }
2944
2945
    /**
2946
     * Get the min value from an array.
2947
     *
2948
     * @return mixed
2949
     */
2950 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...
2951
    {
2952 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2953 1
            return false;
2954
        }
2955
2956 9
        return \min($this->getArray());
2957
    }
2958
2959
    /**
2960
     * Move an array element to a new index.
2961
     *
2962
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2963
     *
2964
     * @param int|string $from
2965
     * @param int        $to
2966
     *
2967
     * @return static
2968
     *                <p>(Immutable)</p>
2969
     */
2970 1
    public function moveElement($from, $to): self
2971
    {
2972 1
        $array = $this->getArray();
2973
2974 1
        if (\is_int($from)) {
2975 1
            $tmp = \array_splice($array, $from, 1);
2976 1
            \array_splice($array, (int) $to, 0, $tmp);
2977 1
            $output = $array;
2978 1
        } elseif (\is_string($from)) {
2979 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2980 1
            $itemToMove = $array[$from];
2981 1
            if ($indexToMove !== false) {
2982 1
                \array_splice($array, $indexToMove, 1);
2983
            }
2984 1
            $i = 0;
2985 1
            $output = [];
2986 1
            foreach ($array as $key => $item) {
2987 1
                if ($i === $to) {
2988 1
                    $output[$from] = $itemToMove;
2989
                }
2990 1
                $output[$key] = $item;
2991 1
                ++$i;
2992
            }
2993
        } else {
2994
            $output = [];
2995
        }
2996
2997 1
        return static::create(
2998 1
            $output,
2999 1
            $this->iteratorClass,
3000 1
            false
3001
        );
3002
    }
3003
3004
    /**
3005
     * Get a subset of the items from the given array.
3006
     *
3007
     * @param mixed[] $keys
3008
     *
3009
     * @return static
3010
     *                <p>(Immutable)</p>
3011
     */
3012
    public function only(array $keys): self
3013
    {
3014
        $array = $this->getArray();
3015
3016
        return static::create(
3017
            \array_intersect_key($array, \array_flip($keys)),
3018
            $this->iteratorClass,
3019
            false
3020
        );
3021
    }
3022
3023
    /**
3024
     * Pad array to the specified size with a given value.
3025
     *
3026
     * @param int   $size  <p>Size of the result array.</p>
3027
     * @param mixed $value <p>Empty value by default.</p>
3028
     *
3029
     * @return static
3030
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
3031
     */
3032 4
    public function pad(int $size, $value): self
3033
    {
3034 4
        return static::create(
3035 4
            \array_pad($this->getArray(), $size, $value),
3036 4
            $this->iteratorClass,
3037 4
            false
3038
        );
3039
    }
3040
3041
    /**
3042
     * Pop a specified value off the end of the current array.
3043
     *
3044
     * @return mixed
3045
     *               <p>(Mutable) The popped element from the current array.</p>
3046
     */
3047 4
    public function pop()
3048
    {
3049 4
        $this->generatorToArray();
3050
3051 4
        return \array_pop($this->array);
3052
    }
3053
3054
    /**
3055
     * Prepend a (key) + value to the current array.
3056
     *
3057
     * @param mixed $value
3058
     * @param mixed $key
3059
     *
3060
     * @return static
3061
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
3062
     */
3063 9
    public function prepend($value, $key = null): self
3064
    {
3065 9
        $this->generatorToArray();
3066
3067 9
        if ($key === null) {
3068 8
            \array_unshift($this->array, $value);
3069
        } else {
3070
            /** @noinspection AdditionOperationOnArraysInspection */
3071 2
            $this->array = [$key => $value] + $this->array;
3072
        }
3073
3074 9
        return $this;
3075
    }
3076
3077
    /**
3078
     * Add a suffix to each key.
3079
     *
3080
     * @param mixed $suffix
3081
     *
3082
     * @return static
3083
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
3084
     */
3085 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...
3086
    {
3087
        // init
3088 10
        $result = [];
3089
3090 10
        foreach ($this->getGenerator() as $key => $item) {
3091 9
            if ($item instanceof self) {
3092
                $result[$key] = $item->prependToEachKey($suffix);
3093 9
            } elseif (\is_array($item)) {
3094
                $result[$key] = self::create(
3095
                    $item,
3096
                    $this->iteratorClass,
3097
                    false
3098
                )->prependToEachKey($suffix)
3099
                    ->toArray();
3100
            } else {
3101 9
                $result[$key . $suffix] = $item;
3102
            }
3103
        }
3104
3105 10
        return self::create(
3106 10
            $result,
3107 10
            $this->iteratorClass,
3108 10
            false
3109
        );
3110
    }
3111
3112
    /**
3113
     * Add a suffix to each value.
3114
     *
3115
     * @param mixed $suffix
3116
     *
3117
     * @return static
3118
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3119
     */
3120 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...
3121
    {
3122
        // init
3123 10
        $result = [];
3124
3125 10
        foreach ($this->getGenerator() as $key => $item) {
3126 9
            if ($item instanceof self) {
3127
                $result[$key] = $item->prependToEachValue($suffix);
3128 9
            } elseif (\is_array($item)) {
3129
                $result[$key] = self::create(
3130
                    $item,
3131
                    $this->iteratorClass,
3132
                    false
3133
                )->prependToEachValue($suffix)
3134
                    ->toArray();
3135 9
            } elseif (\is_object($item)) {
3136 1
                $result[$key] = $item;
3137
            } else {
3138 9
                $result[$key] = $item . $suffix;
3139
            }
3140
        }
3141
3142 10
        return self::create(
3143 10
            $result,
3144 10
            $this->iteratorClass,
3145 10
            false
3146
        );
3147
    }
3148
3149
    /**
3150
     * Return the value of a given key and
3151
     * delete the key.
3152
     *
3153
     * @param int|int[]|string|string[]|null $keyOrKeys
3154
     * @param mixed                          $fallback
3155
     *
3156
     * @return mixed
3157
     */
3158 1
    public function pull($keyOrKeys = null, $fallback = null)
3159
    {
3160 1
        if ($keyOrKeys === null) {
3161
            $array = $this->getArray();
3162
            $this->clear();
3163
3164
            return $array;
3165
        }
3166
3167 1
        if (\is_array($keyOrKeys)) {
3168 1
            $valueOrValues = [];
3169 1
            foreach ($keyOrKeys as $key) {
3170 1
                $valueOrValues[] = $this->get($key, $fallback);
3171 1
                $this->offsetUnset($key);
3172
            }
3173
        } else {
3174 1
            $valueOrValues = $this->get($keyOrKeys, $fallback);
3175 1
            $this->offsetUnset($keyOrKeys);
3176
        }
3177
3178 1
        return $valueOrValues;
3179
    }
3180
3181
    /**
3182
     * Push one or more values onto the end of array at once.
3183
     *
3184
     * @return static
3185
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3186
     */
3187 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...
3188
    {
3189 4
        $this->generatorToArray();
3190
3191 4
        if (\func_num_args()) {
3192 4
            $args = \func_get_args();
3193 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...
3194
        }
3195
3196 4
        return $this;
3197
    }
3198
3199
    /**
3200
     * Get a random value from the current array.
3201
     *
3202
     * @param int|null $number <p>How many values you will take?</p>
3203
     *
3204
     * @return static
3205
     *                <p>(Immutable)</p>
3206
     */
3207 18
    public function randomImmutable(int $number = null): self
3208
    {
3209 18
        $this->generatorToArray();
3210
3211 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...
3212 1
            return static::create(
3213 1
                [],
3214 1
                $this->iteratorClass,
3215 1
                false
3216
            );
3217
        }
3218
3219 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...
3220
            /** @noinspection NonSecureArrayRandUsageInspection */
3221 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3222
3223 13
            return static::create(
3224 13
                $arrayRandValue,
3225 13
                $this->iteratorClass,
3226 13
                false
3227
            );
3228
        }
3229
3230 5
        $arrayTmp = $this->array;
3231
        /** @noinspection NonSecureShuffleUsageInspection */
3232 5
        \shuffle($arrayTmp);
3233
3234 5
        return static::create(
3235 5
            $arrayTmp,
3236 5
            $this->iteratorClass,
3237 5
            false
3238 5
        )->firstsImmutable($number);
3239
    }
3240
3241
    /**
3242
     * Pick a random key/index from the keys of this array.
3243
     *
3244
     * @throws \RangeException If array is empty
3245
     *
3246
     * @return mixed
3247
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3248
     */
3249 4
    public function randomKey()
3250
    {
3251 4
        $result = $this->randomKeys(1);
3252
3253 4
        if (!isset($result[0])) {
3254
            $result[0] = null;
3255
        }
3256
3257 4
        return $result[0];
3258
    }
3259
3260
    /**
3261
     * Pick a given number of random keys/indexes out of this array.
3262
     *
3263
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3264
     *
3265
     * @throws \RangeException If array is empty
3266
     *
3267
     * @return static
3268
     *                <p>(Immutable)</p>
3269
     */
3270 13
    public function randomKeys(int $number): self
3271
    {
3272 13
        $this->generatorToArray();
3273
3274 13
        $count = \count($this->array, \COUNT_NORMAL);
3275
3276 13
        if ($number === 0 || $number > $count) {
3277 2
            throw new \RangeException(
3278 2
                \sprintf(
3279 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3280 2
                    $number,
3281 2
                    $count
3282
                )
3283
            );
3284
        }
3285
3286 11
        $result = (array) \array_rand($this->array, $number);
3287
3288 11
        return static::create(
3289 11
            $result,
3290 11
            $this->iteratorClass,
3291 11
            false
3292
        );
3293
    }
3294
3295
    /**
3296
     * Get a random value from the current array.
3297
     *
3298
     * @param int|null $number <p>How many values you will take?</p>
3299
     *
3300
     * @return static
3301
     *                <p>(Mutable)</p>
3302
     */
3303 17
    public function randomMutable(int $number = null): self
3304
    {
3305 17
        $this->generatorToArray();
3306
3307 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...
3308
            return static::create(
3309
                [],
3310
                $this->iteratorClass,
3311
                false
3312
            );
3313
        }
3314
3315 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...
3316
            /** @noinspection NonSecureArrayRandUsageInspection */
3317 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3318 7
            $this->array = $arrayRandValue;
3319
3320 7
            return $this;
3321
        }
3322
3323
        /** @noinspection NonSecureShuffleUsageInspection */
3324 11
        \shuffle($this->array);
3325
3326 11
        return $this->firstsMutable($number);
3327
    }
3328
3329
    /**
3330
     * Pick a random value from the values of this array.
3331
     *
3332
     * @return mixed
3333
     *               <p>Get a random value or null if there wasn't a value.</p>
3334
     */
3335 4
    public function randomValue()
3336
    {
3337 4
        $result = $this->randomImmutable();
3338
3339 4
        if (!isset($result[0])) {
3340
            $result[0] = null;
3341
        }
3342
3343 4
        return $result[0];
3344
    }
3345
3346
    /**
3347
     * Pick a given number of random values out of this array.
3348
     *
3349
     * @param int $number
3350
     *
3351
     * @return static
3352
     *                <p>(Mutable)</p>
3353
     */
3354 7
    public function randomValues(int $number): self
3355
    {
3356 7
        return $this->randomMutable($number);
3357
    }
3358
3359
    /**
3360
     * Get a random value from an array, with the ability to skew the results.
3361
     *
3362
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3363
     *
3364
     * @param array    $array
3365
     * @param int|null $number <p>How many values you will take?</p>
3366
     *
3367
     * @return static
3368
     *                <p>(Immutable)</p>
3369
     */
3370 9
    public function randomWeighted(array $array, int $number = null): self
3371
    {
3372
        // init
3373 9
        $options = [];
3374
3375 9
        foreach ($array as $option => $weight) {
3376 9
            if ($this->searchIndex($option) !== false) {
3377 9
                for ($i = 0; $i < $weight; ++$i) {
3378 1
                    $options[] = $option;
3379
                }
3380
            }
3381
        }
3382
3383 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3384
    }
3385
3386
    /**
3387
     * Reduce the current array via callable e.g. anonymous-function.
3388
     *
3389
     * @param callable $callable
3390
     * @param array    $init
3391
     *
3392
     * @return static
3393
     *                <p>(Immutable)</p>
3394
     */
3395 16
    public function reduce($callable, array $init = []): self
3396
    {
3397 16
        if ($this->generator) {
3398 1
            $result = $init;
3399
3400 1
            foreach ($this->getGenerator() as $value) {
3401 1
                $result = $callable($result, $value);
3402
            }
3403
3404 1
            return static::create(
3405 1
                $result,
3406 1
                $this->iteratorClass,
3407 1
                false
3408
            );
3409
        }
3410
3411 16
        $result = \array_reduce($this->array, $callable, $init);
3412
3413 16
        if ($result === null) {
3414
            $this->array = [];
3415
        } else {
3416 16
            $this->array = (array) $result;
3417
        }
3418
3419 16
        return static::create(
3420 16
            $this->array,
3421 16
            $this->iteratorClass,
3422 16
            false
3423
        );
3424
    }
3425
3426
    /**
3427
     * @param bool $unique
3428
     *
3429
     * @return static
3430
     *                <p>(Immutable)</p>
3431
     */
3432 14
    public function reduce_dimension(bool $unique = true): self
3433
    {
3434
        // init
3435 14
        $result = [[]];
3436
3437 14
        foreach ($this->getGenerator() as $val) {
3438 12
            if (\is_array($val)) {
3439 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
3440
            } else {
3441 12
                $result[] = [$val];
3442
            }
3443
        }
3444 14
        $result = \array_merge(...$result);
3445
3446 14
        $resultArrayy = new self($result);
3447
3448 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
3449
    }
3450
3451
    /**
3452
     * Create a numerically re-indexed Arrayy object.
3453
     *
3454
     * @return static
3455
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3456
     */
3457 9
    public function reindex(): self
3458
    {
3459 9
        $this->generatorToArray();
3460
3461 9
        $this->array = \array_values($this->array);
3462
3463 9
        return $this;
3464
    }
3465
3466
    /**
3467
     * Return all items that fail the truth test.
3468
     *
3469
     * @param \Closure $closure
3470
     *
3471
     * @return static
3472
     *                <p>(Immutable)</p>
3473
     */
3474 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...
3475
    {
3476
        // init
3477 1
        $filtered = [];
3478
3479 1
        foreach ($this->getGenerator() as $key => $value) {
3480 1
            if (!$closure($value, $key)) {
3481 1
                $filtered[$key] = $value;
3482
            }
3483
        }
3484
3485 1
        return static::create(
3486 1
            $filtered,
3487 1
            $this->iteratorClass,
3488 1
            false
3489
        );
3490
    }
3491
3492
    /**
3493
     * Remove a value from the current array (optional using dot-notation).
3494
     *
3495
     * @param mixed $key
3496
     *
3497
     * @return static
3498
     *                <p>(Mutable)</p>
3499
     */
3500 18
    public function remove($key): self
3501
    {
3502
        // recursive call
3503 18
        if (\is_array($key)) {
3504
            foreach ($key as $k) {
3505
                $this->internalRemove($k);
3506
            }
3507
3508
            return static::create(
3509
                $this->getArray(),
3510
                $this->iteratorClass,
3511
                false
3512
            );
3513
        }
3514
3515 18
        $this->internalRemove($key);
3516
3517 18
        return static::create(
3518 18
            $this->getArray(),
3519 18
            $this->iteratorClass,
3520 18
            false
3521
        );
3522
    }
3523
3524
    /**
3525
     * Remove the first value from the current array.
3526
     *
3527
     * @return static
3528
     *                <p>(Immutable)</p>
3529
     */
3530 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...
3531
    {
3532 7
        $tmpArray = $this->getArray();
3533
3534 7
        \array_shift($tmpArray);
3535
3536 7
        return static::create(
3537 7
            $tmpArray,
3538 7
            $this->iteratorClass,
3539 7
            false
3540
        );
3541
    }
3542
3543
    /**
3544
     * Remove the last value from the current array.
3545
     *
3546
     * @return static
3547
     *                <p>(Immutable)</p>
3548
     */
3549 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...
3550
    {
3551 7
        $tmpArray = $this->getArray();
3552
3553 7
        \array_pop($tmpArray);
3554
3555 7
        return static::create(
3556 7
            $tmpArray,
3557 7
            $this->iteratorClass,
3558 7
            false
3559
        );
3560
    }
3561
3562
    /**
3563
     * Removes a particular value from an array (numeric or associative).
3564
     *
3565
     * @param mixed $value
3566
     *
3567
     * @return static
3568
     *                <p>(Immutable)</p>
3569
     */
3570 7
    public function removeValue($value): self
3571
    {
3572 7
        $this->generatorToArray();
3573
3574
        // init
3575 7
        $isNumericArray = true;
3576
3577 7
        foreach ($this->getGenerator() as $key => $item) {
3578 6
            if ($item === $value) {
3579 6
                if (!\is_int($key)) {
3580
                    $isNumericArray = false;
3581
                }
3582 6
                unset($this->array[$key]);
3583
            }
3584
        }
3585
3586 7
        if ($isNumericArray) {
3587 7
            $this->array = \array_values($this->array);
3588
        }
3589
3590 7
        return static::create(
3591 7
            $this->array,
3592 7
            $this->iteratorClass,
3593 7
            false
3594
        );
3595
    }
3596
3597
    /**
3598
     * Generate array of repeated arrays.
3599
     *
3600
     * @param int $times <p>How many times has to be repeated.</p>
3601
     *
3602
     * @return static
3603
     *                <p>(Immutable)</p>
3604
     */
3605 1
    public function repeat($times): self
3606
    {
3607 1
        if ($times === 0) {
3608 1
            return new static();
3609
        }
3610
3611 1
        return static::create(
3612 1
            \array_fill(0, (int) $times, $this->getArray()),
3613 1
            $this->iteratorClass,
3614 1
            false
3615
        );
3616
    }
3617
3618
    /**
3619
     * Replace a key with a new key/value pair.
3620
     *
3621
     * @param mixed $replace
3622
     * @param mixed $key
3623
     * @param mixed $value
3624
     *
3625
     * @return static
3626
     *                <p>(Immutable)</p>
3627
     */
3628 2
    public function replace($replace, $key, $value): self
3629
    {
3630 2
        $that = clone $this;
3631
3632 2
        return $that->remove($replace)
3633 2
            ->set($key, $value);
3634
    }
3635
3636
    /**
3637
     * Create an array using the current array as values and the other array as keys.
3638
     *
3639
     * @param array $keys <p>An array of keys.</p>
3640
     *
3641
     * @return static
3642
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3643
     */
3644 2
    public function replaceAllKeys(array $keys): self
3645
    {
3646 2
        return static::create(
3647 2
            \array_combine($keys, $this->getArray()),
3648 2
            $this->iteratorClass,
3649 2
            false
3650
        );
3651
    }
3652
3653
    /**
3654
     * Create an array using the current array as keys and the other array as values.
3655
     *
3656
     * @param array $array <p>An array o values.</p>
3657
     *
3658
     * @return static
3659
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3660
     */
3661 2
    public function replaceAllValues(array $array): self
3662
    {
3663 2
        return static::create(
3664 2
            \array_combine($this->array, $array),
3665 2
            $this->iteratorClass,
3666 2
            false
3667
        );
3668
    }
3669
3670
    /**
3671
     * Replace the keys in an array with another set.
3672
     *
3673
     * @param array $keys <p>An array of keys matching the array's size</p>
3674
     *
3675
     * @return static
3676
     *                <p>(Immutable)</p>
3677
     */
3678 1
    public function replaceKeys(array $keys): self
3679
    {
3680 1
        $values = \array_values($this->getArray());
3681 1
        $result = \array_combine($keys, $values);
3682
3683 1
        return static::create(
3684 1
            $result,
3685 1
            $this->iteratorClass,
3686 1
            false
3687
        );
3688
    }
3689
3690
    /**
3691
     * Replace the first matched value in an array.
3692
     *
3693
     * @param mixed $search      <p>The value to replace.</p>
3694
     * @param mixed $replacement <p>The value to replace.</p>
3695
     *
3696
     * @return static
3697
     *                <p>(Immutable)</p>
3698
     */
3699 3
    public function replaceOneValue($search, $replacement = ''): self
3700
    {
3701 3
        $array = $this->getArray();
3702 3
        $key = \array_search($search, $array, true);
3703
3704 3
        if ($key !== false) {
3705 3
            $array[$key] = $replacement;
3706
        }
3707
3708 3
        return static::create(
3709 3
            $array,
3710 3
            $this->iteratorClass,
3711 3
            false
3712
        );
3713
    }
3714
3715
    /**
3716
     * Replace values in the current array.
3717
     *
3718
     * @param mixed $search      <p>The value to replace.</p>
3719
     * @param mixed $replacement <p>What to replace it with.</p>
3720
     *
3721
     * @return static
3722
     *                <p>(Immutable)</p>
3723
     */
3724 1
    public function replaceValues($search, $replacement = ''): self
3725
    {
3726 1
        $array = $this->each(
3727
            static function ($value) use ($search, $replacement) {
3728 1
                return \str_replace($search, $replacement, $value);
3729 1
            }
3730
        );
3731
3732 1
        return $array;
3733
    }
3734
3735
    /**
3736
     * Get the last elements from index $from until the end of this array.
3737
     *
3738
     * @param int $from
3739
     *
3740
     * @return static
3741
     *                <p>(Immutable)</p>
3742
     */
3743 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...
3744
    {
3745 15
        $tmpArray = $this->getArray();
3746
3747 15
        return static::create(
3748 15
            \array_splice($tmpArray, $from),
3749 15
            $this->iteratorClass,
3750 15
            false
3751
        );
3752
    }
3753
3754
    /**
3755
     * Return the array in the reverse order.
3756
     *
3757
     * @return static
3758
     *                <p>(Mutable) Return this Arrayy object.</p>
3759
     */
3760 8
    public function reverse(): self
3761
    {
3762 8
        $this->generatorToArray();
3763
3764 8
        $this->array = \array_reverse($this->array);
3765
3766 8
        return $this;
3767
    }
3768
3769
    /**
3770
     * Sort an array in reverse order.
3771
     *
3772
     * @param int $sort_flags [optional] <p>
3773
     *                        You may modify the behavior of the sort using the optional
3774
     *                        parameter sort_flags, for details
3775
     *                        see sort.
3776
     *                        </p>
3777
     *
3778
     * @return static
3779
     *                <p>(Mutable) Return this Arrayy object.</p>
3780
     */
3781 4
    public function rsort(int $sort_flags = 0): self
3782
    {
3783 4
        $this->generatorToArray();
3784
3785 4
        \rsort($this->array, $sort_flags);
3786
3787 4
        return $this;
3788
    }
3789
3790
    /**
3791
     * Search for the first index of the current array via $value.
3792
     *
3793
     * @param mixed $value
3794
     *
3795
     * @return false|float|int|string
3796
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3797
     */
3798 20
    public function searchIndex($value)
3799
    {
3800 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3801 19
            if ($value === $valueFromArray) {
3802 19
                return $keyFromArray;
3803
            }
3804
        }
3805
3806 11
        return false;
3807
    }
3808
3809
    /**
3810
     * Search for the value of the current array via $index.
3811
     *
3812
     * @param mixed $index
3813
     *
3814
     * @return static
3815
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3816
     */
3817 9
    public function searchValue($index): self
3818
    {
3819 9
        $this->generatorToArray();
3820
3821
        // init
3822 9
        $return = [];
3823
3824 9
        if ($this->array === []) {
3825
            return static::create(
3826
                [],
3827
                $this->iteratorClass,
3828
                false
3829
            );
3830
        }
3831
3832
        // php cast "bool"-index into "int"-index
3833 9
        if ((bool) $index === $index) {
3834 1
            $index = (int) $index;
3835
        }
3836
3837 9
        if ($this->offsetExists($index)) {
3838 7
            $return = [$this->array[$index]];
3839
        }
3840
3841 9
        return static::create(
3842 9
            $return,
3843 9
            $this->iteratorClass,
3844 9
            false
3845
        );
3846
    }
3847
3848
    /**
3849
     * Set a value for the current array (optional using dot-notation).
3850
     *
3851
     * @param string $key   <p>The key to set.</p>
3852
     * @param mixed  $value <p>Its value.</p>
3853
     *
3854
     * @return static
3855
     *                <p>(Mutable)</p>
3856
     */
3857 18
    public function set($key, $value): self
3858
    {
3859 18
        $this->generatorToArray();
3860
3861 18
        $this->internalSet($key, $value);
3862
3863 18
        return $this;
3864
    }
3865
3866
    /**
3867
     * Get a value from a array and set it if it was not.
3868
     *
3869
     * WARNING: this method only set the value, if the $key is not already set
3870
     *
3871
     * @param mixed $key      <p>The key</p>
3872
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3873
     *
3874
     * @return mixed
3875
     *               <p>(Mutable)</p>
3876
     */
3877 11
    public function setAndGet($key, $fallback = null)
3878
    {
3879 11
        $this->generatorToArray();
3880
3881
        // If the key doesn't exist, set it.
3882 11
        if (!$this->has($key)) {
3883 4
            $this->array = $this->set($key, $fallback)->getArray();
3884
        }
3885
3886 11
        return $this->get($key);
3887
    }
3888
3889
    /**
3890
     * Shifts a specified value off the beginning of array.
3891
     *
3892
     * @return mixed
3893
     *               <p>(Mutable) A shifted element from the current array.</p>
3894
     */
3895 4
    public function shift()
3896
    {
3897 4
        $this->generatorToArray();
3898
3899 4
        return \array_shift($this->array);
3900
    }
3901
3902
    /**
3903
     * Shuffle the current array.
3904
     *
3905
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3906
     * @param array $array  [optional]
3907
     *
3908
     * @return static
3909
     *                <p>(Immutable)</p>
3910
     */
3911 1
    public function shuffle(bool $secure = false, array $array = null): self
3912
    {
3913 1
        if ($array === null) {
3914 1
            $array = $this->getArray();
3915
        }
3916
3917 1
        if ($secure !== true) {
3918
            /** @noinspection NonSecureShuffleUsageInspection */
3919 1
            \shuffle($array);
3920
        } else {
3921 1
            $size = \count($array, \COUNT_NORMAL);
3922 1
            $keys = \array_keys($array);
3923 1
            for ($i = $size - 1; $i > 0; --$i) {
3924
                try {
3925 1
                    $r = \random_int(0, $i);
3926
                } catch (\Exception $e) {
3927
                    /** @noinspection RandomApiMigrationInspection */
3928
                    $r = \mt_rand(0, $i);
3929
                }
3930 1
                if ($r !== $i) {
3931 1
                    $temp = $array[$keys[$r]];
3932 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3933 1
                    $array[$keys[$i]] = $temp;
3934
                }
3935
            }
3936
3937
            // reset indices
3938 1
            $array = \array_values($array);
3939
        }
3940
3941 1
        foreach ($array as $key => $value) {
3942
            // check if recursive is needed
3943 1
            if (\is_array($value) === true) {
3944 1
                $array[$key] = $this->shuffle($secure, $value);
3945
            }
3946
        }
3947
3948 1
        return static::create(
3949 1
            $array,
3950 1
            $this->iteratorClass,
3951 1
            false
3952
        );
3953
    }
3954
3955
    /**
3956
     * Count the values from the current array.
3957
     *
3958
     * alias: for "Arrayy->count()"
3959
     *
3960
     * @param int $mode
3961
     *
3962
     * @return int
3963
     */
3964 20
    public function size(int $mode = \COUNT_NORMAL): int
3965
    {
3966 20
        return $this->count($mode);
3967
    }
3968
3969
    /**
3970
     * Checks whether array has exactly $size items.
3971
     *
3972
     * @param int $size
3973
     *
3974
     * @return bool
3975
     */
3976 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...
3977
    {
3978
        // init
3979 1
        $itemsTempCount = 0;
3980
3981 1
        foreach ($this->getGenerator() as $key => $value) {
3982 1
            ++$itemsTempCount;
3983 1
            if ($itemsTempCount > $size) {
3984 1
                return false;
3985
            }
3986
        }
3987
3988 1
        return $itemsTempCount === $size;
3989
    }
3990
3991
    /**
3992
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3993
     * smaller than $fromSize.
3994
     *
3995
     * @param int $fromSize
3996
     * @param int $toSize
3997
     *
3998
     * @return bool
3999
     */
4000 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
4001
    {
4002 1
        if ($fromSize > $toSize) {
4003 1
            $tmp = $toSize;
4004 1
            $toSize = $fromSize;
4005 1
            $fromSize = $tmp;
4006
        }
4007
4008
        // init
4009 1
        $itemsTempCount = 0;
4010
4011 1
        foreach ($this->getGenerator() as $key => $value) {
4012 1
            ++$itemsTempCount;
4013 1
            if ($itemsTempCount > $toSize) {
4014 1
                return false;
4015
            }
4016
        }
4017
4018 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
4019
    }
4020
4021
    /**
4022
     * Checks whether array has more than $size items.
4023
     *
4024
     * @param int $size
4025
     *
4026
     * @return bool
4027
     */
4028 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...
4029
    {
4030
        // init
4031 1
        $itemsTempCount = 0;
4032
4033 1
        foreach ($this->getGenerator() as $key => $value) {
4034 1
            ++$itemsTempCount;
4035 1
            if ($itemsTempCount > $size) {
4036 1
                return true;
4037
            }
4038
        }
4039
4040 1
        return $itemsTempCount > $size;
4041
    }
4042
4043
    /**
4044
     * Checks whether array has less than $size items.
4045
     *
4046
     * @param int $size
4047
     *
4048
     * @return bool
4049
     */
4050 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...
4051
    {
4052
        // init
4053 1
        $itemsTempCount = 0;
4054
4055 1
        foreach ($this->getGenerator() as $key => $value) {
4056 1
            ++$itemsTempCount;
4057 1
            if ($itemsTempCount > $size) {
4058 1
                return false;
4059
            }
4060
        }
4061
4062 1
        return $itemsTempCount < $size;
4063
    }
4064
4065
    /**
4066
     * Counts all elements in an array, or something in an object.
4067
     *
4068
     * <p>
4069
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
4070
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
4071
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
4072
     * implemented and used in PHP.
4073
     * </p>
4074
     *
4075
     * @return int
4076
     *             <p>
4077
     *             The number of elements in var, which is
4078
     *             typically an array, since anything else will have one
4079
     *             element.
4080
     *             </p>
4081
     *             <p>
4082
     *             If var is not an array or an object with
4083
     *             implemented Countable interface,
4084
     *             1 will be returned.
4085
     *             There is one exception, if var is &null;,
4086
     *             0 will be returned.
4087
     *             </p>
4088
     *             <p>
4089
     *             Caution: count may return 0 for a variable that isn't set,
4090
     *             but it may also return 0 for a variable that has been initialized with an
4091
     *             empty array. Use isset to test if a variable is set.
4092
     *             </p>
4093
     */
4094 10
    public function sizeRecursive(): int
4095
    {
4096 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
4097
    }
4098
4099
    /**
4100
     * Extract a slice of the array.
4101
     *
4102
     * @param int      $offset       <p>Slice begin index.</p>
4103
     * @param int|null $length       <p>Length of the slice.</p>
4104
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
4105
     *
4106
     * @return static
4107
     *                <p>A slice of the original array with length $length.</p>
4108
     */
4109 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
4110
    {
4111 4
        return static::create(
4112 4
            \array_slice(
4113 4
                $this->getArray(),
4114 4
                $offset,
4115 4
                $length,
4116 4
                $preserveKeys
4117
            ),
4118 4
            $this->iteratorClass,
4119 4
            false
4120
        );
4121
    }
4122
4123
    /**
4124
     * Sort the current array and optional you can keep the keys.
4125
     *
4126
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4127
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
4128
     *                              <strong>SORT_NATURAL</strong></p>
4129
     * @param bool       $keepKeys
4130
     *
4131
     * @return static
4132
     *                <p>(Mutable) Return this Arrayy object.</p>
4133
     */
4134 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4135
    {
4136 20
        $this->generatorToArray();
4137
4138 20
        return $this->sorting(
4139 20
            $this->array,
4140 20
            $direction,
4141 20
            $strategy,
4142 20
            $keepKeys
4143
        );
4144
    }
4145
4146
    /**
4147
     * Sort the current array by key.
4148
     *
4149
     * @see http://php.net/manual/en/function.ksort.php
4150
     * @see http://php.net/manual/en/function.krsort.php
4151
     *
4152
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4153
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4154
     *                              <strong>SORT_NATURAL</strong></p>
4155
     *
4156
     * @return static
4157
     *                <p>(Mutable) Return this Arrayy object.</p>
4158
     */
4159 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4160
    {
4161 18
        $this->generatorToArray();
4162
4163 18
        $this->sorterKeys($this->array, $direction, $strategy);
4164
4165 18
        return $this;
4166
    }
4167
4168
    /**
4169
     * Sort the current array by value.
4170
     *
4171
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4172
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4173
     *                              <strong>SORT_NATURAL</strong></p>
4174
     *
4175
     * @return static
4176
     *                <p>(Mutable)</p>
4177
     */
4178 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4179
    {
4180 1
        return $this->sort($direction, $strategy, true);
4181
    }
4182
4183
    /**
4184
     * Sort the current array by value.
4185
     *
4186
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4187
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4188
     *                              <strong>SORT_NATURAL</strong></p>
4189
     *
4190
     * @return static
4191
     *                <p>(Mutable)</p>
4192
     */
4193 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4194
    {
4195 1
        return $this->sort($direction, $strategy, false);
4196
    }
4197
4198
    /**
4199
     * Sort a array by value, by a closure or by a property.
4200
     *
4201
     * - If the sorter is null, the array is sorted naturally.
4202
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
4203
     *
4204
     * @param callable|string|null $sorter
4205
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or
4206
     *                                        <strong>SORT_DESC</strong></p>
4207
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4208
     *                                        <strong>SORT_NATURAL</strong></p>
4209
     *
4210
     * @return static
4211
     *                <p>(Immutable)</p>
4212
     */
4213 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4214
    {
4215 1
        $array = $this->getArray();
4216 1
        $direction = $this->getDirection($direction);
4217
4218
        // Transform all values into their results.
4219 1
        if ($sorter) {
4220 1
            $arrayy = static::create(
4221 1
                $array,
4222 1
                $this->iteratorClass,
4223 1
                false
4224
            );
4225
4226 1
            $results = $arrayy->each(
4227
                function ($value) use ($sorter) {
4228 1
                    if (\is_callable($sorter)) {
4229 1
                        return $sorter($value);
4230
                    }
4231
4232 1
                    return $this->get($sorter, null, $this->getArray());
4233 1
                }
4234
            );
4235
4236 1
            $results = $results->getArray();
4237
        } else {
4238 1
            $results = $array;
4239
        }
4240
4241
        // Sort by the results and replace by original values
4242 1
        \array_multisort($results, $direction, $strategy, $array);
4243
4244 1
        return static::create(
4245 1
            $array,
4246 1
            $this->iteratorClass,
4247 1
            false
4248
        );
4249
    }
4250
4251
    /**
4252
     * Split an array in the given amount of pieces.
4253
     *
4254
     * @param int  $numberOfPieces
4255
     * @param bool $keepKeys
4256
     *
4257
     * @return static
4258
     *                <p>(Immutable)</p>
4259
     */
4260 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
4261
    {
4262 1
        $this->generatorToArray();
4263
4264 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4265
4266 1
        if ($arrayCount === 0) {
4267 1
            $result = [];
4268
        } else {
4269 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4270 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4271
        }
4272
4273 1
        return static::create(
4274 1
            $result,
4275 1
            $this->iteratorClass,
4276 1
            false
4277
        );
4278
    }
4279
4280
    /**
4281
     * Stripe all empty items.
4282
     *
4283
     * @return static
4284
     *                <p>(Immutable)</p>
4285
     */
4286 1
    public function stripEmpty(): self
4287
    {
4288 1
        return $this->filter(
4289
            static function ($item) {
4290 1
                if ($item === null) {
4291 1
                    return false;
4292
                }
4293
4294 1
                return (bool) \trim((string) $item);
4295 1
            }
4296
        );
4297
    }
4298
4299
    /**
4300
     * Swap two values between positions by key.
4301
     *
4302
     * @param int|string $swapA <p>a key in the array</p>
4303
     * @param int|string $swapB <p>a key in the array</p>
4304
     *
4305
     * @return static
4306
     *                <p>(Immutable)</p>
4307
     */
4308 1
    public function swap($swapA, $swapB): self
4309
    {
4310 1
        $array = $this->getArray();
4311
4312 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4313
4314 1
        return static::create(
4315 1
            $array,
4316 1
            $this->iteratorClass,
4317 1
            false
4318
        );
4319
    }
4320
4321
    /**
4322
     * alias: for "Arrayy->getArray()"
4323
     *
4324
     * @see Arrayy::getArray()
4325
     */
4326 204
    public function toArray()
4327
    {
4328 204
        return $this->getArray();
4329
    }
4330
4331
    /**
4332
     * Convert the current array to JSON.
4333
     *
4334
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4335
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4336
     *
4337
     * @return string
4338
     */
4339 6
    public function toJson(int $options = 0, int $depth = 512): string
4340
    {
4341 6
        $return = \json_encode($this->getArray(), $options, $depth);
4342 6
        if ($return === false) {
4343
            return '';
4344
        }
4345
4346 6
        return $return;
4347
    }
4348
4349
    /**
4350
     * Implodes array to a string with specified separator.
4351
     *
4352
     * @param string $separator [optional] <p>The element's separator.</p>
4353
     *
4354
     * @return string
4355
     *                <p>The string representation of array, separated by ",".</p>
4356
     */
4357 20
    public function toString(string $separator = ','): string
4358
    {
4359 20
        return $this->implode($separator);
4360
    }
4361
4362
    /**
4363
     * Return a duplicate free copy of the current array.
4364
     *
4365
     * @return static
4366
     *                <p>(Mutable)</p>
4367
     */
4368 12
    public function unique(): self
4369
    {
4370
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4371
4372 12
        $this->array = $this->reduce(
4373
            static function ($resultArray, $value) {
4374 11
                if (!\in_array($value, $resultArray, true)) {
4375 11
                    $resultArray[] = $value;
4376
                }
4377
4378 11
                return $resultArray;
4379 12
            },
4380 12
            []
4381 12
        )->getArray();
4382 12
        $this->generator = null;
4383
4384 12
        return $this;
4385
    }
4386
4387
    /**
4388
     * Return a duplicate free copy of the current array. (with the old keys)
4389
     *
4390
     * @return static
4391
     *                <p>(Mutable)</p>
4392
     */
4393 11
    public function uniqueKeepIndex(): self
4394
    {
4395
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4396
4397
        // init
4398 11
        $array = $this->getArray();
4399
4400 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...
4401 11
            \array_keys($array),
4402
            static function ($resultArray, $key) use ($array) {
4403 10
                if (!\in_array($array[$key], $resultArray, true)) {
4404 10
                    $resultArray[$key] = $array[$key];
4405
                }
4406
4407 10
                return $resultArray;
4408 11
            },
4409 11
            []
4410
        );
4411 11
        $this->generator = null;
4412
4413 11
        if ($this->array === null) {
4414
            $this->array = [];
4415
        } else {
4416 11
            $this->array = (array) $this->array;
4417
        }
4418
4419 11
        return $this;
4420
    }
4421
4422
    /**
4423
     * alias: for "Arrayy->unique()"
4424
     *
4425
     * @return static
4426
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4427
     *
4428
     * @see Arrayy::unique()
4429
     */
4430 10
    public function uniqueNewIndex(): self
4431
    {
4432 10
        return $this->unique();
4433
    }
4434
4435
    /**
4436
     * Prepends one or more values to the beginning of array at once.
4437
     *
4438
     * @return static
4439
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4440
     */
4441 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...
4442
    {
4443 4
        $this->generatorToArray();
4444
4445 4
        if (\func_num_args()) {
4446 4
            $args = \func_get_args();
4447 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...
4448
        }
4449
4450 4
        return $this;
4451
    }
4452
4453
    /**
4454
     * Get all values from a array.
4455
     *
4456
     * @return static
4457
     *                <p>(Immutable)</p>
4458
     */
4459 2
    public function values(): self
4460
    {
4461 2
        return static::create(
4462
            function () {
4463
                /** @noinspection YieldFromCanBeUsedInspection */
4464 2
                foreach ($this->getGenerator() as $value) {
4465 2
                    yield $value;
4466
                }
4467 2
            },
4468 2
            $this->iteratorClass,
4469 2
            false
4470
        );
4471
    }
4472
4473
    /**
4474
     * Apply the given function to every element in the array, discarding the results.
4475
     *
4476
     * @param callable $callable
4477
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4478
     *
4479
     * @return static
4480
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4481
     */
4482 11
    public function walk($callable, bool $recursive = false): self
4483
    {
4484 11
        $this->generatorToArray();
4485
4486 11
        if ($recursive === true) {
4487 6
            \array_walk_recursive($this->array, $callable);
4488
        } else {
4489 5
            \array_walk($this->array, $callable);
4490
        }
4491
4492 11
        return $this;
4493
    }
4494
4495
    /**
4496
     * Convert an array into a object.
4497
     *
4498
     * @param array $array PHP array
4499
     *
4500
     * @return \stdClass
4501
     */
4502 4
    protected static function arrayToObject(array $array = []): \stdClass
4503
    {
4504
        // init
4505 4
        $object = new \stdClass();
4506
4507 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4508 1
            return $object;
4509
        }
4510
4511 3
        foreach ($array as $name => $value) {
4512 3
            if (\is_array($value)) {
4513 1
                $object->{$name} = self::arrayToObject($value);
4514
            } else {
4515 3
                $object->{$name} = $value;
4516
            }
4517
        }
4518
4519 3
        return $object;
4520
    }
4521
4522
    /**
4523
     * @param array|\Generator|null $input        <p>
4524
     *                                            An array containing keys to return.
4525
     *                                            </p>
4526
     * @param mixed                 $search_value [optional] <p>
4527
     *                                            If specified, then only keys containing these values are returned.
4528
     *                                            </p>
4529
     * @param bool                  $strict       [optional] <p>
4530
     *                                            Determines if strict comparison (===) should be used during the
4531
     *                                            search.
4532
     *                                            </p>
4533
     *
4534
     * @return array
4535
     *               <p>an array of all the keys in input</p>
4536
     */
4537 10
    protected function array_keys_recursive(
4538
        $input = null,
4539
        $search_value = null,
4540
        bool $strict = true
4541
    ): array {
4542
        // init
4543 10
        $keys = [];
4544 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4545
4546 10
        if ($input === null) {
4547
            $input = $this->getGenerator();
4548
        }
4549
4550 10
        foreach ($input as $key => $value) {
4551
            if (
4552 10
                $search_value === null
4553
                ||
4554
                (
4555
                    \is_array($search_value) === true
4556
                    &&
4557 10
                    \in_array($key, $search_value, $strict)
4558
                )
4559
            ) {
4560 10
                $keys[] = $key;
4561
            }
4562
4563
            // check if recursive is needed
4564 10
            if (\is_array($value) === true) {
4565 10
                $keysTmp[] = $this->array_keys_recursive($value);
4566
            }
4567
        }
4568
4569 10
        return \array_merge($keys, ...$keysTmp);
4570
    }
4571
4572
    /**
4573
     * @param mixed      $path
4574
     * @param callable   $callable
4575
     * @param array|null $currentOffset
4576
     */
4577 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4578
    {
4579 4
        $this->generatorToArray();
4580
4581 4
        if ($currentOffset === null) {
4582 4
            $currentOffset = &$this->array;
4583
        }
4584
4585 4
        $explodedPath = \explode($this->pathSeparator, $path);
4586 4
        if ($explodedPath === false) {
4587
            return;
4588
        }
4589
4590 4
        $nextPath = \array_shift($explodedPath);
4591
4592 4
        if (!isset($currentOffset[$nextPath])) {
4593
            return;
4594
        }
4595
4596 4
        if (!empty($explodedPath)) {
4597 1
            $this->callAtPath(
4598 1
                \implode($this->pathSeparator, $explodedPath),
4599 1
                $callable,
4600 1
                $currentOffset[$nextPath]
4601
            );
4602
        } else {
4603 4
            $callable($currentOffset[$nextPath]);
4604
        }
4605 4
    }
4606
4607
    /**
4608
     * create a fallback for array
4609
     *
4610
     * 1. use the current array, if it's a array
4611
     * 2. fallback to empty array, if there is nothing
4612
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4613
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4614
     * 5. call "__toArray()" on object, if the method exists
4615
     * 6. cast a string or object with "__toString()" into an array
4616
     * 7. throw a "InvalidArgumentException"-Exception
4617
     *
4618
     * @param mixed $data
4619
     *
4620
     * @throws \InvalidArgumentException
4621
     *
4622
     * @return array
4623
     */
4624 969
    protected function fallbackForArray(&$data): array
4625
    {
4626 969
        $data = $this->internalGetArray($data);
4627
4628 969
        if ($data === null) {
4629 2
            throw new \InvalidArgumentException(
4630 2
                'Passed value should be a array'
4631
            );
4632
        }
4633
4634 967
        return $data;
4635
    }
4636
4637
    /**
4638
     * Get correct PHP constant for direction.
4639
     *
4640
     * @param int|string $direction
4641
     *
4642
     * @return int
4643
     */
4644 39
    protected function getDirection($direction): int
4645
    {
4646 39
        if (\is_string($direction)) {
4647 10
            $direction = \strtolower($direction);
4648
4649 10
            if ($direction === 'desc') {
4650 2
                $direction = \SORT_DESC;
4651
            } else {
4652 8
                $direction = \SORT_ASC;
4653
            }
4654
        }
4655
4656
        if (
4657 39
            $direction !== \SORT_DESC
4658
            &&
4659 39
            $direction !== \SORT_ASC
4660
        ) {
4661
            $direction = \SORT_ASC;
4662
        }
4663
4664 39
        return $direction;
4665
    }
4666
4667
    /**
4668
     * @param mixed $glue
4669
     * @param mixed $pieces
4670
     * @param bool  $useKeys
4671
     *
4672
     * @return string
4673
     */
4674 36
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4675
    {
4676 36
        if ($pieces instanceof self) {
4677 1
            $pieces = $pieces->getArray();
4678
        }
4679
4680 36
        if (\is_array($pieces)) {
4681 36
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4682 36
            $pieces_count_not_zero = $pieces_count > 0;
4683
4684 36
            return \implode(
4685 36
                $glue,
4686 36
                \array_map(
4687 36
                    [$this, 'implode_recursive'],
4688 36
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4689 36
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4690
                )
4691
            );
4692
        }
4693
4694
        if (
4695 36
            \is_scalar($pieces)
4696
            ||
4697 36
            (\is_object($pieces) && \method_exists($pieces, '__toString'))
4698
        ) {
4699 31
            return (string) $pieces;
4700
        }
4701
4702 9
        return '';
4703
    }
4704
4705
    /**
4706
     * @param mixed                 $needle   <p>
4707
     *                                        The searched value.
4708
     *                                        </p>
4709
     *                                        <p>
4710
     *                                        If needle is a string, the comparison is done
4711
     *                                        in a case-sensitive manner.
4712
     *                                        </p>
4713
     * @param array|\Generator|null $haystack <p>
4714
     *                                        The array.
4715
     *                                        </p>
4716
     * @param bool                  $strict   [optional] <p>
4717
     *                                        If the third parameter strict is set to true
4718
     *                                        then the in_array function will also check the
4719
     *                                        types of the
4720
     *                                        needle in the haystack.
4721
     *                                        </p>
4722
     *
4723
     * @return bool
4724
     *              <p>true if needle is found in the array, false otherwise</p>
4725
     */
4726 18
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4727
    {
4728 18
        if ($haystack === null) {
4729
            $haystack = $this->getGenerator();
4730
        }
4731
4732 18
        foreach ($haystack as $item) {
4733 14
            if (\is_array($item) === true) {
4734 3
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4735
            } else {
4736
                /** @noinspection NestedPositiveIfStatementsInspection */
4737 14
                if ($strict === true) {
4738 14
                    $returnTmp = $item === $needle;
4739
                } else {
4740
                    $returnTmp = $item == $needle;
4741
                }
4742
            }
4743
4744 14
            if ($returnTmp === true) {
4745 14
                return true;
4746
            }
4747
        }
4748
4749 8
        return false;
4750
    }
4751
4752
    /**
4753
     * @param mixed $data
4754
     *
4755
     * @return array|null
4756
     */
4757 969
    protected function internalGetArray(&$data)
4758
    {
4759 969
        if (\is_array($data)) {
4760 966
            return $data;
4761
        }
4762
4763 49
        if (!$data) {
4764 6
            return [];
4765
        }
4766
4767 48
        $isObject = \is_object($data);
4768
4769 48
        if ($isObject && $data instanceof self) {
4770 2
            return $data->getArray();
4771
        }
4772
4773 47
        if ($isObject && $data instanceof \ArrayObject) {
4774
            return $data->getArrayCopy();
4775
        }
4776
4777 47
        if ($isObject && $data instanceof \Generator) {
4778
            return static::createFromGeneratorImmutable($data)->getArray();
4779
        }
4780
4781 47
        if ($isObject && $data instanceof \Traversable) {
4782
            return static::createFromObject($data)->getArray();
4783
        }
4784
4785 47
        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...
4786
            return (array) $data->jsonSerialize();
4787
        }
4788
4789 47
        if (\is_callable($data)) {
4790 40
            $this->generator = new ArrayyRewindableGenerator($data);
4791
4792 40
            return [];
4793
        }
4794
4795 9
        if ($isObject && \method_exists($data, '__toArray')) {
4796
            return (array) $data->__toArray();
4797
        }
4798
4799 9
        if (\is_scalar($data)) {
4800 7
            return [$data];
4801
        }
4802
4803 2
        if ($isObject && \method_exists($data, '__toString')) {
4804
            return [(string) $data];
4805
        }
4806
4807 2
        return null;
4808
    }
4809
4810
    /**
4811
     * Internal mechanics of remove method.
4812
     *
4813
     * @param mixed $key
4814
     *
4815
     * @return bool
4816
     */
4817 18
    protected function internalRemove($key): bool
4818
    {
4819 18
        $this->generatorToArray();
4820
4821
        if (
4822 18
            $this->pathSeparator
4823
            &&
4824 18
            \is_string($key)
4825
            &&
4826 18
            \strpos($key, $this->pathSeparator) !== false
4827
        ) {
4828
            $path = \explode($this->pathSeparator, (string) $key);
4829
4830
            if ($path !== false) {
4831
                // crawl though the keys
4832
                while (\count($path, \COUNT_NORMAL) > 1) {
4833
                    $key = \array_shift($path);
4834
4835
                    if (!$this->has($key)) {
4836
                        return false;
4837
                    }
4838
4839
                    $this->array = &$this->array[$key];
4840
                }
4841
4842
                $key = \array_shift($path);
4843
            }
4844
        }
4845
4846 18
        unset($this->array[$key]);
4847
4848 18
        return true;
4849
    }
4850
4851
    /**
4852
     * Internal mechanic of set method.
4853
     *
4854
     * @param int|string|null $key
4855
     * @param mixed           $value
4856
     * @param bool            $checkProperties
4857
     *
4858
     * @return bool
4859
     */
4860 846
    protected function internalSet($key, $value, $checkProperties = true): bool
4861
    {
4862
        if (
4863 846
            $checkProperties === true
4864
            &&
4865 846
            $this->properties !== []
4866
        ) {
4867 13
            if (isset($this->properties[$key]) === false) {
4868
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4869
            }
4870
4871 13
            $this->properties[$key]->checkType($value);
4872
        }
4873
4874 845
        if ($key === null) {
4875
            return false;
4876
        }
4877
4878 845
        $this->generatorToArray();
4879
4880 845
        $array = &$this->array;
4881
4882
        if (
4883 845
            $this->pathSeparator
4884
            &&
4885 845
            \is_string($key)
4886
            &&
4887 845
            \strpos($key, $this->pathSeparator) !== false
4888
        ) {
4889 3
            $path = \explode($this->pathSeparator, (string) $key);
4890
4891 3
            if ($path !== false) {
4892
                // crawl through the keys
4893 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4894 3
                    $key = \array_shift($path);
4895
4896 3
                    $array = &$array[$key];
4897
                }
4898
4899 3
                $key = \array_shift($path);
4900
            }
4901
        }
4902
4903 845
        $array[$key] = $value;
4904
4905 845
        return true;
4906
    }
4907
4908
    /**
4909
     * Convert a object into an array.
4910
     *
4911
     * @param object $object
4912
     *
4913
     * @return mixed
4914
     */
4915 5
    protected static function objectToArray($object)
4916
    {
4917 5
        if (!\is_object($object)) {
4918 4
            return $object;
4919
        }
4920
4921 5
        if (\is_object($object)) {
4922 5
            $object = \get_object_vars($object);
4923
        }
4924
4925 5
        return \array_map(['static', 'objectToArray'], $object);
4926
    }
4927
4928
    /**
4929
     * sorting keys
4930
     *
4931
     * @param array      $elements
4932
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4933
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4934
     *                              <strong>SORT_NATURAL</strong></p>
4935
     *
4936
     * @return static
4937
     *                <p>(Mutable) Return this Arrayy object.</p>
4938
     */
4939 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4940
    {
4941 18
        $direction = $this->getDirection($direction);
4942
4943
        switch ($direction) {
4944 18
            case 'desc':
4945 18
            case \SORT_DESC:
4946 6
                \krsort($elements, $strategy);
4947
4948 6
                break;
4949 13
            case 'asc':
4950 13
            case \SORT_ASC:
4951
            default:
4952 13
                \ksort($elements, $strategy);
4953
        }
4954
4955 18
        return $this;
4956
    }
4957
4958
    /**
4959
     * @param array      $elements  <p>Warning: used as reference</p>
4960
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4961
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4962
     *                              <strong>SORT_NATURAL</strong></p>
4963
     * @param bool       $keepKeys
4964
     *
4965
     * @return static
4966
     *                <p>(Mutable) Return this Arrayy object.</p>
4967
     */
4968 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4969
    {
4970 20
        $direction = $this->getDirection($direction);
4971
4972 20
        if (!$strategy) {
4973 20
            $strategy = \SORT_REGULAR;
4974
        }
4975
4976
        switch ($direction) {
4977 20
            case 'desc':
4978 20
            case \SORT_DESC:
4979 9
                if ($keepKeys) {
4980 5
                    \arsort($elements, $strategy);
4981
                } else {
4982 4
                    \rsort($elements, $strategy);
4983
                }
4984
4985 9
                break;
4986 11
            case 'asc':
4987 11
            case \SORT_ASC:
4988
            default:
4989 11
                if ($keepKeys) {
4990 4
                    \asort($elements, $strategy);
4991
                } else {
4992 7
                    \sort($elements, $strategy);
4993
                }
4994
        }
4995
4996 20
        return $this;
4997
    }
4998
4999
    /**
5000
     * @return bool
5001
     */
5002 889
    private function generatorToArray(): bool
5003
    {
5004 889
        if ($this->generator) {
5005 1
            $this->array = $this->getArray();
5006 1
            $this->generator = null;
5007
5008 1
            return true;
5009
        }
5010
5011 889
        return false;
5012
    }
5013
5014
    /**
5015
     * @return Property[]
5016
     */
5017 15
    private function getPropertiesFromPhpDoc(): array
5018
    {
5019 15
        static $PROPERTY_CACHE = [];
5020 15
        $cacheKey = 'Class::' . static::class;
5021
5022 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
5023 14
            return $PROPERTY_CACHE[$cacheKey];
5024
        }
5025
5026
        // init
5027 2
        $properties = [];
5028
5029 2
        $reflector = new \ReflectionClass($this);
5030 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
5031 2
        $docComment = $reflector->getDocComment();
5032 2
        if ($docComment) {
5033 2
            $docblock = $factory->create($docComment);
5034 2
            foreach ($docblock->getTagsByName('property') as $tag) {
5035
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
5036 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
5037
            }
5038
        }
5039
5040 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
5041
    }
5042
}
5043