Completed
Pull Request — master (#26)
by Lars
02:56 queued 01:14
created

Arrayy::unique()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 24

Duplication

Lines 5
Ratio 20.83 %

Code Coverage

Tests 12
CRAP Score 3.004

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 5
loc 24
ccs 12
cts 13
cp 0.9231
crap 3.004
rs 9.536
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy;
6
7
/** @noinspection ClassReImplementsParentInterfaceInspection */
8
/** @noinspection PhpComposerExtensionStubsInspection */
9
10
/**
11
 * Methods to manage arrays.
12
 *
13
 * For the full copyright and license information, please view the LICENSE
14
 * file that was distributed with this source code.
15
 */
16
class Arrayy extends \ArrayObject implements \IteratorAggregate, \ArrayAccess, \Serializable, \JsonSerializable, \Countable
17
{
18
    /**
19
     * @var array
20
     */
21
    protected $array = [];
22
23
    /**
24
     * @var string
25
     */
26
    protected $iteratorClass = ArrayyIterator::class;
27
28
    /**
29
     * @var string
30
     */
31
    protected $pathSeparator = '.';
32
33
    /**
34
     * @var bool
35
     */
36
    protected $checkPropertyTypes = false;
37
38
    /**
39
     * @var bool
40
     */
41
    protected $checkForMissingPropertiesInConstructor = false;
42
43
    /**
44
     * @var bool
45
     */
46
    protected $checkPropertiesMismatchInConstructor = false;
47
48
    /**
49
     * @var array|Property[]
50
     */
51
    protected $properties = [];
52
53
    /** @noinspection MagicMethodsValidityInspection */
54
    /** @noinspection PhpMissingParentConstructorInspection */
55
56
    /**
57
     * Initializes
58
     *
59
     * @param mixed  $array                                  <p>
60
     *                                                       Should be an array, otherwise it will try to convert
61
     *                                                       it into an array.
62
     *                                                       </p>
63
     * @param string $iteratorClass                          optional <p>
64
     *                                                       You can overwrite the ArrayyIterator, but mostly you don't
65
     *                                                       need this option.
66
     *                                                       </p>
67
     * @param bool   $checkForMissingPropertiesInConstructor optional <p>
68
     *                                                       You need to extend the "Arrayy"-class and you need to set
69
     *                                                       the $checkPropertiesMismatchInConstructor class property to
70
     *                                                       true, otherwise this option didn't not work anyway.
71
     *                                                       </p>
72
     */
73 890
    public function __construct($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true)
74
    {
75 890
        $array = $this->fallbackForArray($array);
76
77
        // used only for serialize + unserialize, all other methods are overwritten
78 888
        parent::__construct([], 0, $iteratorClass);
79
80
        if (
81 888
            $this->checkPropertyTypes === true
82
            ||
83
            (
84 879
                $this->checkForMissingPropertiesInConstructor === true
85
                &&
86 888
                $checkForMissingPropertiesInConstructor === true
87
            )
88
        ) {
89 9
            $this->properties = $this->getPublicProperties();
90
        }
91
92
        if (
93 888
            $this->checkPropertiesMismatchInConstructor === true
94
            &&
95 888
            \count($array) !== 0
96
            &&
97 888
            \count(\array_diff_key($this->properties, $array)) > 0
98
        ) {
99 1
            throw new \InvalidArgumentException('Property mismatch - input: ' . \print_r(\array_keys($array), true) . ' | expected: ' . \print_r(\array_keys($this->properties), true));
100
        }
101
102 887
        \array_walk(
103
            $array,
104 887
            function (&$value, &$key) {
105 775
                $this->internalSet($key, $value);
106 887
            }
107
        );
108
109 884
        $this->setIteratorClass($iteratorClass);
110 884
    }
111
112
    /**
113
     * Get a value by key.
114
     *
115
     * @param mixed $key
116
     *
117
     * @return mixed
118
     *                <p>Get a Value from the current array.</p>
119
     */
120 3
    public function &__get($key)
121
    {
122 3
        $return = $this->get($key);
123
124 3
        if (\is_array($return)) {
125
            return static::create($return, $this->iteratorClass, false);
126
        }
127
128 3
        return $return;
129
    }
130
131
    /**
132
     * Call object as function.
133
     *
134
     * @param mixed $key
135
     *
136
     * @return mixed
137
     */
138 1
    public function __invoke($key = null)
139
    {
140 1
        if ($key !== null) {
141 1
            return $this->array[$key] ?? false;
142
        }
143
144
        return (array) $this->array;
145
    }
146
147
    /**
148
     * Whether or not an element exists by key.
149
     *
150
     * @param mixed $key
151
     *
152
     * @return bool
153
     *              <p>True is the key/index exists, otherwise false.</p>
154
     */
155
    public function __isset($key)
156
    {
157
        return $this->offsetExists($key);
158
    }
159
160
    /**
161
     * Assigns a value to the specified element.
162
     *
163
     * @param mixed $key
164
     * @param mixed $value
165
     */
166 2
    public function __set($key, $value)
167
    {
168 2
        $this->internalSet($key, $value);
169 2
    }
170
171
    /**
172
     * magic to string
173
     *
174
     * @return string
175
     */
176 15
    public function __toString()
177
    {
178 15
        return $this->toString();
179
    }
180
181
    /**
182
     * Unset element by key.
183
     *
184
     * @param mixed $key
185
     */
186
    public function __unset($key)
187
    {
188
        $this->internalRemove($key);
189
    }
190
191
    /**
192
     * alias: for "Arrayy->append()"
193
     *
194
     * @see Arrayy::append()
195
     *
196
     * @param mixed $value
197
     *
198
     * @return static
199
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
200
     */
201 1
    public function add($value)
202
    {
203 1
        return $this->append($value);
204
    }
205
206
    /**
207
     * Append a (key) + value to the current array.
208
     *
209
     * @param mixed $value
210
     * @param mixed $key
211
     *
212
     * @return static
213
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
214
     */
215 9
    public function append($value, $key = null)
216
    {
217 9
        if ($key !== null) {
218
            if (
219
                isset($this->array[$key])
220
                &&
221
                \is_array($this->array[$key])
222
            ) {
223
                $this->array[$key][] = $value;
224
            } else {
225
                $this->array[$key] = $value;
226
            }
227
        } else {
228 9
            $this->array[] = $value;
229
        }
230
231 9
        return $this;
232
    }
233
234
    /**
235
     * Sort the entries by value.
236
     *
237
     * @param int $sort_flags [optional] <p>
238
     *                        You may modify the behavior of the sort using the optional
239
     *                        parameter sort_flags, for details
240
     *                        see sort.
241
     *                        </p>
242
     *
243
     * @return static
244
     *                <p>(Mutable) Return this Arrayy object.</p>
245
     */
246 4
    public function asort(int $sort_flags = 0)
247
    {
248 4
        \asort($this->array, $sort_flags);
249
250 4
        return $this;
251
    }
252
253
    /**
254
     * Counts all elements in an array, or something in an object.
255
     *
256
     * <p>
257
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
258
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
259
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
260
     * implemented and used in PHP.
261
     * </p>
262
     *
263
     * @see http://php.net/manual/en/function.count.php
264
     *
265
     * @param int $mode [optional] If the optional mode parameter is set to
266
     *                  COUNT_RECURSIVE (or 1), count
267
     *                  will recursively count the array. This is particularly useful for
268
     *                  counting all the elements of a multidimensional array. count does not detect infinite recursion.
269
     *
270
     * @return int
271
     *              <p>
272
     *              The number of elements in var, which is
273
     *              typically an array, since anything else will have one
274
     *              element.
275
     *              </p>
276
     *              <p>
277
     *              If var is not an array or an object with
278
     *              implemented Countable interface,
279
     *              1 will be returned.
280
     *              There is one exception, if var is &null;,
281
     *              0 will be returned.
282
     *              </p>
283
     *              <p>
284
     *              Caution: count may return 0 for a variable that isn't set,
285
     *              but it may also return 0 for a variable that has been initialized with an
286
     *              empty array. Use isset to test if a variable is set.
287
     *              </p>
288
     */
289 37
    public function count(int $mode = \COUNT_NORMAL): int
290
    {
291 37
        return \count($this->array, $mode);
292
    }
293
294
    /**
295
     * Exchange the array for another one.
296
     *
297
     * @param array|static $data
298
     *
299
     * @return array
300
     */
301 1
    public function exchangeArray($data): array
302
    {
303 1
        $this->array = $this->fallbackForArray($data);
304
305 1
        return $this->array;
306
    }
307
308
    /**
309
     * Creates a copy of the ArrayyObject.
310
     *
311
     * @return array
312
     */
313 1
    public function getArrayCopy(): array
314
    {
315 1
        return $this->array;
316
    }
317
318
    /**
319
     * Returns a new ArrayyIterator, thus implementing the \ArrayIterator interface.
320
     *
321
     * @return \ArrayIterator
322
     *                         <p>An iterator for the values in the array.</p>
323
     */
324 12
    public function getIterator(): \ArrayIterator
325
    {
326 12
        $iterator = $this->getIteratorClass();
327
328 12
        return new $iterator($this->array);
329
    }
330
331
    /**
332
     * Gets the iterator classname for the ArrayObject.
333
     *
334
     * @return string
335
     */
336 12
    public function getIteratorClass(): string
337
    {
338 12
        return $this->iteratorClass;
339
    }
340
341
    /**
342
     * Sort the entries by key
343
     *
344
     * @param int $sort_flags [optional] <p>
345
     *                        You may modify the behavior of the sort using the optional
346
     *                        parameter sort_flags, for details
347
     *                        see sort.
348
     *                        </p>
349
     *
350
     * @return static
351
     *                 <p>(Mutable) Return this Arrayy object.</p>
352
     */
353 4
    public function ksort(int $sort_flags = 0)
354
    {
355 4
        \ksort($this->array, $sort_flags);
356
357 4
        return $this;
358
    }
359
360
    /**
361
     * Sort an array using a case insensitive "natural order" algorithm
362
     *
363
     * @return static
364
     *                 <p>(Mutable) Return this Arrayy object.</p>
365
     */
366
    public function natcasesort()
367
    {
368
        \natcasesort($this->array);
369
370
        return $this;
371
    }
372
373
    /**
374
     * Sort entries using a "natural order" algorithm
375
     *
376
     * @return static
377
     *                 <p>(Mutable) Return this Arrayy object.</p>
378
     */
379 1
    public function natsort()
380
    {
381 1
        \natsort($this->array);
382
383 1
        return $this;
384
    }
385
386
    /**
387
     * Whether or not an offset exists.
388
     *
389
     * @param bool|float|int|string $offset
390
     *
391
     * @return bool
392
     */
393 44
    public function offsetExists($offset): bool
394
    {
395 44
        if ($this->isEmpty()) {
396 4
            return false;
397
        }
398
399
        // php cast "bool"-index into "int"-index
400 40
        if ((bool) $offset === $offset) {
401 1
            $offset = (int) $offset;
402
        }
403
404 40
        $tmpReturn = \array_key_exists($offset, $this->array);
405
406
        if (
407 40
            $tmpReturn === true
408
            ||
409
            (
410 16
                $tmpReturn === false
411
                &&
412 40
                \strpos((string) $offset, $this->pathSeparator) === false
413
            )
414
        ) {
415 38
            return $tmpReturn;
416
        }
417
418 3
        $offsetExists = false;
419
420 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
421 3
            $offsetExists = false;
422 3
            $explodedPath = \explode($this->pathSeparator, (string) $offset);
423 3
            $lastOffset = \array_pop($explodedPath);
424 3
            $containerPath = \implode($this->pathSeparator, $explodedPath);
425
426 3
            $this->callAtPath(
427 3
                $containerPath,
428 3
                function ($container) use ($lastOffset, &$offsetExists) {
429 3
                    $offsetExists = \array_key_exists($lastOffset, $container);
430 3
                }
431
            );
432
        }
433
434 3
        return $offsetExists;
435
    }
436
437
    /**
438
     * Returns the value at specified offset.
439
     *
440
     * @param float|int|string $offset
441
     *
442
     * @return mixed
443
     *                <p>Will return null if the offset did not exists.</p>
444
     */
445 30
    public function offsetGet($offset)
446
    {
447 30
        return $this->offsetExists($offset) ? $this->get($offset) : null;
448
    }
449
450
    /**
451
     * Assigns a value to the specified offset.
452
     *
453
     * @param int|string|null $offset
454
     * @param mixed           $value
455
     */
456 20
    public function offsetSet($offset, $value)
457
    {
458 20
        if ($offset === null) {
459 4
            $this->array[] = $value;
460
        } else {
461 16
            $this->internalSet($offset, $value);
462
        }
463 20
    }
464
465
    /**
466
     * Unset an offset.
467
     *
468
     * @param float|int|string $offset
469
     */
470 7
    public function offsetUnset($offset)
471
    {
472 7
        if ($this->isEmpty()) {
473 1
            return;
474
        }
475
476 6
        if (\array_key_exists($offset, $this->array)) {
477 4
            unset($this->array[$offset]);
478
479 4
            return;
480
        }
481
482 3
        if (\strpos((string) $offset, $this->pathSeparator) !== false) {
483 2
            $path = \explode($this->pathSeparator, (string) $offset);
484 2
            $pathToUnset = \array_pop($path);
485
486 2
            $this->callAtPath(
487 2
                \implode($this->pathSeparator, $path),
488 2
                function (&$offset) use ($pathToUnset) {
489 2
                    unset($offset[$pathToUnset]);
490 2
                }
491
            );
492
        }
493 3
    }
494
495
    /** @noinspection SenselessProxyMethodInspection | can not add return type, because of the "Serializable" interface */
496
497
    /**
498
     * Serialize the current "Arrayy"-object.
499
     *
500
     * @return string
501
     */
502 2
    public function serialize()
503
    {
504 2
        return parent::serialize();
505
    }
506
507
    /**
508
     * Sets the iterator classname for the current "Arrayy"-object.
509
     *
510
     * @param string $class
511
     *
512
     * @throws \InvalidArgumentException
513
     *
514
     * @return void
515
     */
516 884
    public function setIteratorClass($class)
517
    {
518 884
        if (\class_exists($class)) {
519 884
            $this->iteratorClass = $class;
520
521 884
            return;
522
        }
523
524
        if (\strpos($class, '\\') === 0) {
525
            $class = '\\' . $class;
526
            if (\class_exists($class)) {
527
                $this->iteratorClass = $class;
528
529
                return;
530
            }
531
        }
532
533
        throw new \InvalidArgumentException('The iterator class does not exist: ' . $class);
534
    }
535
536
    /**
537
     * Sort the entries with a user-defined comparison function and maintain key association.
538
     *
539
     * @param \callable $function
540
     *
541
     * @throws \InvalidArgumentException
542
     *
543
     * @return static
544
     *                <p>(Mutable) Return this Arrayy object.</p>
545
     */
546 View Code Duplication
    public function uasort($function)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
547
    {
548
        if (!\is_callable($function)) {
549
            throw new \InvalidArgumentException(
550
                'Passed function must be callable'
551
            );
552
        }
553
554
        \uasort($this->array, $function);
555
556
        return $this;
557
    }
558
559
    /**
560
     * Sort the entries by keys using a user-defined comparison function.
561
     *
562
     * @param \callable $function
563
     *
564
     * @throws \InvalidArgumentException
565
     *
566
     * @return static
567
     *                <p>(Mutable) Return this Arrayy object.</p>
568
     */
569 5
    public function uksort($function)
570
    {
571 5
        return $this->customSortKeys($function);
572
    }
573
574
    /**
575
     * Unserialize an string and return the instance of the "Arrayy"-class.
576
     *
577
     * @param string $string
578
     *
579
     * @return static
580
     */
581 2
    public function unserialize($string)
582
    {
583 2
        parent::unserialize($string);
584
585 2
        return $this;
586
    }
587
588
    /**
589
     * Append a (key) + values to the current array.
590
     *
591
     * @param array $values
592
     * @param mixed $key
593
     *
594
     * @return static
595
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
596
     */
597 1
    public function appendArrayValues(array $values, $key = null)
598
    {
599 1
        if ($key !== null) {
600
            if (
601 1
                isset($this->array[$key])
602
                &&
603 1
                \is_array($this->array[$key])
604
            ) {
605 1
                foreach ($values as $value) {
606 1
                    $this->array[$key][] = $value;
607
                }
608
            } else {
609
                foreach ($values as $value) {
610 1
                    $this->array[$key] = $value;
611
                }
612
            }
613
        } else {
614
            foreach ($values as $value) {
615
                $this->array[] = $value;
616
            }
617
        }
618
619 1
        return $this;
620
    }
621
622
    /**
623
     * Add a suffix to each key.
624
     *
625
     * @param mixed $prefix
626
     *
627
     * @return static
628
     *                <p>(Immutable) Return an Arrayy object, with the prefixed keys.</p>
629
     */
630 10 View Code Duplication
    public function appendToEachKey($prefix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
631
    {
632
        // init
633 10
        $result = [];
634
635 10
        foreach ($this->getGenerator() as $key => $item) {
636 9
            if ($item instanceof self) {
637
                $result[$prefix . $key] = $item->appendToEachKey($prefix);
638 9
            } elseif (\is_array($item)) {
639
                $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
640
                                              ->appendToEachKey($prefix)
641
                                              ->toArray();
642
            } else {
643 9
                $result[$prefix . $key] = $item;
644
            }
645
        }
646
647 10
        return self::create($result, $this->iteratorClass, false);
648
    }
649
650
    /**
651
     * Add a prefix to each value.
652
     *
653
     * @param mixed $prefix
654
     *
655
     * @return static
656
     *                <p>(Immutable) Return an Arrayy object, with the prefixed values.</p>
657
     */
658 10 View Code Duplication
    public function appendToEachValue($prefix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
659
    {
660
        // init
661 10
        $result = [];
662
663 10
        foreach ($this->getGenerator() as $key => $item) {
664 9
            if ($item instanceof self) {
665
                $result[$key] = $item->appendToEachValue($prefix);
666 9
            } elseif (\is_array($item)) {
667
                $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
668 9
            } elseif (\is_object($item)) {
669 1
                $result[$key] = $item;
670
            } else {
671 9
                $result[$key] = $prefix . $item;
672
            }
673
        }
674
675 10
        return self::create($result, $this->iteratorClass, false);
676
    }
677
678
    /**
679
     * Convert an array into a object.
680
     *
681
     * @param array $array PHP array
682
     *
683
     * @return \stdClass
684
     */
685 4
    protected static function arrayToObject(array $array = []): \stdClass
686
    {
687
        // init
688 4
        $object = new \stdClass();
689
690 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
691 1
            return $object;
692
        }
693
694 3
        foreach ($array as $name => $value) {
695 3
            if (\is_array($value)) {
696 1
                $object->{$name} = self::arrayToObject($value);
697
            } else {
698 3
                $object->{$name} = $value;
699
            }
700
        }
701
702 3
        return $object;
703
    }
704
705
    /**
706
     * @param array|\Generator|null $input        <p>
707
     *                                            An array containing keys to return.
708
     *                                            </p>
709
     * @param mixed                 $search_value [optional] <p>
710
     *                                            If specified, then only keys containing these values are returned.
711
     *                                            </p>
712
     * @param bool                  $strict       [optional] <p>
713
     *                                            Determines if strict comparison (===) should be used during the
714
     *                                            search.
715
     *                                            </p>
716
     *
717
     * @return array
718
     *               <p>an array of all the keys in input</p>
719
     */
720 10
    protected function array_keys_recursive($input = null, $search_value = null, bool $strict = true): array
721
    {
722
        // init
723 10
        $keys = [];
724 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
725
726 10
        if ($input === null) {
727
            $input = $this->getGenerator();
728
        }
729
730 10
        foreach ($input as $key => $value) {
731
            if (
732 10
                $search_value === null
733
                ||
734
                (
735
                    \is_array($search_value) === true
736
                    &&
737 10
                    \in_array($key, $search_value, $strict)
738
                )
739
            ) {
740 10
                $keys[] = $key;
741
            }
742
743
            // check if recursive is needed
744 10
            if (\is_array($value) === true) {
745 10
                $keysTmp[] = $this->array_keys_recursive($value);
746
            }
747
        }
748
749 10
        return \array_merge($keys, ...$keysTmp);
750
    }
751
752
    /**
753
     * Sort an array in reverse order and maintain index association.
754
     *
755
     * @return static
756
     *                 <p>(Mutable) Return this Arrayy object.</p>
757
     */
758 4
    public function arsort()
759
    {
760 4
        \arsort($this->array);
761
762 4
        return $this;
763
    }
764
765
    /**
766
     * Iterate over the current array and execute a callback for each loop.
767
     *
768
     * @param \Closure $closure
769
     *
770
     * @return static
771
     *                <p>(Immutable)</p>
772
     */
773 2
    public function at(\Closure $closure)
774
    {
775 2
        $arrayy = clone $this;
776
777 2
        foreach ($arrayy->getGenerator() as $key => $value) {
778 2
            $closure($value, $key);
779
        }
780
781 2
        return static::create(
782 2
            $arrayy->toArray(),
783 2
            $this->iteratorClass,
784 2
            false
785
        );
786
    }
787
788
    /**
789
     * Returns the average value of the current array.
790
     *
791
     * @param int $decimals <p>The number of decimal-numbers to return.</p>
792
     *
793
     * @return float|int
794
     *                   <p>The average value.</p>
795
     */
796 10
    public function average($decimals = 0)
797
    {
798 10
        $count = \count($this->array, \COUNT_NORMAL);
799
800 10
        if (!$count) {
801 2
            return 0;
802
        }
803
804 8
        if (!\is_int($decimals)) {
805 3
            $decimals = 0;
806
        }
807
808 8
        return \round(\array_sum($this->array) / $count, $decimals);
809
    }
810
811
    /**
812
     * @param mixed      $path
813
     * @param \callable  $callable
814
     * @param array|null $currentOffset
815
     */
816 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
817
    {
818 4
        if ($currentOffset === null) {
819 4
            $currentOffset = &$this->array;
820
        }
821
822 4
        $explodedPath = \explode($this->pathSeparator, $path);
823 4
        $nextPath = \array_shift($explodedPath);
824
825 4
        if (!isset($currentOffset[$nextPath])) {
826
            return;
827
        }
828
829 4
        if (!empty($explodedPath)) {
830 1
            $this->callAtPath(
831 1
                \implode($this->pathSeparator, $explodedPath),
832 1
                $callable,
833 1
                $currentOffset[$nextPath]
834
            );
835
        } else {
836 4
            $callable($currentOffset[$nextPath]);
837
        }
838 4
    }
839
840
    /**
841
     * Changes all keys in an array.
842
     *
843
     * @param int $case [optional] <p> Either <strong>CASE_UPPER</strong><br />
844
     *                  or <strong>CASE_LOWER</strong> (default)</p>
845
     *
846
     * @return static
847
     *                 <p>(Immutable)</p>
848
     */
849 1
    public function changeKeyCase(int $case = \CASE_LOWER)
850
    {
851
        if (
852 1
            $case !== \CASE_LOWER
853
            &&
854 1
            $case !== \CASE_UPPER
855
        ) {
856
            $case = \CASE_LOWER;
857
        }
858
859 1
        $return = [];
860 1
        foreach ($this->array as $key => $value) {
861 1
            if ($case === \CASE_LOWER) {
862
                /** @noinspection PhpComposerExtensionStubsInspection */
863 1
                $key = \mb_strtolower((string) $key);
864
            } else {
865
                /** @noinspection PhpComposerExtensionStubsInspection */
866 1
                $key = \mb_strtoupper((string) $key);
867
            }
868
869 1
            $return[$key] = $value;
870
        }
871
872 1
        return static::create(
873 1
            $return,
874 1
            $this->iteratorClass,
875 1
            false
876
        );
877
    }
878
879
    /**
880
     * Change the path separator of the array wrapper.
881
     *
882
     * By default, the separator is: "."
883
     *
884
     * @param string $separator <p>Separator to set.</p>
885
     *
886
     * @return static
887
     *                <p>Mutable</p>
888
     */
889 1
    public function changeSeparator($separator)
890
    {
891 1
        $this->pathSeparator = $separator;
892
893 1
        return $this;
894
    }
895
896
    /**
897
     * Create a chunked version of the current array.
898
     *
899
     * @param int  $size         <p>Size of each chunk.</p>
900
     * @param bool $preserveKeys <p>Whether array keys are preserved or no.</p>
901
     *
902
     * @return static
903
     *                <p>(Immutable) A new array of chunks from the original array.</p>
904
     */
905 4
    public function chunk($size, $preserveKeys = false)
906
    {
907 4
        return static::create(
908 4
            \array_chunk($this->array, $size, $preserveKeys),
909 4
            $this->iteratorClass,
910 4
            false
911
        );
912
    }
913
914
    /**
915
     * Clean all falsy values from the current array.
916
     *
917
     * @return static
918
     *                 <p>(Immutable)</p>
919
     */
920 8
    public function clean()
921
    {
922 8
        return $this->filter(
923 8
            function ($value) {
924 7
                return (bool) $value;
925 8
            }
926
        );
927
    }
928
929
    /**
930
     * WARNING!!! -> Clear the current array.
931
     *
932
     * @return static
933
     *                <p>(Mutable) Return this Arrayy object, with an empty array.</p>
934
     */
935 4
    public function clear()
936
    {
937 4
        $this->array = [];
938
939 4
        return $this;
940
    }
941
942
    /**
943
     * Check if an item is in the current array.
944
     *
945
     * @param float|int|string $value
946
     * @param bool             $recursive
947
     * @param bool             $strict
948
     *
949
     * @return bool
950
     */
951 22
    public function contains($value, $recursive = false, $strict = true): bool
952
    {
953 22
        if ($recursive === true) {
954 18
            return $this->in_array_recursive($value, $this->array, $strict);
955
        }
956
957 13
        return \in_array($value, $this->array, $strict);
958
    }
959
960
    /**
961
     * Check if an (case-insensitive) string is in the current array.
962
     *
963
     * @param string $value
964
     * @param bool   $recursive
965
     *
966
     * @return bool
967
     */
968 26
    public function containsCaseInsensitive($value, $recursive = false): bool
969
    {
970 26
        if ($recursive === true) {
971
            /** @noinspection PhpComposerExtensionStubsInspection */
972 26
            return $this->in_array_recursive(
973 26
                \mb_strtoupper((string) $value),
974 26
                $this->walk(
975 26
                    function (&$val) {
976
                        /** @noinspection PhpComposerExtensionStubsInspection */
977 22
                        $val = \mb_strtoupper((string) $val);
978 26
                    },
979 26
                    true
980 26
                )->getArray(),
981 26
                true
982
            );
983
        }
984
985
        /** @noinspection PhpComposerExtensionStubsInspection */
986 13
        return \in_array(
987 13
            \mb_strtoupper((string) $value),
988 13
            $this->walk(
989 13
                function (&$val) {
990
                    /** @noinspection PhpComposerExtensionStubsInspection */
991 11
                    $val = \mb_strtoupper((string) $val);
992 13
                },
993 13
                false
994 13
            )->getArray(),
995 13
            true
996
        );
997
    }
998
999
    /**
1000
     * Check if the given key/index exists in the array.
1001
     *
1002
     * @param float|int|string $key <p>key/index to search for</p>
1003
     *
1004
     * @return bool
1005
     *               <p>Returns true if the given key/index exists in the array, false otherwise.</p>
1006
     */
1007 4
    public function containsKey($key): bool
1008
    {
1009 4
        return $this->offsetExists($key);
1010
    }
1011
1012
    /**
1013
     * Check if all given needles are present in the array as key/index.
1014
     *
1015
     * @param array $needles <p>The keys you are searching for.</p>
1016
     * @param bool  $recursive
1017
     *
1018
     * @return bool
1019
     *              <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1020
     */
1021 2
    public function containsKeys(array $needles, $recursive = false): bool
1022
    {
1023 2
        if ($recursive === true) {
1024 2
            return \count(
1025 2
                       \array_intersect($needles, $this->keys(true)->getArray()),
1026 2
                       \COUNT_RECURSIVE
1027
                   )
1028
                   ===
1029 2
                   \count(
1030 2
                       $needles,
1031 2
                       \COUNT_RECURSIVE
1032
                   );
1033
        }
1034
1035 1
        return \count(
1036 1
                   \array_intersect($needles, $this->keys()->getArray()),
1037 1
                   \COUNT_NORMAL
1038
               )
1039
               ===
1040 1
               \count(
1041 1
                   $needles,
1042 1
                   \COUNT_NORMAL
1043
               );
1044
    }
1045
1046
    /**
1047
     * Check if all given needles are present in the array as key/index.
1048
     *
1049
     * @param array $needles <p>The keys you are searching for.</p>
1050
     *
1051
     * @return bool
1052
     *               <p>Returns true if all the given keys/indexes exists in the array, false otherwise.</p>
1053
     */
1054 1
    public function containsKeysRecursive(array $needles): bool
1055
    {
1056 1
        return $this->containsKeys($needles, true);
1057
    }
1058
1059
    /**
1060
     * alias: for "Arrayy->contains()"
1061
     *
1062
     * @see Arrayy::contains()
1063
     *
1064
     * @param float|int|string $value
1065
     *
1066
     * @return bool
1067
     */
1068 9
    public function containsValue($value): bool
1069
    {
1070 9
        return $this->contains($value);
1071
    }
1072
1073
    /**
1074
     * alias: for "Arrayy->contains($value, true)"
1075
     *
1076
     * @see Arrayy::contains()
1077
     *
1078
     * @param float|int|string $value
1079
     *
1080
     * @return bool
1081
     */
1082 18
    public function containsValueRecursive($value): bool
1083
    {
1084 18
        return $this->contains($value, true);
1085
    }
1086
1087
    /**
1088
     * Check if all given needles are present in the array.
1089
     *
1090
     * @param array $needles
1091
     *
1092
     * @return bool
1093
     *               <p>Returns true if all the given values exists in the array, false otherwise.</p>
1094
     */
1095 1
    public function containsValues(array $needles): bool
1096
    {
1097 1
        return \count(\array_intersect($needles, $this->array), \COUNT_NORMAL)
1098
               ===
1099 1
               \count($needles, \COUNT_NORMAL);
1100
    }
1101
1102
    /**
1103
     * Counts all the values of an array
1104
     *
1105
     * @see http://php.net/manual/en/function.array-count-values.php
1106
     *
1107
     * @return static
1108
     *                <p>
1109
     *                (Immutable)
1110
     *                An associative Arrayy-object of values from input as
1111
     *                keys and their count as value.
1112
     *                </p>
1113
     */
1114 1
    public function countValues(): self
1115
    {
1116 1
        return new static(\array_count_values($this->array));
1117
    }
1118
1119
    /**
1120
     * Creates an Arrayy object.
1121
     *
1122
     * @param mixed  $array
1123
     * @param string $iteratorClass
1124
     * @param bool   $checkForMissingPropertiesInConstructor
1125
     *
1126
     * @return static
1127
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1128
     */
1129 535
    public static function create($array = [], string $iteratorClass = ArrayyIterator::class, bool $checkForMissingPropertiesInConstructor = true): self
1130
    {
1131 535
        return new static($array, $iteratorClass, $checkForMissingPropertiesInConstructor);
1132
    }
1133
1134
    /**
1135
     * WARNING: Creates an Arrayy object by reference.
1136
     *
1137
     * @param array $array
1138
     *
1139
     * @return static
1140
     *                 <p>(Mutable) Return this Arrayy object.</p>
1141
     */
1142 1
    public function createByReference(array &$array = []): self
1143
    {
1144 1
        $array = $this->fallbackForArray($array);
1145
1146 1
        $this->array = &$array;
1147
1148 1
        return $this;
1149
    }
1150
1151
    /**
1152
     * Create an new Arrayy object via JSON.
1153
     *
1154
     * @param string $json
1155
     *
1156
     * @return static
1157
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1158
     */
1159 5
    public static function createFromJson(string $json)
1160
    {
1161
        /** @noinspection PhpComposerExtensionStubsInspection */
1162 5
        return static::create(\json_decode($json, true));
1163
    }
1164
1165
    /**
1166
     * Create an new instance filled with values from an object that have implemented ArrayAccess.
1167
     *
1168
     * @param \ArrayAccess $object <p>Object that implements ArrayAccess</p>
1169
     *
1170
     * @return static
1171
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1172
     */
1173 4
    public static function createFromObject(\ArrayAccess $object)
1174
    {
1175
        // init
1176 4
        $array = new static();
1177
1178 4
        if ($object instanceof self) {
1179 4
            $objectArray = $object->getGenerator();
1180
        } else {
1181
            $objectArray = $object;
1182
        }
1183
1184 4
        foreach ($objectArray as $key => $value) {
1185 3
            $array[$key] = $value;
1186
        }
1187
1188 4
        return $array;
1189
    }
1190
1191
    /**
1192
     * Create an new instance filled with a copy of values from a "Generator"-object.
1193
     *
1194
     * @param \Generator $generator
1195
     *
1196
     * @return static
1197
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1198
     */
1199 4
    public static function createFromGeneratorImmutable(\Generator $generator)
1200
    {
1201
        // init
1202 4
        $array = new static();
1203
1204 4
        foreach ($generator as $key => $value) {
1205 3
            $array[$key] = $value;
1206
        }
1207
1208 4
        return $array;
1209
    }
1210
1211
    /**
1212
     * Create an new instance filled with values from an object.
1213
     *
1214
     * @param object $object
1215
     *
1216
     * @return static
1217
     *                 <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1218
     */
1219 5
    public static function createFromObjectVars($object): self
1220
    {
1221 5
        return new static(self::objectToArray($object));
1222
    }
1223
1224
    /**
1225
     * Create an new Arrayy object via string.
1226
     *
1227
     * @param string      $str       <p>The input string.</p>
1228
     * @param string|null $delimiter <p>The boundary string.</p>
1229
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1230
     *                               used.</p>
1231
     *
1232
     * @return static
1233
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1234
     */
1235 8
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null)
1236
    {
1237 8
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1238 1
            \preg_match_all($regEx, $str, $array);
1239
1240 1
            if (!empty($array)) {
1241 1
                $array = $array[0];
1242
            }
1243
        } else {
1244 7
            $array = \explode($delimiter, $str);
1245
        }
1246
1247
        // trim all string in the array
1248 8
        \array_walk(
1249
            $array,
1250 8
            function (&$val) {
1251
                /** @noinspection ReferenceMismatchInspection */
1252 8
                if (\is_string($val)) {
1253 8
                    $val = \trim($val);
1254
                }
1255 8
            }
1256
        );
1257
1258 8
        return static::create($array);
1259
    }
