Completed
Push — master ( b4c66b...783b82 )
by Lars
01:47
created

Arrayy::moveElementToFirstPlace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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