1260
1261
    /**
1262
     * Create an new instance containing a range of elements.
1263
     *
1264
     * @param mixed $low  <p>First value of the sequence.</p>
1265
     * @param mixed $high <p>The sequence is ended upon reaching the end value.</p>
1266
     * @param int   $step <p>Used as the increment between elements in the sequence.</p>
1267
     *
1268
     * @return static
1269
     *                 <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1270
     */
1271 1
    public static function createWithRange($low, $high, int $step = 1)
1272
    {
1273 1
        return static::create(\range($low, $high, $step));
1274
    }
1275
1276
    /**
1277
     * Custom sort by index via "uksort".
1278
     *
1279
     * @see http://php.net/manual/en/function.uksort.php
1280
     *
1281
     * @param \callable $function
1282
     *
1283
     * @throws \InvalidArgumentException
1284
     *
1285
     * @return static
1286
     *                 <p>(Mutable) Return this Arrayy object.</p>
1287
     */
1288 5 View Code Duplication
    public function customSortKeys($function)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1289
    {
1290 5
        if (!\is_callable($function)) {
1291
            throw new \InvalidArgumentException(
1292
                'Passed function must be callable'
1293
            );
1294
        }
1295
1296 5
        \uksort($this->array, $function);
1297
1298 5
        return $this;
1299
    }
1300
1301
    /**
1302
     * Custom sort by value via "usort".
1303
     *
1304
     * @see http://php.net/manual/en/function.usort.php
1305
     *
1306
     * @param \callable $function
1307
     *
1308
     * @throws \InvalidArgumentException
1309
     *
1310
     * @return static
1311
     *                 <p>(Mutable) Return this Arrayy object.</p>
1312
     */
1313 5 View Code Duplication
    public function customSortValues($function)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1314
    {
1315 5
        if (!\is_callable($function)) {
1316
            throw new \InvalidArgumentException(
1317
                'Passed function must be callable'
1318
            );
1319
        }
1320
1321 5
        \usort($this->array, $function);
1322
1323 5
        return $this;
1324
    }
1325
1326
    /**
1327
     * Return values that are only in the current array.
1328
     *
1329
     * @param array $array
1330
     *
1331
     * @return static
1332
     *                <p>(Immutable)</p>
1333
     */
1334 12
    public function diff(array $array = [])
1335
    {
1336 12
        $result = \array_diff($this->array, $array);
1337
1338 12
        return static::create($result, $this->iteratorClass, false);
1339
    }
1340
1341
    /**
1342
     * Return values that are only in the current multi-dimensional array.
1343
     *
1344
     * @param array      $array
1345
     * @param array|null $helperVariableForRecursion <p>(only for internal usage)</p>
1346
     *
1347
     * @return static
1348
     *                <p>(Immutable)</p>
1349
     */
1350 1
    public function diffRecursive(array $array = [], $helperVariableForRecursion = null)
1351
    {
1352
        // init
1353 1
        $result = [];
1354
1355
        if (
1356 1
            $helperVariableForRecursion !== null
1357
            &&
1358 1
            \is_array($helperVariableForRecursion)
1359
        ) {
1360
            $arrayForTheLoop = $helperVariableForRecursion;
1361
        } else {
1362 1
            $arrayForTheLoop = $this->getGenerator();
1363
        }
1364
1365 1
        foreach ($arrayForTheLoop as $key => $value) {
1366 1
            if ($value instanceof self) {
1367
                $value = $value->getArray();
1368
            }
1369
1370 1
            if (\array_key_exists($key, $array)) {
1371 1
                if ($value !== $array[$key]) {
1372 1
                    $result[$key] = $value;
1373
                }
1374
            } else {
1375 1
                $result[$key] = $value;
1376
            }
1377
        }
1378
1379 1
        return static::create($result, $this->iteratorClass, false);
1380
    }
1381
1382
    /**
1383
     * Return values that are only in the new $array.
1384
     *
1385
     * @param array $array
1386
     *
1387
     * @return static
1388
     *                <p>(Immutable)</p>
1389
     */
1390 8
    public function diffReverse(array $array = [])
1391
    {
1392 8
        return static::create(
1393 8
            \array_diff($array, $this->array),
1394 8
            $this->iteratorClass,
1395 8
            false
1396
        );
1397
    }
1398
1399
    /**
1400
     * Divide an array into two arrays. One with keys and the other with values.
1401
     *
1402
     * @return static
1403
     *                <p>(Immutable)</p>
1404
     */
1405 1
    public function divide()
1406
    {
1407 1
        return static::create(
1408
            [
1409 1
                $this->keys(),
1410 1
                $this->values(),
1411
            ],
1412 1
            $this->iteratorClass,
1413 1
            false
1414
        );
1415
    }
1416
1417
    /**
1418
     * Iterate over the current array and modify the array's value.
1419
     *
1420
     * @param \Closure $closure
1421
     *
1422
     * @return static
1423
     *                <p>(Immutable)</p>
1424
     */
1425 4 View Code Duplication
    public function each(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1426
    {
1427
        // init
1428 4
        $array = [];
1429
1430 4
        foreach ($this->getGenerator() as $key => $value) {
1431 4
            $array[$key] = $closure($value, $key);
1432
        }
1433
1434 4
        return static::create($array, $this->iteratorClass, false);
1435
    }
1436
1437
    /**
1438
     * Check if a value is in the current array using a closure.
1439
     *
1440
     * @param \Closure $closure
1441
     *
1442
     * @return bool
1443
     *              <p>Returns true if the given value is found, false otherwise.</p>
1444
     */
1445 4
    public function exists(\Closure $closure): bool
1446
    {
1447
        // init
1448 4
        $isExists = false;
1449
1450 4
        foreach ($this->getGenerator() as $key => $value) {
1451 3
            if ($closure($value, $key)) {
1452 1
                $isExists = true;
1453
1454 3
                break;
1455
            }
1456
        }
1457
1458 4
        return $isExists;
1459
    }
1460
1461
    /**
1462
     * create a fallback for array
1463
     *
1464
     * 1. use the current array, if it's a array
1465
     * 2. fallback to empty array, if there is nothing
1466
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
1467
     * 4. call "createFromObject()" on object, if there is a "\ArrayAccess"-object
1468
     * 5. call "__toArray()" on object, if the method exists
1469
     * 6. cast a string or object with "__toString()" into an array
1470
     * 7. throw a "InvalidArgumentException"-Exception
1471
     *
1472
     * @param mixed $array
1473
     *
1474
     * @throws \InvalidArgumentException
1475
     *
1476
     * @return array
1477
     */
1478 890
    protected function fallbackForArray(&$array): array
1479
    {
1480 890
        if (\is_array($array)) {
1481 887
            return $array;
1482
        }
1483
1484 11
        if (!$array) {
1485 6
            return [];
1486
        }
1487
1488 10
        $isObject = \is_object($array);
1489
1490 10
        if ($isObject && $array instanceof self) {
1491 1
            return $array->getArray();
1492
        }
1493
1494 9
        if ($isObject && $array instanceof \ArrayAccess) {
1495
            /** @noinspection ReferenceMismatchInspection */
1496
            return static::createFromObject($array)->getArray();
1497
        }
1498
1499 9
        if ($isObject && $array instanceof \ArrayObject) {
1500
            return $array->getArrayCopy();
1501
        }
1502
1503 9
        if ($isObject && \method_exists($array, '__toArray')) {
1504
            return (array) $array->__toArray();
1505
        }
1506
1507
        /** @noinspection ReferenceMismatchInspection */
1508
        if (
1509 9
            \is_string($array)
1510
            ||
1511 9
            ($isObject && \method_exists($array, '__toString'))
1512
        ) {
1513 7
            return [(string) $array];
1514
        }
1515
1516 2
        throw new \InvalidArgumentException(
1517 2
            'Passed value should be a array'
1518
        );
1519
    }
1520
1521
    /**
1522
     * Fill the array until "$num" with "$default" values.
1523
     *
1524
     * @param int   $num
1525
     * @param mixed $default
1526
     *
1527
     * @return static
1528
     *                <p>(Immutable)</p>
1529
     */
1530 8
    public function fillWithDefaults(int $num, $default = null)
1531
    {
1532 8
        if ($num < 0) {
1533 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1534
        }
1535
1536 7
        $tmpArray = $this->array;
1537
1538 7
        $count = \count($tmpArray);
1539
1540 7
        while ($count < $num) {
1541 4
            $tmpArray[] = $default;
1542 4
            $count++;
1543
        }
1544
1545 7
        return static::create($tmpArray, $this->iteratorClass, false);
1546
    }
1547
1548
    /**
1549
     * Find all items in an array that pass the truth test.
1550
     *
1551
     * @param \Closure|null $closure [optional] <p>
1552
     *                               The callback function to use
1553
     *                               </p>
1554
     *                               <p>
1555
     *                               If no callback is supplied, all entries of
1556
     *                               input equal to false (see
1557
     *                               converting to
1558
     *                               boolean) will be removed.
1559
     *                               </p>
1560
     *                               * @param int $flag [optional] <p>
1561
     *                               Flag determining what arguments are sent to <i>callback</i>:
1562
     *                               </p><ul>
1563
     *                               <li>
1564
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1565
     *                               to <i>callback</i> instead of the value</span>
1566
     *                               </li>
1567
     *                               <li>
1568
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1569
     *                               arguments to <i>callback</i> instead of the value</span>
1570
     *                               </li>
1571
     *                               </ul>
1572
     *
1573
     * @return static
1574
     *                <p>(Immutable)</p>
1575
     */
1576 10
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH)
1577
    {
1578 10
        if (!$closure) {
1579 1
            return $this->clean();
1580
        }
1581
1582 10
        return static::create(
1583 10
            \array_filter($this->array, $closure, $flag),
1584 10
            $this->iteratorClass,
1585 10
            false
1586
        );
1587
    }
1588
1589
    /**
1590
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1591
     * property within that.
1592
     *
1593
     * @param string          $property
1594
     * @param string|string[] $value
1595
     * @param string          $comparisonOp
1596
     *                            <p>
1597
     *                            'eq' (equals),<br />
1598
     *                            'gt' (greater),<br />
1599
     *                            'gte' || 'ge' (greater or equals),<br />
1600
     *                            'lt' (less),<br />
1601
     *                            'lte' || 'le' (less or equals),<br />
1602
     *                            'ne' (not equals),<br />
1603
     *                            'contains',<br />
1604
     *                            'notContains',<br />
1605
     *                            'newer' (via strtotime),<br />
1606
     *                            'older' (via strtotime),<br />
1607
     *                            </p>
1608
     *
1609
     * @return static
1610
     *                <p>(Immutable)</p>
1611
     */
1612 1
    public function filterBy(string $property, $value, string $comparisonOp = null)
1613
    {
1614 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...
1615 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1616
        }
1617
1618
        $ops = [
1619 1
            'eq' => function ($item, $prop, $value) {
1620 1
                return $item[$prop] === $value;
1621 1
            },
1622 1
            'gt' => function ($item, $prop, $value) {
1623
                return $item[$prop] > $value;
1624 1
            },
1625 1
            'ge' => function ($item, $prop, $value) {
1626
                return $item[$prop] >= $value;
1627 1
            },
1628 1
            'gte' => function ($item, $prop, $value) {
1629
                return $item[$prop] >= $value;
1630 1
            },
1631 1
            'lt' => function ($item, $prop, $value) {
1632 1
                return $item[$prop] < $value;
1633 1
            },
1634 1
            'le' => function ($item, $prop, $value) {
1635
                return $item[$prop] <= $value;
1636 1
            },
1637 1
            'lte' => function ($item, $prop, $value) {
1638
                return $item[$prop] <= $value;
1639 1
            },
1640 1
            'ne' => function ($item, $prop, $value) {
1641
                return $item[$prop] !== $value;
1642 1
            },
1643 1
            'contains' => function ($item, $prop, $value) {
1644 1
                return \in_array($item[$prop], (array) $value, true);
1645 1
            },
1646 1
            'notContains' => function ($item, $prop, $value) {
1647
                return !\in_array($item[$prop], (array) $value, true);
1648 1
            },
1649 1
            'newer' => function ($item, $prop, $value) {
1650
                return \strtotime($item[$prop]) > \strtotime($value);
1651 1
            },
1652 1
            'older' => function ($item, $prop, $value) {
1653
                return \strtotime($item[$prop]) < \strtotime($value);
1654 1
            },
1655
        ];
1656
1657 1
        $result = \array_values(
1658 1
            \array_filter(
1659 1
                (array) $this->array,
1660 1
                function ($item) use (
1661 1
                    $property,
1662 1
                    $value,
1663 1
                    $ops,
1664 1
                    $comparisonOp
1665
                ) {
1666 1
                    $item = (array) $item;
1667 1
                    $itemArrayy = new self($item);
1668 1
                    $item[$property] = $itemArrayy->get($property, []);
1669
1670 1
                    return $ops[$comparisonOp]($item, $property, $value);
1671 1
                }
1672
            )
1673
        );
1674
1675 1
        return static::create($result, $this->iteratorClass, false);
1676
    }
1677
1678
    /**
1679
     * Find the first item in an array that passes the truth test,
1680
     *  otherwise return false
1681
     *
1682
     * @param \Closure $closure
1683
     *
1684
     * @return false|mixed
1685
     *                      <p>Return false if we did not find the value.</p>
1686
     */
1687 8
    public function find(\Closure $closure)
1688
    {
1689 8
        foreach ($this->getGenerator() as $key => $value) {
1690 6
            if ($closure($value, $key)) {
1691 6
                return $value;
1692
            }
1693
        }
1694
1695 3
        return false;
1696
    }
1697
1698
    /**
1699
     * find by ...
1700
     *
1701
     * @param string          $property
1702
     * @param string|string[] $value
1703
     * @param string          $comparisonOp
1704
     *
1705
     * @return static
1706
     *                 <p>(Immutable)</p>
1707
     */
1708
    public function findBy(string $property, $value, string $comparisonOp = 'eq')
1709
    {
1710
        return $this->filterBy($property, $value, $comparisonOp);
1711
    }
1712
1713
    /**
1714
     * Get the first value from the current array.
1715
     *
1716
     * @return mixed
1717
     *               <p>Return null if there wasn't a element.</p>
1718
     */
1719 13
    public function first()
1720
    {
1721 13
        $tmpArray = $this->array;
1722
1723 13
        return \array_shift($tmpArray);
1724
    }
1725
1726
    /**
1727
     * Get the first value(s) from the current array.
1728
     *
1729
     * @param int|null $number <p>How many values you will take?</p>
1730
     *
1731
     * @return static
1732
     *                <p>(Immutable)</p>
1733
     */
1734 28 View Code Duplication
    public function firstsImmutable(int $number = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1735
    {
1736 28
        if ($number === null) {
1737 7
            $arrayTmp = $this->array;
1738 7
            $array = (array) \array_shift($arrayTmp);
1739
        } else {
1740 21
            $number = (int) $number;
1741 21
            $arrayTmp = $this->array;
1742 21
            $array = \array_splice($arrayTmp, 0, $number);
1743
        }
1744
1745 28
        return static::create($array, $this->iteratorClass, false);
1746
    }
1747
1748
    /**
1749
     * Get the first value(s) from the current array.
1750
     *
1751
     * @param int|null $number <p>How many values you will take?</p>
1752
     *
1753
     * @return static
1754
     *                <p>(Mutable)</p>
1755
     */
1756 26
    public function firstsMutable(int $number = null)
1757
    {
1758 26
        if ($number === null) {
1759 11
            $this->array = (array) \array_shift($this->array);
1760
        } else {
1761 15
            $number = (int) $number;
1762 15
            $this->array = \array_splice($this->array, 0, $number);
1763
        }
1764
1765 26
        return $this;
1766
    }
1767
1768
    /**
1769
     * Exchanges all keys with their associated values in an array.
1770
     *
1771
     * @return static
1772
     *                <p>(Immutable)</p>
1773
     */
1774 1
    public function flip()
1775
    {
1776 1
        return static::create(
1777 1
            \array_flip($this->array),
1778 1
            $this->iteratorClass,
1779 1
            false
1780
        );
1781
    }
1782
1783
    /**
1784
     * Get a value from an array (optional using dot-notation).
1785
     *
1786
     * @param mixed $key       <p>The key to look for.</p>
1787
     * @param mixed $fallback  <p>Value to fallback to.</p>
1788
     * @param array $array     <p>The array to get from, if it's set to "null" we use the current array from the
1789
     *                         class.</p>
1790
     *
1791
     * @return mixed|static
1792
     */
1793 68
    public function get($key, $fallback = null, array $array = null)
1794
    {
1795 68
        if ($array !== null) {
1796 4
            $usedArray = $array;
1797
        } else {
1798 64
            $usedArray = $this->array;
1799
        }
1800
1801 68
        if ($key === null) {
1802 1
            return static::create($usedArray, $this->iteratorClass, false);
1803
        }
1804
1805
        // php cast "bool"-index into "int"-index
1806 68
        if ((bool) $key === $key) {
1807 3
            $key = (int) $key;
1808
        }
1809
1810 68
        if (\array_key_exists($key, $usedArray) === true) {
1811 58
            if (\is_array($usedArray[$key])) {
1812 8
                return static::create($usedArray[$key], $this->iteratorClass, false);
1813
            }
1814
1815 52
            return $usedArray[$key];
1816
        }
1817
1818
        // Crawl through array, get key according to object or not
1819 22
        foreach (\explode($this->pathSeparator, (string) $key) as $segment) {
1820 22
            if (!isset($usedArray[$segment])) {
1821 21
                return $fallback instanceof \Closure ? $fallback() : $fallback;
1822
            }
1823
1824 6
            $usedArray = $usedArray[$segment];
1825
        }
1826
1827 6
        if (\is_array($usedArray)) {
1828 1
            return static::create($usedArray, $this->iteratorClass, false);
1829
        }
1830
1831 6
        return $usedArray;
1832
    }
1833
1834
    /**
1835
     * Get the current array from the "Arrayy"-object.
1836
     *
1837
     * @return array
1838
     */
1839 581
    public function getArray(): array
1840
    {
1841
        // init
1842 581
        $array = [];
1843
1844 581
        foreach ($this->array as $key => &$value) {
1845 478
            if ($value instanceof self) {
1846
                $array[$key] = $value->getArray();
1847
            } else {
1848 478
                $array[$key] = $value;
1849
            }
1850
        }
1851
1852 581
        return $array;
1853
    }
1854
1855
    /**
1856
     * Get the current array from the "Arrayy"-object as generator.
1857
     *
1858
     * @return \Generator
1859
     */
1860 125
    public function getGenerator(): \Generator
1861
    {
1862 125
        yield from $this->array;
1863 93
    }
1864
1865
    /**
1866
     * Returns the values from a single column of the input array, identified by
1867
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1868
     *
1869
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1870
     * array by the values from the $indexKey column in the input array.
1871
     *
1872
     * @param mixed $columnKey
1873
     * @param mixed $indexKey
1874
     *
1875
     * @return static
1876
     *                <p>(Immutable)</p>
1877
     */
1878 1
    public function getColumn($columnKey = null, $indexKey = null)
1879
    {
1880 1
        return static::create(
1881 1
            \array_column($this->array, $columnKey, $indexKey),
1882 1
            $this->iteratorClass,
1883 1
            false
1884
        );
1885
    }
1886
1887
    /**
1888
     * Get correct PHP constant for direction.
1889
     *
1890
     * @param int|string $direction
1891
     *
1892
     * @return int
1893
     */
1894 39
    protected function getDirection($direction): int
1895
    {
1896 39
        if (\is_string($direction)) {
1897 10
            $direction = \strtolower($direction);
1898
1899 10
            if ($direction === 'desc') {
1900 2
                $direction = \SORT_DESC;
1901
            } else {
1902 8
                $direction = \SORT_ASC;
1903
            }
1904
        }
1905
1906
        if (
1907 39
            $direction !== \SORT_DESC
1908
            &&
1909 39
            $direction !== \SORT_ASC
1910
        ) {
1911
            $direction = \SORT_ASC;
1912
        }
1913
1914 39
        return $direction;
1915
    }
1916
1917
    /**
1918
     * alias: for "Arrayy->keys()"
1919
     *
1920
     * @see Arrayy::keys()
1921
     *
1922
     * @return static
1923
     *                <p>(Immutable)</p>
1924
     */
1925 1
    public function getKeys()
1926
    {
1927 1
        return $this->keys();
1928
    }
1929
1930
    /**
1931
     * Get the current array from the "Arrayy"-object as object.
1932
     *
1933
     * @return \stdClass
1934
     */
1935 4
    public function getObject(): \stdClass
1936
    {
1937 4
        return self::arrayToObject($this->getArray());
1938
    }
1939
1940
    /**
1941
     * @return array|Property[]
1942
     */
1943 9
    protected function getPublicProperties(): array
1944
    {
1945 9
        static $PROPERTY_CACHE = [];
1946 9
        $cacheKey = 'Class::' . static::class;
1947
1948 9
        if (isset($PROPERTY_CACHE[$cacheKey])) {
1949 8
            return $PROPERTY_CACHE[$cacheKey];
1950
        }
1951
1952
        // init
1953 2
        $properties = [];
1954
1955 2
        $reflector = new \ReflectionClass($this);
1956 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
1957 2
        $docblock = $factory->create($reflector->getDocComment());
1958 2
        foreach ($docblock->getTagsByName('property') as $tag) {
1959
            /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
1960 2
            $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
1961
        }
1962
1963 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
1964
    }
1965
1966
    /**
1967
     * alias: for "Arrayy->randomImmutable()"
1968
     *
1969
     * @see Arrayy::randomImmutable()
1970
     *
1971
     * @return static
1972
     *                <p>(Immutable)</p>
1973
     */
1974 4
    public function getRandom()
1975
    {
1976 4
        return $this->randomImmutable();
1977
    }
1978
1979
    /**
1980
     * alias: for "Arrayy->randomKey()"
1981
     *
1982
     * @see Arrayy::randomKey()
1983
     *
1984
     * @return mixed
1985
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1986
     */
1987 3
    public function getRandomKey()
1988
    {
1989 3
        return $this->randomKey();
1990
    }
1991
1992
    /**
1993
     * alias: for "Arrayy->randomKeys()"
1994
     *
1995
     * @see Arrayy::randomKeys()
1996
     *
1997
     * @param int $number
1998
     *
1999
     * @return static
2000
     *                 <p>(Immutable)</p>
2001
     */
2002 8
    public function getRandomKeys(int $number)
2003
    {
2004 8
        return $this->randomKeys($number);
2005
    }
2006
2007
    /**
2008
     * alias: for "Arrayy->randomValue()"
2009
     *
2010
     * @see Arrayy::randomValue()
2011
     *
2012
     * @return mixed
2013
     *                <p>Get a random value or null if there wasn't a value.</p>
2014
     */
2015 3
    public function getRandomValue()
2016
    {
2017 3
        return $this->randomValue();
2018
    }
2019
2020
    /**
2021
     * alias: for "Arrayy->randomValues()"
2022
     *
2023
     * @see Arrayy::randomValues()
2024
     *
2025
     * @param int $number
2026
     *
2027
     * @return static
2028
     *                 <p>(Immutable)</p>
2029
     */
2030 6
    public function getRandomValues(int $number)
2031
    {
2032 6
        return $this->randomValues($number);
2033
    }
2034
2035
    /**
2036
     * Group values from a array according to the results of a closure.
2037
     *
2038
     * @param \callable $grouper <p>A callable function name.</p>
2039
     * @param bool      $saveKeys
2040
     *
2041
     * @return static
2042
     *                 <p>(Immutable)</p>
2043
     */
2044 4
    public function group($grouper, bool $saveKeys = false)
2045
    {
2046
        // init
2047 4
        $result = [];
2048
2049
        // Iterate over values, group by property/results from closure.
2050 4
        foreach ($this->getGenerator() as $key => $value) {
2051 4
            $groupKey = \is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $this->array);
2052 4
            $newValue = $this->get($groupKey, null, $result);
2053
2054 4
            if ($groupKey instanceof self) {
2055
                $groupKey = $groupKey->getArray();
2056
            }
2057
2058 4
            if ($newValue instanceof self) {
2059 4
                $newValue = $newValue->getArray();
2060
            }
2061
2062
            // Add to results.
2063 4
            if ($groupKey !== null) {
2064 3
                if ($saveKeys) {
2065 2
                    $result[$groupKey] = $newValue;
2066 2
                    $result[$groupKey][$key] = $value;
2067
                } else {
2068 1
                    $result[$groupKey] = $newValue;
2069 4
                    $result[$groupKey][] = $value;
2070
                }
2071
            }
2072
        }
2073
2074 4
        return static::create($result, $this->iteratorClass, false);
2075
    }
2076
2077
    /**
2078
     * Check if an array has a given key.
2079
     *
2080
     * @param mixed $key
2081
     *
2082
     * @return bool
2083
     */
2084 23
    public function has($key): bool
2085
    {
2086 23
        static $UN_FOUND = null;
2087
2088 23
        if ($UN_FOUND === null) {
2089
            // Generate unique string to use as marker.
2090 1
            $UN_FOUND = \uniqid('arrayy', true);
2091
        }
2092
2093 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2094
    }
2095
2096
    /**
2097
     * Implodes the values of this array.
2098
     *
2099
     * @param string $glue
2100
     *
2101
     * @return string
2102
     */
2103 27
    public function implode(string $glue = ''): string
2104
    {
2105 27
        return $this->implode_recursive($glue, $this->array, false);
2106
    }
2107
2108
    /**
2109
     * Implodes the keys of this array.
2110
     *
2111
     * @param string $glue
2112
     *
2113
     * @return string
2114
     */
2115 8
    public function implodeKeys(string $glue = ''): string
2116
    {
2117 8
        return $this->implode_recursive($glue, $this->array, true);
2118
    }
2119
2120
    /**
2121
     * @param mixed               $glue
2122
     * @param array|static|string $pieces
2123
     * @param bool                $useKeys
2124
     *
2125
     * @return string
2126
     */
2127 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
2128
    {
2129 35
        if ($pieces instanceof self) {
2130 1
            $pieces = $pieces->getArray();
2131
        }
2132
2133 35
        if (\is_array($pieces)) {
2134 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
2135 35
            $pieces_count_not_zero = $pieces_count > 0;
2136
2137 35
            return \implode(
2138 35
                $glue,
2139 35
                \array_map(
2140 35
                    [$this, 'implode_recursive'],
2141 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
2142 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
2143
                )
2144
            );
2145
        }
2146
2147 35
        return (string) $pieces;
2148
    }
2149
2150
    /**
2151
     * @param mixed                 $needle   <p>
2152
     *                                        The searched value.
2153
     *                                        </p>
2154
     *                                        <p>
2155
     *                                        If needle is a string, the comparison is done
2156
     *                                        in a case-sensitive manner.
2157
     *                                        </p>
2158
     * @param array|\Generator|null $haystack <p>
2159
     *                                        The array.
2160
     *                                        </p>
2161
     * @param bool                  $strict   [optional] <p>
2162
     *                                        If the third parameter strict is set to true
2163
     *                                        then the in_array function will also check the
2164
     *                                        types of the
2165
     *                                        needle in the haystack.
2166
     *                                        </p>
2167
     *
2168
     * @return bool
2169
     *               <p>true if needle is found in the array, false otherwise</p>
2170
     */
2171 44
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
2172
    {
2173 44
        if ($haystack === null) {
2174
            $haystack = $this->getGenerator();
2175
        }
2176
2177 44
        foreach ($haystack as $item) {
2178 36
            if (\is_array($item) === true) {
2179 8
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
2180
            } else {
2181 36
                $returnTmp = ($strict === true ? $item === $needle : $item == $needle);
2182
            }
2183
2184 36
            if ($returnTmp === true) {
2185 36
                return true;
2186
            }
2187
        }
2188
2189 18
        return false;
2190
    }
2191
2192
    /**
2193
     * Given a list and an iterate-function that returns
2194
     * a key for each element in the list (or a property name),
2195
     * returns an object with an index of each item.
2196
     *
2197
     * @param mixed $key
2198
     *
2199
     * @return static
2200
     *                <p>(Immutable)</p>
2201
     */
2202 4
    public function indexBy($key)
2203
    {
2204
        // init
2205 4
        $results = [];
2206
2207 4
        foreach ($this->getGenerator() as $a) {
2208 4
            if (\array_key_exists($key, $a) === true) {
2209 4
                $results[$a[$key]] = $a;
2210
            }
2211
        }
2212
2213 4
        return static::create($results, $this->iteratorClass, false);
2214
    }
2215
2216
    /**
2217
     * alias: for "Arrayy->searchIndex()"
2218
     *
2219
     * @see Arrayy::searchIndex()
2220
     *
2221
     * @param mixed $value <p>The value to search for.</p>
2222
     *
2223
     * @return mixed
2224
     */
2225 4
    public function indexOf($value)
2226
    {
2227 4
        return $this->searchIndex($value);
2228
    }
2229
2230
    /**
2231
     * Get everything but the last..$to items.
2232
     *
2233
     * @param int $to
2234
     *
2235
     * @return static
2236
     *                 <p>(Immutable)</p>
2237
     */
2238 12
    public function initial(int $to = 1)
2239
    {
2240 12
        return $this->firstsImmutable(\count($this->array, \COUNT_NORMAL) - $to);
2241
    }
2242
2243
    /**
2244
     * @param mixed $value
2245
     */
2246
    protected function internalGetArray(&$value)
2247
    {
2248
        if ($value instanceof self) {
2249
            $valueTmp = $value->getArray();
2250
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
2251
                $value = [];
2252
            } else {
2253
                /** @noinspection PhpUnusedLocalVariableInspection */
2254
                $value = &$valueTmp;
2255
            }
2256
        }
2257
2258
        /** @noinspection PhpComposerExtensionStubsInspection */
2259
        /** @noinspection NotOptimalIfConditionsInspection */
2260
        if (
2261
            \class_exists('JsonSerializable')
2262
            &&
2263
            $value instanceof \JsonSerializable
0 ignored issues
show
Bug introduced by
The class JsonSerializable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

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

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

2. Missing use statement

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

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

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

Loading history...
2264
        ) {
2265
2266
            /** @noinspection PhpUnusedLocalVariableInspection */
2267
            $value = &$value->jsonSerialize();
2268
        }
2269
    }
2270
2271
    /**
2272
     * Internal mechanics of remove method.
2273
     *
2274
     * @param mixed $key
2275
     *
2276
     * @return bool
2277
     */
2278 18
    protected function internalRemove($key): bool
2279
    {
2280 18
        $path = \explode($this->pathSeparator, (string) $key);
2281
2282
        // Crawl though the keys
2283 18
        while (\count($path, \COUNT_NORMAL) > 1) {
2284
            $key = \array_shift($path);
2285
2286
            if (!$this->has($key)) {
2287
                return false;
2288
            }
2289
2290
            $this->array = &$this->array[$key];
2291
        }
2292
2293 18
        $key = \array_shift($path);
2294
2295 18
        unset($this->array[$key]);
2296
2297 18
        return true;
2298
    }
2299
2300
    /**
2301
     * Internal mechanic of set method.
2302
     *
2303
     * @param string|null $key
2304
     * @param mixed       $value
2305
     *
2306
     * @return bool
2307
     */
2308 779
    protected function internalSet($key, $value): bool
2309
    {
2310 779
        if ($this->checkPropertyTypes === true) {
2311 7
            if (isset($this->properties[$key]) === false) {
2312
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
2313
            }
2314
2315 7
            $this->properties[$key]->checkType($value);
2316
        }
2317
2318 778
        if ($key === null) {
2319
            return false;
2320
        }
2321
2322
        // init
2323 778
        $array = &$this->array;
2324 778
        $path = \explode($this->pathSeparator, (string) $key);
2325
2326
        // Crawl through the keys
2327 778
        while (\count($path, \COUNT_NORMAL) > 1) {
2328 3
            $key = \array_shift($path);
2329
2330 3
            $array = &$array[$key];
2331
        }
2332
2333 778
        $array[\array_shift($path)] = $value;
2334
2335 778
        return true;
2336
    }
2337
2338
    /**
2339
     * Return an array with all elements found in input array.
2340
     *
2341
     * @param array $search
2342
     *
2343
     * @return static
2344
     *                 <p>(Immutable)</p>
2345
     */
2346 2
    public function intersection(array $search)
2347
    {
2348 2
        return static::create(
2349 2
            \array_values(\array_intersect($this->array, $search)),
2350 2
            $this->iteratorClass,
2351 2
            false
2352
        );
2353
    }
2354
2355
    /**
2356
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2357
     *
2358
     * @param array $search
2359
     *
2360
     * @return bool
2361
     */
2362 1
    public function intersects(array $search): bool
2363
    {
2364 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2365
    }
2366
2367
    /**
2368
     * Invoke a function on all of an array's values.
2369
     *
2370
     * @param mixed $callable
2371
     * @param mixed $arguments
2372
     *
2373
     * @return static
2374
     *                 <p>(Immutable)</p>
2375
     */
2376 1
    public function invoke($callable, $arguments = [])
2377
    {
2378
        // If one argument given for each iteration, create an array for it.
2379 1
        if (!\is_array($arguments)) {
2380 1
            $arguments = StaticArrayy::repeat(
2381 1
                $arguments,
2382 1
                \count($this->array, \COUNT_NORMAL)
2383 1
            )->getArray();
2384
        }
2385
2386
        // If the callable has arguments, pass them.
2387 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...
2388 1
            $array = \array_map($callable, $this->array, $arguments);
2389
        } else {
2390 1
            $array = \array_map($callable, $this->array);
2391
        }
2392
2393 1
        return static::create($array, $this->iteratorClass, false);
2394
    }
2395
2396
    /**
2397
     * Check whether array is associative or not.
2398
     *
2399
     * @param bool $recursive
2400
     *
2401
     * @return bool
2402
     *              <p>Returns true if associative, false otherwise.</p>
2403
     */
2404 15
    public function isAssoc(bool $recursive = false): bool
2405
    {
2406 15
        if ($this->isEmpty()) {
2407 3
            return false;
2408
        }
2409
2410 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2411 13
            if (!\is_string($key)) {
2412 13
                return false;
2413
            }
2414
        }
2415
2416 3
        return true;
2417
    }
2418
2419
    /**
2420
     * Check whether the array is empty or not.
2421
     *
2422
     * @return bool
2423
     *              <p>Returns true if empty, false otherwise.</p>
2424
     */
2425 92
    public function isEmpty(): bool
2426
    {
2427 92
        return !$this->array;
2428
    }
2429
2430
    /**
2431
     * Check if the current array is equal to the given "$array" or not.
2432
     *
2433
     * @param array $array
2434
     *
2435
     * @return bool
2436
     */
2437
    public function isEqual(array $array): bool
2438
    {
2439
        return $this->array === $array;
2440
    }
2441
2442
    /**
2443
     * Check if the current array is a multi-array.
2444
     *
2445
     * @return bool
2446
     */
2447 14
    public function isMultiArray(): bool
2448
    {
2449
        return !(
2450 14
            \count($this->array, \COUNT_NORMAL)
2451
            ===
2452 14
            \count($this->array, \COUNT_RECURSIVE)
2453
        );
2454
    }
2455
2456
    /**
2457
     * Check whether array is numeric or not.
2458
     *
2459
     * @return bool
2460
     *              <p>Returns true if numeric, false otherwise.</p>
2461
     */
2462 5
    public function isNumeric(): bool
2463
    {
2464 5
        if ($this->isEmpty()) {
2465 2
            return false;
2466
        }
2467
2468 4
        foreach ($this->keys()->getGenerator() as $key) {
2469 4
            if (!\is_int($key)) {
2470 4
                return false;
2471
            }
2472
        }
2473
2474 2
        return true;
2475
    }
2476
2477
    /**
2478
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2479
     *
2480
     * @param bool $recursive
2481
     *
2482
     * @return bool
2483
     */
2484 1
    public function isSequential(bool $recursive = false): bool
2485
    {
2486
2487
        // recursive
2488
2489 1
        if ($recursive === true) {
2490
            return $this->array_keys_recursive($this->array)
2491
                   ===
2492
                   \range(0, \count($this->array, \COUNT_RECURSIVE) - 1);
2493
        }
2494
2495
        // non recursive
2496
2497 1
        return \array_keys($this->array)
2498
               ===
2499 1
               \range(0, \count($this->array, \COUNT_NORMAL) - 1);
2500
    }
2501
2502
    /**
2503
     * @return array
2504
     */
2505
    public function jsonSerialize(): array
2506
    {
2507
        return $this->getArray();
2508
    }
2509
2510
    /**
2511
     * Get all keys from the current array.
2512
     *
2513
     * @param bool  $recursive    [optional] <p>
2514
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2515
     *                            </p>
2516
     * @param mixed $search_value [optional] <p>
2517
     *                            If specified, then only keys containing these values are returned.
2518
     *                            </p>
2519
     * @param bool  $strict       [optional] <p>
2520
     *                            Determines if strict comparison (===) should be used during the search.
2521
     *                            </p>
2522
     *
2523
     * @return static
2524
     *                  <p>(Immutable) An array of all the keys in input.</p>
2525
     */
2526 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true)
2527
    {
2528
2529
        // recursive
2530
2531 26
        if ($recursive === true) {
2532 3
            if ($search_value === null) {
2533 3
                $array = $this->array_keys_recursive($this->array);
2534
            } else {
2535
                $array = $this->array_keys_recursive($this->array, $search_value, $strict);
2536
            }
2537
2538 3
            return static::create($array, $this->iteratorClass, false);
2539
        }
2540
2541
        // non recursive
2542
2543 25
        if ($search_value === null) {
2544 25
            $array = \array_keys($this->array);
2545
        } else {
2546
            $array = \array_keys($this->array, $search_value, $strict);
2547
        }
2548
2549 25
        return static::create($array, $this->iteratorClass, false);
2550
    }
2551
2552
    /**
2553
     * Sort an array by key in reverse order.
2554
     *
2555
     * @param int $sort_flags [optional] <p>
2556
     *                        You may modify the behavior of the sort using the optional
2557
     *                        parameter sort_flags, for details
2558
     *                        see sort.
2559
     *                        </p>
2560
     *
2561
     * @return static
2562
     *                 <p>(Mutable) Return this Arrayy object.</p>
2563
     */
2564 4
    public function krsort(int $sort_flags = 0)
2565
    {
2566 4
        \krsort($this->array, $sort_flags);
2567
2568 4
        return $this;
2569
    }
2570
2571
    /**
2572
     * Get the last value from the current array.
2573
     *
2574
     * @return mixed
2575
     *                <p>Return null if there wasn't a element.</p>
2576
     */
2577 4
    public function last()
2578
    {
2579 4
        return $this->pop();
2580
    }
2581
2582
    /**
2583
     * Get the last value(s) from the current array.
2584
     *
2585
     * @param int|null $number
2586
     *
2587
     * @return static
2588
     *                <p>(Immutable)</p>
2589
     */
2590 13
    public function lastsImmutable(int $number = null)
2591
    {
2592 13
        if ($this->isEmpty()) {
2593 1
            return static::create([], $this->iteratorClass, false);
2594
        }
2595
2596 12
        if ($number === null) {
2597 8
            $poppedValue = $this->pop();
2598
2599 8
            if ($poppedValue === null) {
2600 1
                $poppedValue = [$poppedValue];
2601
            } else {
2602 7
                $poppedValue = (array) $poppedValue;
2603
            }
2604
2605 8
            $arrayy = static::create($poppedValue, $this->iteratorClass, false);
2606
        } else {
2607 4
            $number = (int) $number;
2608 4
            $arrayy = $this->rest(-$number);
2609
        }
2610
2611 12
        return $arrayy;
2612
    }
2613
2614
    /**
2615
     * Get the last value(s) from the current array.
2616
     *
2617
     * @param int|null $number
2618
     *
2619
     * @return static
2620
     *                 <p>(Mutable)</p>
2621
     */
2622 13
    public function lastsMutable(int $number = null)
2623
    {
2624 13
        if ($this->isEmpty()) {
2625 1
            return $this;
2626
        }
2627
2628 12
        if ($number === null) {
2629 8
            $poppedValue = $this->pop();
2630
2631 8
            if ($poppedValue === null) {
2632 1
                $poppedValue = [$poppedValue];
2633
            } else {
2634 7
                $poppedValue = (array) $poppedValue;
2635
            }
2636
2637 8
            $this->array = static::create($poppedValue, $this->iteratorClass, false)->array;
2638
        } else {
2639 4
            $number = (int) $number;
2640 4
            $this->array = $this->rest(-$number)->array;
2641
        }
2642
2643 12
        return $this;
2644
    }
2645
2646
    /**
2647
     * Count the values from the current array.
2648
     *
2649
     * alias: for "Arrayy->count()"
2650
     *
2651
     * @see Arrayy::count()
2652
     *
2653
     * @param int $mode
2654
     *
2655
     * @return int
2656
     */
2657 20
    public function length(int $mode = \COUNT_NORMAL): int
2658
    {
2659 20
        return $this->count($mode);
2660
    }
2661
2662
    /**
2663
     * Apply the given function to the every element of the array,
2664
     * collecting the results.
2665
     *
2666
     * @param \callable $callable
2667
     *
2668
     * @return static
2669
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2670
     */
2671 4
    public function map($callable)
2672
    {
2673 4
        return static::create(
2674 4
            \array_map($callable, $this->array),
2675 4
            $this->iteratorClass,
2676 4
            false
2677
        );
2678
    }
2679
2680
    /**
2681
     * Check if all items in current array match a truth test.
2682
     *
2683
     * @param \Closure $closure
2684
     *
2685
     * @return bool
2686
     */
2687 15 View Code Duplication
    public function matches(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2688
    {
2689 15
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2690 2
            return false;
2691
        }
2692
2693 13
        foreach ($this->getGenerator() as $key => $value) {
2694 13
            $value = $closure($value, $key);
2695
2696 13
            if ($value === false) {
2697 13
                return false;
2698
            }
2699
        }
2700
2701 7
        return true;
2702
    }
2703
2704
    /**
2705
     * Check if any item in the current array matches a truth test.
2706
     *
2707
     * @param \Closure $closure
2708
     *
2709
     * @return bool
2710
     */
2711 14 View Code Duplication
    public function matchesAny(\Closure $closure): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2712
    {
2713 14
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2714 2
            return false;
2715
        }
2716
2717 12
        foreach ($this->getGenerator() as $key => $value) {
2718 12
            $value = $closure($value, $key);
2719
2720 12
            if ($value === true) {
2721 12
                return true;
2722
            }
2723
        }
2724
2725 4
        return false;
2726
    }
2727
2728
    /**
2729
     * Get the max value from an array.
2730
     *
2731
     * @return mixed
2732
     */
2733 10 View Code Duplication
    public function max()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2734
    {
2735 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2736 1
            return false;
2737
        }
2738
2739 9
        return \max($this->array);
2740
    }
2741
2742
    /**
2743
     * Merge the new $array into the current array.
2744
     *
2745
     * - keep key,value from the current array, also if the index is in the new $array
2746
     *
2747
     * @param array $array
2748
     * @param bool  $recursive
2749
     *
2750
     * @return static
2751
     *                <p>(Immutable)</p>
2752
     */
2753 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2754
    {
2755 25
        if ($recursive === true) {
2756 4
            $result = \array_replace_recursive($this->array, $array);
2757
        } else {
2758 21
            $result = \array_replace($this->array, $array);
2759
        }
2760
2761 25
        return static::create($result, $this->iteratorClass, false);
2762
    }
2763
2764
    /**
2765
     * Merge the new $array into the current array.
2766
     *
2767
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2768
     * - create new indexes
2769
     *
2770
     * @param array $array
2771
     * @param bool  $recursive
2772
     *
2773
     * @return static
2774
     *                <p>(Immutable)</p>
2775
     */
2776 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2777
    {
2778 16
        if ($recursive === true) {
2779 4
            $result = \array_merge_recursive($this->array, $array);
2780
        } else {
2781 12
            $result = \array_merge($this->array, $array);
2782
        }
2783
2784 16
        return static::create($result, $this->iteratorClass, false);
2785
    }
2786
2787
    /**
2788
     * Merge the the current array into the $array.
2789
     *
2790
     * - use key,value from the new $array, also if the index is in the current array
2791
     *
2792
     * @param array $array
2793
     * @param bool  $recursive
2794
     *
2795
     * @return static
2796
     *                <p>(Immutable)</p>
2797
     */
2798 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2799
    {
2800 16
        if ($recursive === true) {
2801 4
            $result = \array_replace_recursive($array, $this->array);
2802
        } else {
2803 12
            $result = \array_replace($array, $this->array);
2804
        }
2805
2806 16
        return static::create($result, $this->iteratorClass, false);
2807
    }
2808
2809
    /**
2810
     * Merge the current array into the new $array.
2811
     *
2812
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2813
     * - create new indexes
2814
     *
2815
     * @param array $array
2816
     * @param bool  $recursive
2817
     *
2818
     * @return static
2819
     *                <p>(Immutable)</p>
2820
     */
2821 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2822
    {
2823 17
        if ($recursive === true) {
2824 4
            $result = \array_merge_recursive($array, $this->array);
2825
        } else {
2826 13
            $result = \array_merge($array, $this->array);
2827
        }
2828
2829 17
        return static::create($result, $this->iteratorClass, false);
2830
    }
2831
2832
    /**
2833
     * @return ArrayyMeta|static
2834
     */
2835 8
    public static function meta()
2836
    {
2837 8
        return (new ArrayyMeta())->getMetaObject(static::class);
2838
    }
2839
2840
    /**
2841
     * Get the min value from an array.
2842
     *
2843
     * @return mixed
2844
     */
2845 10 View Code Duplication
    public function min()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2846
    {
2847 10
        if (\count($this->array, \COUNT_NORMAL) === 0) {
2848 1
            return false;
2849
        }
2850
2851 9
        return \min($this->array);
2852
    }
2853
2854
    /**
2855
     * Move an array element to a new index.
2856
     *
2857
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2858
     *
2859
     * @param int|string $from
2860
     * @param int|string $to
2861
     *
2862
     * @return static
2863
     *                 <p>(Immutable)</p>
2864
     */
2865 1
    public function moveElement($from, $to)
2866
    {
2867 1
        $array = $this->array;
2868
2869 1
        if (\is_int($from)) {
2870 1
            $tmp = \array_splice($array, $from, 1);
2871 1
            \array_splice($array, $to, 0, $tmp);
2872 1
            $output = $array;
2873 1
        } elseif (\is_string($from)) {
2874 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2875 1
            $itemToMove = $array[$from];
2876 1
            \array_splice($array, $indexToMove, 1);
2877 1
            $i = 0;
2878 1
            $output = [];
2879 1
            foreach ($array as $key => $item) {
2880 1
                if ($i === $to) {
2881 1
                    $output[$from] = $itemToMove;
2882
                }
2883 1
                $output[$key] = $item;
2884 1
                $i++;
2885
            }
2886
        } else {
2887
            $output = [];
2888
        }
2889
2890 1
        return static::create($output, $this->iteratorClass, false);
2891
    }
2892
2893
    /**
2894
     * Convert a object into an array.
2895
     *
2896
     * @param object $object
2897
     *
2898
     * @return mixed
2899
     */
2900 5
    protected static function objectToArray($object)
2901
    {
2902 5
        if (!\is_object($object)) {
2903 4
            return $object;
2904
        }
2905
2906 5
        if (\is_object($object)) {
2907 5
            $object = \get_object_vars($object);
2908
        }
2909
2910 5
        return \array_map(['self', 'objectToArray'], $object);
2911
    }
2912
2913
    /**
2914
     * Get a subset of the items from the given array.
2915
     *
2916
     * @param mixed[] $keys
2917
     *
2918
     * @return static
2919
     *                <p>(Immutable)</p>
2920
     */
2921
    public function only(array $keys)
2922
    {
2923
        $array = $this->array;
2924
2925
        return static::create(
2926
            \array_intersect_key($array, \array_flip($keys)),
2927
            $this->iteratorClass,
2928
            false
2929
        );
2930
    }
2931
2932
    /**
2933
     * Pad array to the specified size with a given value.
2934
     *
2935
     * @param int   $size  <p>Size of the result array.</p>
2936
     * @param mixed $value <p>Empty value by default.</p>
2937
     *
2938
     * @return static
2939
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2940
     */
2941 4
    public function pad(int $size, $value)
2942
    {
2943 4
        return static::create(
2944 4
            \array_pad($this->array, $size, $value),
2945 4
            $this->iteratorClass,
2946 4
            false
2947
        );
2948
    }
2949
2950
    /**
2951
     * Pop a specified value off the end of the current array.
2952
     *
2953
     * @return mixed
2954
     *               <p>(Mutable) The popped element from the current array.</p>
2955
     */
2956 16
    public function pop()
2957
    {
2958 16
        return \array_pop($this->array);
2959
    }
2960
2961
    /**
2962
     * Prepend a (key) + value to the current array.
2963
     *
2964
     * @param mixed $value
2965
     * @param mixed $key
2966
     *
2967
     * @return static
2968
     *                 <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2969
     */
2970 8
    public function prepend($value, $key = null)
2971
    {
2972 8
        if ($key === null) {
2973 8
            \array_unshift($this->array, $value);
2974
        } else {
2975
            /** @noinspection AdditionOperationOnArraysInspection */
2976 1
            $this->array = [$key => $value] + $this->array;
2977
        }
2978
2979 8
        return $this;
2980
    }
2981
2982
    /**
2983
     * Add a suffix to each key.
2984
     *
2985
     * @param mixed $suffix
2986
     *
2987
     * @return static
2988
     *                 <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2989
     */
2990 10 View Code Duplication
    public function prependToEachKey($suffix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2991
    {
2992
        // init
2993 10
        $result = [];
2994
2995 10
        foreach ($this->getGenerator() as $key => $item) {
2996 9
            if ($item instanceof self) {
2997
                $result[$key] = $item->prependToEachKey($suffix);
2998 9
            } elseif (\is_array($item)) {
2999
                $result[$key] = self::create($item, $this->iteratorClass, false)->prependToEachKey($suffix)->toArray();
3000
            } else {
3001 9
                $result[$key . $suffix] = $item;
3002
            }
3003
        }
3004
3005 10
        return self::create($result, $this->iteratorClass, false);
3006
    }
3007
3008
    /**
3009
     * Add a suffix to each value.
3010
     *
3011
     * @param mixed $suffix
3012
     *
3013
     * @return static
3014
     *                 <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
3015
     */
3016 10 View Code Duplication
    public function prependToEachValue($suffix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3017
    {
3018
        // init
3019 10
        $result = [];
3020
3021 10
        foreach ($this->getGenerator() as $key => $item) {
3022 9
            if ($item instanceof self) {
3023
                $result[$key] = $item->prependToEachValue($suffix);
3024 9
            } elseif (\is_array($item)) {
3025
                $result[$key] = self::create($item, $this->iteratorClass, false)
3026
                                    ->prependToEachValue($suffix)
3027
                                    ->toArray();
3028 9
            } elseif (\is_object($item)) {
3029 1
                $result[$key] = $item;
3030
            } else {
3031 9
                $result[$key] = $item . $suffix;
3032
            }
3033
        }
3034
3035 10
        return self::create($result, $this->iteratorClass, false);
3036
    }
3037
3038
    /**
3039
     * Push one or more values onto the end of array at once.
3040
     *
3041
     * @return static
3042
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
3043
     */
3044 4 View Code Duplication
    public function push(/* variadic arguments allowed */)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3045
    {
3046 4
        if (\func_num_args()) {
3047 4
            $args = \array_merge([&$this->array], \func_get_args());
3048 4
            \array_push(...$args);
3049
        }
3050
3051 4
        return $this;
3052
    }
3053
3054
    /**
3055
     * Get a random value from the current array.
3056
     *
3057
     * @param int|null $number <p>How many values you will take?</p>
3058
     *
3059
     * @return static
3060
     *                <p>(Immutable)</p>
3061
     */
3062 18
    public function randomImmutable(int $number = null)
3063
    {
3064 18 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3065 1
            return static::create([], $this->iteratorClass, false);
3066
        }
3067
3068 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3069
            /** @noinspection NonSecureArrayRandUsageInspection */
3070 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3071
3072 13
            return static::create($arrayRandValue, $this->iteratorClass, false);
3073
        }
3074
3075 5
        $arrayTmp = $this->array;
3076
        /** @noinspection NonSecureShuffleUsageInspection */
3077 5
        \shuffle($arrayTmp);
3078
3079 5
        return static::create($arrayTmp, $this->iteratorClass, false)->firstsImmutable($number);
3080
    }
3081
3082
    /**
3083
     * Pick a random key/index from the keys of this array.
3084
     *
3085
     * @throws \RangeException If array is empty
3086
     *
3087
     * @return mixed
3088
     *                <p>Get a key/index or null if there wasn't a key/index.</p>
3089
     */
3090 4
    public function randomKey()
3091
    {
3092 4
        $result = $this->randomKeys(1);
3093
3094 4
        if (!isset($result[0])) {
3095
            $result[0] = null;
3096
        }
3097
3098 4
        return $result[0];
3099
    }
3100
3101
    /**
3102
     * Pick a given number of random keys/indexes out of this array.
3103
     *
3104
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3105
     *
3106
     * @throws \RangeException If array is empty
3107
     *
3108
     * @return static
3109
     *                 <p>(Immutable)</p>
3110
     */
3111 13
    public function randomKeys(int $number)
3112
    {
3113 13
        $count = \count($this->array, \COUNT_NORMAL);
3114
3115 13
        if ($number === 0 || $number > $count) {
3116 2
            throw new \RangeException(
3117 2
                \sprintf(
3118 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3119 2
                    $number,
3120 2
                    $count
3121
                )
3122
            );
3123
        }
3124
3125 11
        $result = (array) \array_rand($this->array, $number);
3126
3127 11
        return static::create($result, $this->iteratorClass, false);
3128
    }
3129
3130
    /**
3131
     * Get a random value from the current array.
3132
     *
3133
     * @param int|null $number <p>How many values you will take?</p>
3134
     *
3135
     * @return static
3136
     *                 <p>(Mutable)</p>
3137
     */
3138 17
    public function randomMutable(int $number = null)
3139
    {
3140 17 View Code Duplication
        if (\count($this->array, \COUNT_NORMAL) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3141
            return static::create([], $this->iteratorClass, false);
3142
        }
3143
3144 17 View Code Duplication
        if ($number === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3145
            /** @noinspection NonSecureArrayRandUsageInspection */
3146 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3147 7
            $this->array = $arrayRandValue;
3148
3149 7
            return $this;
3150
        }
3151
3152
        /** @noinspection NonSecureShuffleUsageInspection */
3153 11
        \shuffle($this->array);
3154
3155 11
        return $this->firstsMutable($number);
3156
    }
3157
3158
    /**
3159
     * Pick a random value from the values of this array.
3160
     *
3161
     * @return mixed
3162
     *                <p>Get a random value or null if there wasn't a value.</p>
3163
     */
3164 4
    public function randomValue()
3165
    {
3166 4
        $result = $this->randomImmutable();
3167
3168 4
        if (!isset($result[0])) {
3169
            $result[0] = null;
3170
        }
3171
3172 4
        return $result[0];
3173
    }
3174
3175
    /**
3176
     * Pick a given number of random values out of this array.
3177
     *
3178
     * @param int $number
3179
     *
3180
     * @return static
3181
     *                 <p>(Mutable)</p>
3182
     */
3183 7
    public function randomValues(int $number)
3184
    {
3185 7
        return $this->randomMutable($number);
3186
    }
3187
3188
    /**
3189
     * Get a random value from an array, with the ability to skew the results.
3190
     *
3191
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3192
     *
3193
     * @param array    $array
3194
     * @param int|null $number <p>How many values you will take?</p>
3195
     *
3196
     * @return static
3197
     *                 <p>(Immutable)</p>
3198
     */
3199 9
    public function randomWeighted(array $array, int $number = null)
3200
    {
3201
        // init
3202 9
        $options = [];
3203
3204 9
        foreach ($array as $option => $weight) {
3205 9
            if ($this->searchIndex($option) !== false) {
3206 9
                for ($i = 0; $i < $weight; ++$i) {
3207 1
                    $options[] = $option;
3208
                }
3209
            }
3210
        }
3211
3212 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3213
    }
3214
3215
    /**
3216
     * Reduce the current array via callable e.g. anonymous-function.
3217
     *
3218
     * @param \callable $callable
3219
     * @param array     $init
3220
     *
3221
     * @return static
3222
     *                 <p>(Immutable)</p>
3223
     */
3224 4
    public function reduce($callable, array $init = [])
3225
    {
3226 4
        $result = \array_reduce($this->array, $callable, $init);
3227
3228 4
        if ($result === null) {
3229
            $this->array = [];
3230
        } else {
3231 4
            $this->array = (array) $result;
3232
        }
3233
3234 4
        return static::create($this->array, $this->iteratorClass, false);
3235
    }
3236
3237
    /**
3238
     * Create a numerically re-indexed Arrayy object.
3239
     *
3240
     * @return static
3241
     *                 <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3242
     */
3243 9
    public function reindex()
3244
    {
3245 9
        $this->array = \array_values($this->array);
3246
3247 9
        return $this;
3248
    }
3249
3250
    /**
3251
     * Return all items that fail the truth test.
3252
     *
3253
     * @param \Closure $closure
3254
     *
3255
     * @return static
3256
     *                <p>(Immutable)</p>
3257
     */
3258 1 View Code Duplication
    public function reject(\Closure $closure)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3259
    {
3260 1
        $filtered = [];
3261
3262 1
        foreach ($this->getGenerator() as $key => $value) {
3263 1
            if (!$closure($value, $key)) {
3264 1
                $filtered[$key] = $value;
3265
            }
3266
        }
3267
3268 1
        return static::create($filtered, $this->iteratorClass, false);
3269
    }
3270
3271
    /**
3272
     * Remove a value from the current array (optional using dot-notation).
3273
     *
3274
     * @param mixed $key
3275
     *
3276
     * @return static
3277
     *                <p>(Immutable)</p>
3278
     */
3279 18
    public function remove($key)
3280
    {
3281
        // recursive call
3282 18
        if (\is_array($key)) {
3283
            foreach ($key as $k) {
3284
                $this->internalRemove($k);
3285
            }
3286
3287
            return static::create($this->array, $this->iteratorClass, false);
3288
        }
3289
3290 18
        $this->internalRemove($key);
3291
3292 18
        return static::create($this->array, $this->iteratorClass, false);
3293
    }
3294
3295
    /**
3296
     * Remove the first value from the current array.
3297
     *
3298
     * @return static
3299
     *                <p>(Immutable)</p>
3300
     */
3301 7
    public function removeFirst()
3302
    {
3303 7
        $tmpArray = $this->array;
3304 7
        \array_shift($tmpArray);
3305
3306 7
        return static::create($tmpArray, $this->iteratorClass, false);
3307
    }
3308
3309
    /**
3310
     * Remove the last value from the current array.
3311
     *
3312
     * @return static
3313
     *                <p>(Immutable)</p>
3314
     */
3315 7
    public function removeLast()
3316
    {
3317 7
        $tmpArray = $this->array;
3318 7
        \array_pop($tmpArray);
3319
3320 7
        return static::create($tmpArray, $this->iteratorClass, false);
3321
    }
3322
3323
    /**
3324
     * Removes a particular value from an array (numeric or associative).
3325
     *
3326
     * @param mixed $value
3327
     *
3328
     * @return static
3329
     *                <p>(Immutable)</p>
3330
     */
3331 7
    public function removeValue($value)
3332
    {
3333 7
        $isNumericArray = true;
3334 7
        foreach ($this->getGenerator() as $key => $item) {
3335 6
            if ($item === $value) {
3336 6
                if (!\is_int($key)) {
3337
                    $isNumericArray = false;
3338
                }
3339 6
                unset($this->array[$key]);
3340
            }
3341
        }
3342
3343 7
        if ($isNumericArray) {
3344 7
            $this->array = \array_values($this->array);
3345
        }
3346
3347 7
        return static::create($this->array, $this->iteratorClass, false);
3348
    }
3349
3350
    /**
3351
     * Generate array of repeated arrays.
3352
     *
3353
     * @param int $times <p>How many times has to be repeated.</p>
3354
     *
3355
     * @return static
3356
     *                <p>(Immutable)</p>
3357
     */
3358 1
    public function repeat($times): self
3359
    {
3360 1
        if ($times === 0) {
3361 1
            return new static();
3362
        }
3363
3364 1
        return static::create(
3365 1
            \array_fill(0, (int) $times, $this->array),
3366 1
            $this->iteratorClass,
3367 1
            false
3368
        );
3369
    }
3370
3371
    /**
3372
     * Replace a key with a new key/value pair.
3373
     *
3374
     * @param mixed $replace
3375
     * @param mixed $key
3376
     * @param mixed $value
3377
     *
3378
     * @return static
3379
     *                 <p>(Immutable)</p>
3380
     */
3381 2
    public function replace($replace, $key, $value)
3382
    {
3383 2
        $that = $this->remove($replace);
3384
3385 2
        return $that->set($key, $value);
3386
    }
3387
3388
    /**
3389
     * Create an array using the current array as values and the other array as keys.
3390
     *
3391
     * @param array $keys <p>An array of keys.</p>
3392
     *
3393
     * @return static
3394
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3395
     */
3396 2
    public function replaceAllKeys(array $keys)
3397
    {
3398 2
        return static::create(
3399 2
            \array_combine($keys, $this->array),
3400 2
            $this->iteratorClass,
3401 2
            false
3402
        );
3403
    }
3404
3405
    /**
3406
     * Create an array using the current array as keys and the other array as values.
3407
     *
3408
     * @param array $array <p>An array o values.</p>
3409
     *
3410
     * @return static
3411
     *                 <p>(Immutable) Arrayy object with values from the other array.</p>
3412
     */
3413 2
    public function replaceAllValues(array $array)
3414
    {
3415 2
        return static::create(
3416 2
            \array_combine($this->array, $array),
3417 2
            $this->iteratorClass,
3418 2
            false
3419
        );
3420
    }
3421
3422
    /**
3423
     * Replace the keys in an array with another set.
3424
     *
3425
     * @param array $keys <p>An array of keys matching the array's size</p>
3426
     *
3427
     * @return static
3428
     *                <p>(Immutable)</p>
3429
     */
3430 1
    public function replaceKeys(array $keys)
3431
    {
3432 1
        $values = \array_values($this->array);
3433 1
        $result = \array_combine($keys, $values);
3434
3435 1
        return static::create($result, $this->iteratorClass, false);
3436
    }
3437
3438
    /**
3439
     * Replace the first matched value in an array.
3440
     *
3441
     * @param mixed $search      <p>The value to replace.</p>
3442
     * @param mixed $replacement <p>The value to replace.</p>
3443
     *
3444
     * @return static
3445
     *                 <p>(Immutable)</p>
3446
     */
3447 3
    public function replaceOneValue($search, $replacement = '')
3448
    {
3449 3
        $array = $this->array;
3450 3
        $key = \array_search($search, $array, true);
3451
3452 3
        if ($key !== false) {
3453 3
            $array[$key] = $replacement;
3454
        }
3455
3456 3
        return static::create($array, $this->iteratorClass, false);
3457
    }
3458
3459
    /**
3460
     * Replace values in the current array.
3461
     *
3462
     * @param mixed $search      <p>The value to replace.</p>
3463
     * @param mixed $replacement <p>What to replace it with.</p>
3464
     *
3465
     * @return static
3466
     *                 <p>(Immutable)</p>
3467
     */
3468 1
    public function replaceValues($search, $replacement = '')
3469
    {
3470 1
        $array = $this->each(
3471 1
            function ($value) use ($search, $replacement) {
3472 1
                return \str_replace($search, $replacement, $value);
3473 1
            }
3474
        );
3475
3476 1
        return $array;
3477
    }
3478
3479
    /**
3480
     * Get the last elements from index $from until the end of this array.
3481
     *
3482
     * @param int $from
3483
     *
3484
     * @return static
3485
     *                 <p>(Immutable)</p>
3486
     */
3487 15
    public function rest(int $from = 1)
3488
    {
3489 15
        $tmpArray = $this->array;
3490
3491 15
        return static::create(
3492 15
            \array_splice($tmpArray, $from),
3493 15
            $this->iteratorClass,
3494 15
            false
3495
        );
3496
    }
3497
3498
    /**
3499
     * Return the array in the reverse order.
3500
     *
3501
     * @return static
3502
     *                 <p>(Mutable) Return this Arrayy object.</p>
3503
     */
3504 8
    public function reverse()
3505
    {
3506 8
        $this->array = \array_reverse($this->array);
3507
3508 8
        return $this;
3509
    }
3510
3511
    /**
3512
     * Sort an array in reverse order.
3513
     *
3514
     * @param int $sort_flags [optional] <p>
3515
     *                        You may modify the behavior of the sort using the optional
3516
     *                        parameter sort_flags, for details
3517
     *                        see sort.
3518
     *                        </p>
3519
     *
3520
     * @return static
3521
     *                 <p>(Mutable) Return this Arrayy object.</p>
3522
     */
3523 4
    public function rsort(int $sort_flags = 0)
3524
    {
3525 4
        \rsort($this->array, $sort_flags);
3526
3527 4
        return $this;
3528
    }
3529
3530
    /**
3531
     * Search for the first index of the current array via $value.
3532
     *
3533
     * @param mixed $value
3534
     *
3535
     * @return float|int|string
3536
     */
3537 20
    public function searchIndex($value)
3538
    {
3539 20
        return \array_search($value, $this->array, true);
3540
    }
3541
3542
    /**
3543
     * Search for the value of the current array via $index.
3544
     *
3545
     * @param mixed $index
3546
     *
3547
     * @return static
3548
     *                 <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3549
     */
3550 9
    public function searchValue($index)
3551
    {
3552
        // init
3553 9
        $return = [];
3554
3555 9
        if ($this->isEmpty()) {
3556
            return static::create([], $this->iteratorClass, false);
3557
        }
3558
3559
        // php cast "bool"-index into "int"-index
3560 9
        if ((bool) $index === $index) {
3561 1
            $index = (int) $index;
3562
        }
3563
3564 9
        if (\array_key_exists($index, $this->array) === true) {
3565 7
            $return = [$this->array[$index]];
3566
        }
3567
3568 9
        return static::create($return, $this->iteratorClass, false);
3569
    }
3570
3571
    /**
3572
     * Set a value for the current array (optional using dot-notation).
3573
     *
3574
     * @param string $key   <p>The key to set.</p>
3575
     * @param mixed  $value <p>Its value.</p>
3576
     *
3577
     * @return static
3578
     *                <p>(Mutable)</p>
3579
     */
3580 17
    public function set($key, $value)
3581
    {
3582 17
        $this->internalSet($key, $value);
3583
3584 17
        return $this;
3585
    }
3586
3587
    /**
3588
     * Get a value from a array and set it if it was not.
3589
     *
3590
     * WARNING: this method only set the value, if the $key is not already set
3591
     *
3592
     * @param mixed $key      <p>The key</p>
3593
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3594
     *
3595
     * @return mixed
3596
     *                <p>(Mutable)</p>
3597
     */
3598 11
    public function setAndGet($key, $fallback = null)
3599
    {
3600
        // If the key doesn't exist, set it.
3601 11
        if (!$this->has($key)) {
3602 4
            $this->array = $this->set($key, $fallback)->getArray();
3603
        }
3604
3605 11
        return $this->get($key);
3606
    }
3607
3608
    /**
3609
     * Shifts a specified value off the beginning of array.
3610
     *
3611
     * @return mixed
3612
     *               <p>(Mutable) A shifted element from the current array.</p>
3613
     */
3614 4
    public function shift()
3615
    {
3616 4
        return \array_shift($this->array);
3617
    }
3618
3619
    /**
3620
     * Shuffle the current array.
3621
     *
3622
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3623
     * @param array $array  [optional]
3624
     *
3625
     * @return static
3626
     *                <p>(Immutable)</p>
3627
     */
3628 1
    public function shuffle(bool $secure = false, array $array = null)
3629
    {
3630 1
        if ($array === null) {
3631 1
            $array = $this->array;
3632
        }
3633
3634 1
        if ($secure !== true) {
3635
            /** @noinspection NonSecureShuffleUsageInspection */
3636 1
            \shuffle($array);
3637
        } else {
3638 1
            $size = \count($array, \COUNT_NORMAL);
3639 1
            $keys = \array_keys($array);
3640 1
            for ($i = $size - 1; $i > 0; --$i) {
3641
                try {
3642 1
                    $r = \random_int(0, $i);
3643
                } catch (\Exception $e) {
3644
                    /** @noinspection RandomApiMigrationInspection */
3645
                    $r = \mt_rand(0, $i);
3646
                }
3647 1
                if ($r !== $i) {
3648 1
                    $temp = $array[$keys[$r]];
3649 1
                    $array[$keys[$r]] = $array[$keys[$i]];
3650 1
                    $array[$keys[$i]] = $temp;
3651
                }
3652
            }
3653
3654
            // reset indices
3655 1
            $array = \array_values($array);
3656
        }
3657
3658 1
        foreach ($array as $key => $value) {
3659
            // check if recursive is needed
3660 1
            if (\is_array($value) === true) {
3661 1
                $array[$key] = $this->shuffle($secure, $value);
3662
            }
3663
        }
3664
3665 1
        return static::create($array, $this->iteratorClass, false);
3666
    }
3667
3668
    /**
3669
     * Count the values from the current array.
3670
     *
3671
     * alias: for "Arrayy->count()"
3672
     *
3673
     * @param int $mode
3674
     *
3675
     * @return int
3676
     */
3677 20
    public function size(int $mode = \COUNT_NORMAL): int
3678
    {
3679 20
        return $this->count($mode);
3680
    }
3681
3682
    /**
3683
     * Counts all elements in an array, or something in an object.
3684
     *
3685
     * <p>
3686
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3687
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3688
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3689
     * implemented and used in PHP.
3690
     * </p>
3691
     *
3692
     * @return int
3693
     *              <p>
3694
     *              The number of elements in var, which is
3695
     *              typically an array, since anything else will have one
3696
     *              element.
3697
     *              </p>
3698
     *              <p>
3699
     *              If var is not an array or an object with
3700
     *              implemented Countable interface,
3701
     *              1 will be returned.
3702
     *              There is one exception, if var is &null;,
3703
     *              0 will be returned.
3704
     *              </p>
3705
     *              <p>
3706
     *              Caution: count may return 0 for a variable that isn't set,
3707
     *              but it may also return 0 for a variable that has been initialized with an
3708
     *              empty array. Use isset to test if a variable is set.
3709
     *              </p>
3710
     */
3711 10
    public function sizeRecursive(): int
3712
    {
3713 10
        return \count($this->array, \COUNT_RECURSIVE);
3714
    }
3715
3716
    /**
3717
     * Extract a slice of the array.
3718
     *
3719
     * @param int      $offset       <p>Slice begin index.</p>
3720
     * @param int|null $length       <p>Length of the slice.</p>
3721
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3722
     *
3723
     * @return static
3724
     *                 <p>A slice of the original array with length $length.</p>
3725
     */
3726 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false)
3727
    {
3728 4
        return static::create(
3729 4
            \array_slice($this->array, $offset, $length, $preserveKeys),
3730 4
            $this->iteratorClass,
3731 4
            false
3732
        );
3733
    }
3734
3735
    /**
3736
     * Sort the current array and optional you can keep the keys.
3737
     *
3738
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3739
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3740
     *                              <strong>SORT_NATURAL</strong></p>
3741
     * @param bool       $keepKeys
3742
     *
3743
     * @return static
3744
     *                 <p>(Mutable) Return this Arrayy object.</p>
3745
     */
3746 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
3747
    {
3748 20
        return $this->sorting($this->array, $direction, $strategy, $keepKeys);
3749
    }
3750
3751
    /**
3752
     * Sort the current array by key.
3753
     *
3754
     * @see http://php.net/manual/en/function.ksort.php
3755
     * @see http://php.net/manual/en/function.krsort.php
3756
     *
3757
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3758
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3759
     *                              <strong>SORT_NATURAL</strong></p>
3760
     *
3761
     * @return static
3762
     *                 <p>(Mutable) Return this Arrayy object.</p>
3763
     */
3764 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3765
    {
3766
        /** @noinspection UnusedFunctionResultInspection */
3767 18
        $this->sorterKeys($this->array, $direction, $strategy);
3768
3769 18
        return $this;
3770
    }
3771
3772
    /**
3773
     * Sort the current array by value.
3774
     *
3775
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3776
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3777
     *                              <strong>SORT_NATURAL</strong></p>
3778
     *
3779
     * @return static
3780
     *                <p>(Mutable)</p>
3781
     */
3782 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3783
    {
3784 1
        return $this->sort($direction, $strategy, true);
3785
    }
3786
3787
    /**
3788
     * Sort the current array by value.
3789
     *
3790
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3791
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3792
     *                              <strong>SORT_NATURAL</strong></p>
3793
     *
3794
     * @return static
3795
     *                <p>(Mutable)</p>
3796
     */
3797 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3798
    {
3799 1
        return $this->sort($direction, $strategy, false);
3800
    }
3801
3802
    /**
3803
     * Sort a array by value, by a closure or by a property.
3804
     *
3805
     * - If the sorter is null, the array is sorted naturally.
3806
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3807
     *
3808
     * @param \callable|null $sorter
3809
     * @param int|string     $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3810
     * @param int            $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3811
     *                                  <strong>SORT_NATURAL</strong></p>
3812
     *
3813
     * @return static
3814
     *                <p>(Immutable)</p>
3815
     */
3816 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3817
    {
3818 1
        $array = (array) $this->array;
3819 1
        $direction = $this->getDirection($direction);
3820
3821
        // Transform all values into their results.
3822 1
        if ($sorter) {
3823 1
            $arrayy = static::create($array, $this->iteratorClass, false);
3824
3825 1
            $that = $this;
3826 1
            $results = $arrayy->each(
3827 1
                function ($value) use ($sorter, $that) {
3828 1
                    return \is_callable($sorter) ? $sorter($value) : $that->get($sorter, null, $value);
3829 1
                }
3830
            );
3831
3832 1
            $results = $results->getArray();
3833
        } else {
3834 1
            $results = $array;
3835
        }
3836
3837
        // Sort by the results and replace by original values
3838 1
        \array_multisort($results, $direction, $strategy, $array);
3839
3840 1
        return static::create($array, $this->iteratorClass, false);
3841
    }
3842
3843
    /**
3844
     * sorting keys
3845
     *
3846
     * @param array      $elements
3847
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3848
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3849
     *                              <strong>SORT_NATURAL</strong></p>
3850
     *
3851
     * @return static
3852
     *                <p>(Mutable) Return this Arrayy object.</p>
3853
     */
3854 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR)
3855
    {
3856 18
        $direction = $this->getDirection($direction);
3857
3858
        switch ($direction) {
3859 18
            case 'desc':
3860 18
            case \SORT_DESC:
3861 6
                \krsort($elements, $strategy);
3862
3863 6
                break;
3864 13
            case 'asc':
3865 13
            case \SORT_ASC:
3866
            default:
3867 13
                \ksort($elements, $strategy);
3868
        }
3869
3870 18
        return $this;
3871
    }
3872
3873
    /**
3874
     * @param array       $elements <p>Warning: used as reference</p>
3875
     * @param int|string  $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3876
     * @param int         $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3877
     *                               <strong>SORT_NATURAL</strong></p>
3878
     * @param bool        $keepKeys
3879
     *
3880
     * @return static
3881
     *                <p>(Mutable) Return this Arrayy object.</p>
3882
     */
3883 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false)
3884
    {
3885 20
        $direction = $this->getDirection($direction);
3886
3887 20
        if (!$strategy) {
3888 20
            $strategy = \SORT_REGULAR;
3889
        }
3890
3891
        switch ($direction) {
3892 20
            case 'desc':
3893 20
            case \SORT_DESC:
3894 9
                if ($keepKeys) {
3895 5
                    \arsort($elements, $strategy);
3896
                } else {
3897 4
                    \rsort($elements, $strategy);
3898
                }
3899
3900 9
                break;
3901 11
            case 'asc':
3902 11
            case \SORT_ASC:
3903
            default:
3904 11
                if ($keepKeys) {
3905 4
                    \asort($elements, $strategy);
3906
                } else {
3907 7
                    \sort($elements, $strategy);
3908
                }
3909
        }
3910
3911 20
        return $this;
3912
    }
3913
3914
    /**
3915
     * Split an array in the given amount of pieces.
3916
     *
3917
     * @param int  $numberOfPieces
3918
     * @param bool $keepKeys
3919
     *
3920
     * @return static
3921
     *                <p>(Immutable)</p>
3922
     */
3923 1 View Code Duplication
    public function split(int $numberOfPieces = 2, bool $keepKeys = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3924
    {
3925 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
3926
3927 1
        if ($arrayCount === 0) {
3928 1
            $result = [];
3929
        } else {
3930 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
3931 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
3932
        }
3933
3934 1
        return static::create($result, $this->iteratorClass, false);
3935
    }
3936
3937
    /**
3938
     * Stripe all empty items.
3939
     *
3940
     * @return static
3941
     *                 <p>(Immutable)</p>
3942
     */
3943 1
    public function stripEmpty()
3944
    {
3945 1
        return $this->filter(
3946 1
            function ($item) {
3947 1
                if ($item === null) {
3948 1
                    return false;
3949
                }
3950
3951 1
                return (bool) \trim((string) $item);
3952 1
            }
3953
        );
3954
    }
3955
3956
    /**
3957
     * Swap two values between positions by key.
3958
     *
3959
     * @param int|string $swapA <p>a key in the array</p>
3960
     * @param int|string $swapB <p>a key in the array</p>
3961
     *
3962
     * @return static
3963
     *                <p>(Immutable)</p>
3964
     */
3965 1
    public function swap($swapA, $swapB)
3966
    {
3967 1
        $array = $this->array;
3968
3969 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
3970
3971 1
        return static::create($array, $this->iteratorClass, false);
3972
    }
3973
3974
    /**
3975
     * alias: for "Arrayy->getArray()"
3976
     *
3977
     * @see Arrayy::getArray()
3978
     */
3979 192
    public function toArray()
3980
    {
3981 192
        return $this->getArray();
3982
    }
3983
3984
    /**
3985
     * Convert the current array to JSON.
3986
     *
3987
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
3988
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
3989
     *
3990
     * @return string
3991
     */
3992 6
    public function toJson(int $options = 0, int $depth = 512): string
3993
    {
3994
        /** @noinspection PhpComposerExtensionStubsInspection */
3995 6
        return \json_encode($this->array, $options, $depth);
3996
    }
3997
3998
    /**
3999
     * Implodes array to a string with specified separator.
4000
     *
4001
     * @param string $separator [optional] <p>The element's separator.</p>
4002
     *
4003
     * @return string
4004
     *                <p>The string representation of array, separated by ",".</p>
4005
     */
4006 19
    public function toString(string $separator = ','): string
4007
    {
4008 19
        return $this->implode($separator);
4009
    }
4010
4011
    /**
4012
     * Return a duplicate free copy of the current array.
4013
     *
4014
     * @return static
4015
     *                <p>(Mutable)</p>
4016
     */
4017 10
    public function unique()
4018
    {
4019
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4020
4021 10
        $this->array = \array_reduce(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_reduce($this->arr...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...
4022 10
            $this->array,
4023 10
            function ($resultArray, $value) {
4024 9
                if (!\in_array($value, $resultArray, true)) {
4025 9
                    $resultArray[] = $value;
4026
                }
4027
4028 9
                return $resultArray;
4029 10
            },
4030 10
            []
4031
        );
4032
4033 10 View Code Duplication
        if ($this->array === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4034
            $this->array = [];
4035
        } else {
4036 10
            $this->array = (array) $this->array;
4037
        }
4038
4039 10
        return $this;
4040
    }
4041
4042
    /**
4043
     * Return a duplicate free copy of the current array. (with the old keys)
4044
     *
4045
     * @return static
4046
     *                 <p>(Mutable)</p>
4047
     */
4048 11
    public function uniqueKeepIndex()
4049
    {
4050
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4051
4052
        // init
4053 11
        $array = $this->array;
4054
4055 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...
4056 11
            \array_keys($array),
4057 11
            function ($resultArray, $key) use ($array) {
4058 10
                if (!\in_array($array[$key], $resultArray, true)) {
4059 10
                    $resultArray[$key] = $array[$key];
4060
                }
4061
4062 10
                return $resultArray;
4063 11
            },
4064 11
            []
4065
        );
4066
4067 11 View Code Duplication
        if ($this->array === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4068
            $this->array = [];
4069
        } else {
4070 11
            $this->array = (array) $this->array;
4071
        }
4072
4073 11
        return $this;
4074
    }
4075
4076
    /**
4077
     * alias: for "Arrayy->unique()"
4078
     *
4079
     * @see Arrayy::unique()
4080
     *
4081
     * @return static
4082
     *                 <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4083
     */
4084 10
    public function uniqueNewIndex()
4085
    {
4086 10
        return $this->unique();
4087
    }
4088
4089
    /**
4090
     * Prepends one or more values to the beginning of array at once.
4091
     *
4092
     * @return static
4093
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4094
     */
4095 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4096
    {
4097 4
        if (\func_num_args()) {
4098 4
            $args = \array_merge([&$this->array], \func_get_args());
4099 4
            \array_unshift(...$args);
4100
        }
4101
4102 4
        return $this;
4103
    }
4104
4105
    /**
4106
     * Get all values from a array.
4107
     *
4108
     * @return static
4109
     *                 <p>(Immutable)</p>
4110
     */
4111 2
    public function values()
4112
    {
4113 2
        return static::create(
4114 2
            \array_values((array) $this->array),
4115 2
            $this->iteratorClass,
4116 2
            false
4117
        );
4118
    }
4119
4120
    /**
4121
     * Apply the given function to every element in the array, discarding the results.
4122
     *
4123
     * @param \callable $callable
4124
     * @param bool      $recursive <p>Whether array will be walked recursively or no</p>
4125
     *
4126
     * @return static
4127
     *                 <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4128
     */
4129 37
    public function walk($callable, bool $recursive = false)
4130
    {
4131 37
        if ($recursive === true) {
4132 32
            \array_walk_recursive($this->array, $callable);
4133
        } else {
4134 18
            \array_walk($this->array, $callable);
4135
        }
4136
4137 37
        return $this;
4138
    }
4139
}
4140