Completed
Push — master ( 9acfcd...65928f )
by Lars
02:05
created

Arrayy::sizeIsGreaterThan()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 14
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 3

Importance

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1135
     *
1136
     * @return static
1137
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1138
     */
1139 5
    public static function createFromGeneratorFunction(callable $generatorFunction): self
1140
    {
1141 5
        $arrayy = new static($generatorFunction);
1142
1143 5
        return $arrayy;
1144
    }
1145
1146
    /**
1147
     * Create an new instance filled with a copy of values from a "Traversable"-object.
1148
     *
1149
     * @param \Traversable $traversable
1150
     *
1151
     * @return static
1152
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1153
     */
1154
    public static function createFromTraversableImmutable(\Traversable $traversable): self
1155
    {
1156
        return new static(\iterator_to_array($traversable, true));
1157
    }
1158
1159
    /**
1160
     * Create an new instance filled with a copy of values from a "Generator"-object.
1161
     *
1162
     * @param \Generator $generator
1163
     *
1164
     * @return static
1165
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1166
     */
1167 4
    public static function createFromGeneratorImmutable(\Generator $generator): self
1168
    {
1169 4
        return new static(\iterator_to_array($generator, true));
1170
    }
1171
1172
    /**
1173
     * Create an new Arrayy object via JSON.
1174
     *
1175
     * @param string $json
1176
     *
1177
     * @return static
1178
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1179
     */
1180 5
    public static function createFromJson(string $json): self
1181
    {
1182
        /** @noinspection PhpComposerExtensionStubsInspection */
1183 5
        return static::create(\json_decode($json, true));
1184
    }
1185
1186
    /**
1187
     * Create an new instance filled with values from an object that is iterable.
1188
     *
1189
     * @param \Traversable $object <p>iterable object</p>
1190
     *
1191
     * @return static
1192
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1193
     */
1194 4
    public static function createFromObject(\Traversable $object): self
1195
    {
1196
        // init
1197 4
        $array = new static();
1198
1199 4
        if ($object instanceof self) {
1200 4
            $objectArray = $object->getGenerator();
1201
        } else {
1202
            $objectArray = $object;
1203
        }
1204
1205 4
        foreach ($objectArray as $key => $value) {
1206 3
            $array[$key] = $value;
1207
        }
1208
1209 4
        return $array;
1210
    }
1211
1212
    /**
1213
     * Create an new instance filled with values from an object.
1214
     *
1215
     * @param object $object
1216
     *
1217
     * @return static
1218
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1219
     */
1220 5
    public static function createFromObjectVars($object): self
1221
    {
1222 5
        return new static(self::objectToArray($object));
1223
    }
1224
1225
    /**
1226
     * Create an new Arrayy object via string.
1227
     *
1228
     * @param string      $str       <p>The input string.</p>
1229
     * @param string|null $delimiter <p>The boundary string.</p>
1230
     * @param string|null $regEx     <p>Use the $delimiter or the $regEx, so if $pattern is null, $delimiter will be
1231
     *                               used.</p>
1232
     *
1233
     * @return static
1234
     *                <p>(Immutable) Returns an new instance of the Arrayy object.</p>
1235
     */
1236 10
    public static function createFromString(string $str, string $delimiter = null, string $regEx = null): self
1237
    {
1238 10
        if ($regEx) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $regEx of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
1441
    {
1442
        // init
1443 4
        $array = [];
1444
1445 4
        foreach ($this->getGenerator() as $key => $value) {
1446 4
            $array[$key] = $closure($value, $key);
1447
        }
1448
1449 4
        return static::create(
1450 4
            $array,
1451 4
            $this->iteratorClass,
1452 4
            false
1453
        );
1454
    }
1455
1456
    /**
1457
     * Check if a value is in the current array using a closure.
1458
     *
1459
     * @param \Closure $closure
1460
     *
1461
     * @return bool
1462
     *              <p>Returns true if the given value is found, false otherwise.</p>
1463
     */
1464 4
    public function exists(\Closure $closure): bool
1465
    {
1466
        // init
1467 4
        $isExists = false;
1468
1469 4
        foreach ($this->getGenerator() as $key => $value) {
1470 3
            if ($closure($value, $key)) {
1471 1
                $isExists = true;
1472
1473 3
                break;
1474
            }
1475
        }
1476
1477 4
        return $isExists;
1478
    }
1479
1480
    /**
1481
     * Fill the array until "$num" with "$default" values.
1482
     *
1483
     * @param int   $num
1484
     * @param mixed $default
1485
     *
1486
     * @return static
1487
     *                <p>(Immutable)</p>
1488
     */
1489 8
    public function fillWithDefaults(int $num, $default = null): self
1490
    {
1491 8
        if ($num < 0) {
1492 1
            throw new \InvalidArgumentException('The $num parameter can only contain non-negative values.');
1493
        }
1494
1495 7
        $this->generatorToArray();
1496
1497 7
        $tmpArray = $this->array;
1498
1499 7
        $count = \count($tmpArray);
1500
1501 7
        while ($count < $num) {
1502 4
            $tmpArray[] = $default;
1503 4
            ++$count;
1504
        }
1505
1506 7
        return static::create(
1507 7
            $tmpArray,
1508 7
            $this->iteratorClass,
1509 7
            false
1510
        );
1511
    }
1512
1513
    /**
1514
     * Find all items in an array that pass the truth test.
1515
     *
1516
     * @param \Closure|null $closure [optional] <p>
1517
     *                               The callback function to use
1518
     *                               </p>
1519
     *                               <p>
1520
     *                               If no callback is supplied, all entries of
1521
     *                               input equal to false (see
1522
     *                               converting to
1523
     *                               boolean) will be removed.
1524
     *                               </p>
1525
     *                               * @param int $flag [optional] <p>
1526
     *                               Flag determining what arguments are sent to <i>callback</i>:
1527
     *                               </p><ul>
1528
     *                               <li>
1529
     *                               <b>ARRAY_FILTER_USE_KEY</b> [1] - pass key as the only argument
1530
     *                               to <i>callback</i> instead of the value</span>
1531
     *                               </li>
1532
     *                               <li>
1533
     *                               <b>ARRAY_FILTER_USE_BOTH</b> [2] - pass both value and key as
1534
     *                               arguments to <i>callback</i> instead of the value</span>
1535
     *                               </li>
1536
     *                               </ul>
1537
     *
1538
     * @return static
1539
     *                <p>(Immutable)</p>
1540
     */
1541 11
    public function filter($closure = null, int $flag = \ARRAY_FILTER_USE_BOTH): self
1542
    {
1543 11
        if (!$closure) {
1544 1
            return $this->clean();
1545
        }
1546
1547 11
        return static::create(
1548 11
            \array_filter($this->getArray(), $closure, $flag),
1549 11
            $this->iteratorClass,
1550 11
            false
1551
        );
1552
    }
1553
1554
    /**
1555
     * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular
1556
     * property within that.
1557
     *
1558
     * @param string          $property
1559
     * @param string|string[] $value
1560
     * @param string          $comparisonOp
1561
     *                                      <p>
1562
     *                                      'eq' (equals),<br />
1563
     *                                      'gt' (greater),<br />
1564
     *                                      'gte' || 'ge' (greater or equals),<br />
1565
     *                                      'lt' (less),<br />
1566
     *                                      'lte' || 'le' (less or equals),<br />
1567
     *                                      'ne' (not equals),<br />
1568
     *                                      'contains',<br />
1569
     *                                      'notContains',<br />
1570
     *                                      'newer' (via strtotime),<br />
1571
     *                                      'older' (via strtotime),<br />
1572
     *                                      </p>
1573
     *
1574
     * @return static
1575
     *                <p>(Immutable)</p>
1576
     */
1577 1
    public function filterBy(string $property, $value, string $comparisonOp = null): self
1578
    {
1579 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...
1580 1
            $comparisonOp = \is_array($value) ? 'contains' : 'eq';
1581
        }
1582
1583
        $ops = [
1584
            'eq' => static function ($item, $prop, $value) {
1585 1
                return $item[$prop] === $value;
1586 1
            },
1587
            'gt' => static function ($item, $prop, $value) {
1588
                return $item[$prop] > $value;
1589 1
            },
1590
            'ge' => static function ($item, $prop, $value) {
1591
                return $item[$prop] >= $value;
1592 1
            },
1593
            'gte' => static function ($item, $prop, $value) {
1594
                return $item[$prop] >= $value;
1595 1
            },
1596
            'lt' => static function ($item, $prop, $value) {
1597 1
                return $item[$prop] < $value;
1598 1
            },
1599
            'le' => static function ($item, $prop, $value) {
1600
                return $item[$prop] <= $value;
1601 1
            },
1602
            'lte' => static function ($item, $prop, $value) {
1603
                return $item[$prop] <= $value;
1604 1
            },
1605
            'ne' => static function ($item, $prop, $value) {
1606
                return $item[$prop] !== $value;
1607 1
            },
1608
            'contains' => static function ($item, $prop, $value) {
1609 1
                return \in_array($item[$prop], (array) $value, true);
1610 1
            },
1611
            'notContains' => static function ($item, $prop, $value) {
1612
                return !\in_array($item[$prop], (array) $value, true);
1613 1
            },
1614
            'newer' => static function ($item, $prop, $value) {
1615
                return \strtotime($item[$prop]) > \strtotime($value);
1616 1
            },
1617
            'older' => static function ($item, $prop, $value) {
1618
                return \strtotime($item[$prop]) < \strtotime($value);
1619 1
            },
1620
        ];
1621
1622 1
        $result = \array_values(
1623 1
            \array_filter(
1624 1
                $this->getArray(),
1625
                static function ($item) use (
1626 1
                    $property,
1627 1
                    $value,
1628 1
                    $ops,
1629 1
                    $comparisonOp
1630
                ) {
1631 1
                    $item = (array) $item;
1632 1
                    $itemArrayy = new static($item);
1633 1
                    $item[$property] = $itemArrayy->get($property, []);
1634
1635 1
                    return $ops[$comparisonOp]($item, $property, $value);
1636 1
                }
1637
            )
1638
        );
1639
1640 1
        return static::create(
1641 1
            $result,
1642 1
            $this->iteratorClass,
1643 1
            false
1644
        );
1645
    }
1646
1647
    /**
1648
     * Find the first item in an array that passes the truth test,
1649
     *  otherwise return false
1650
     *
1651
     * @param \Closure $closure
1652
     *
1653
     * @return false|mixed
1654
     *                     <p>Return false if we did not find the value.</p>
1655
     */
1656 8
    public function find(\Closure $closure)
1657
    {
1658 8
        foreach ($this->getGenerator() as $key => $value) {
1659 6
            if ($closure($value, $key)) {
1660 6
                return $value;
1661
            }
1662
        }
1663
1664 3
        return false;
1665
    }
1666
1667
    /**
1668
     * find by ...
1669
     *
1670
     * @param string          $property
1671
     * @param string|string[] $value
1672
     * @param string          $comparisonOp
1673
     *
1674
     * @return static
1675
     *                <p>(Immutable)</p>
1676
     */
1677
    public function findBy(string $property, $value, string $comparisonOp = 'eq'): self
1678
    {
1679
        return $this->filterBy($property, $value, $comparisonOp);
1680
    }
1681
1682
    /**
1683
     * Get the first value from the current array.
1684
     *
1685
     * @return mixed
1686
     *               <p>Return null if there wasn't a element.</p>
1687
     */
1688 13
    public function first()
1689
    {
1690 13
        $tmpArray = $this->getArray();
1691
1692 13
        return \array_shift($tmpArray);
1693
    }
1694
1695
    /**
1696
     * Get the first value(s) from the current array.
1697
     * And will return an empty array if there was no first entry.
1698
     *
1699
     * @param int|null $number <p>How many values you will take?</p>
1700
     *
1701
     * @return static
1702
     *                <p>(Immutable)</p>
1703
     */
1704 36
    public function firstsImmutable(int $number = null): self
1705
    {
1706 36
        $arrayTmp = $this->getArray();
1707
1708 36
        if ($number === null) {
1709 14
            $array = (array) \array_shift($arrayTmp);
1710
        } else {
1711 22
            $number = (int) $number;
1712 22
            $array = \array_splice($arrayTmp, 0, $number);
1713
        }
1714
1715 36
        return static::create(
1716 36
            $array,
1717 36
            $this->iteratorClass,
1718 36
            false
1719
        );
1720
    }
1721
1722
    /**
1723
     * Get the first value(s) from the current array.
1724
     * And will return an empty array if there was no first entry.
1725
     *
1726
     * @param int|null $number <p>How many values you will take?</p>
1727
     *
1728
     * @return static
1729
     *                <p>(Mutable)</p>
1730
     */
1731 34
    public function firstsMutable(int $number = null): self
1732
    {
1733 34
        $this->generatorToArray();
1734
1735 34
        if ($number === null) {
1736 19
            $this->array = (array) \array_shift($this->array);
1737
        } else {
1738 15
            $number = (int) $number;
1739 15
            $this->array = \array_splice($this->array, 0, $number);
1740
        }
1741
1742 34
        return $this;
1743
    }
1744
1745
    /**
1746
     * Exchanges all keys with their associated values in an array.
1747
     *
1748
     * @return static
1749
     *                <p>(Immutable)</p>
1750
     */
1751 1
    public function flip(): self
1752
    {
1753 1
        return static::create(
1754 1
            \array_flip($this->getArray()),
1755 1
            $this->iteratorClass,
1756 1
            false
1757
        );
1758
    }
1759
1760
    /**
1761
     * Get a value from an array (optional using dot-notation).
1762
     *
1763
     * @param mixed $key      <p>The key to look for.</p>
1764
     * @param mixed $fallback <p>Value to fallback to.</p>
1765
     * @param array $array    <p>The array to get from, if it's set to "null" we use the current array from the
1766
     *                        class.</p>
1767
     *
1768
     * @return mixed|static
1769
     */
1770 73
    public function get($key, $fallback = null, array $array = null)
1771
    {
1772 73
        if ($array !== null) {
1773 5
            $usedArray = $array;
1774
        } else {
1775 68
            $this->generatorToArray();
1776
1777 68
            $usedArray = $this->array;
1778
        }
1779
1780 73
        if ($key === null) {
1781 1
            return static::create(
1782 1
                $usedArray,
1783 1
                $this->iteratorClass,
1784 1
                false
1785
            );
1786
        }
1787
1788
        // php cast "bool"-index into "int"-index
1789 73
        if ((bool) $key === $key) {
1790 3
            $key = (int) $key;
1791
        }
1792
1793 73
        if (\array_key_exists($key, $usedArray) === true) {
1794 62
            if (\is_array($usedArray[$key])) {
1795 9
                return static::create(
1796 9
                    $usedArray[$key],
1797 9
                    $this->iteratorClass,
1798 9
                    false
1799
                );
1800
            }
1801
1802 55
            return $usedArray[$key];
1803
        }
1804
1805
        // crawl through array, get key according to object or not
1806 23
        $usePath = false;
1807
        if (
1808 23
            $this->pathSeparator
1809
            &&
1810 23
            \is_string($key)
1811
            &&
1812 23
            \strpos($key, $this->pathSeparator) !== false
1813
        ) {
1814 7
            $segments = \explode($this->pathSeparator, (string) $key);
1815 7
            if ($segments !== false) {
1816 7
                $usePath = true;
1817
1818 7
                foreach ($segments as $segment) {
1819
                    if (
1820
                        (
1821 7
                            \is_array($usedArray)
1822
                            ||
1823 7
                            $usedArray instanceof \ArrayAccess
1824
                        )
1825
                        &&
1826 7
                        isset($usedArray[$segment])
1827
                    ) {
1828 7
                        $usedArray = $usedArray[$segment];
1829
1830 7
                        continue;
1831
                    }
1832
1833
                    if (
1834 6
                        \is_object($usedArray)
1835
                        &&
1836 6
                        \property_exists($usedArray, $segment)
1837
                    ) {
1838 1
                        $usedArray = $usedArray->{$segment};
1839
1840 1
                        continue;
1841
                    }
1842
1843 5
                    return $fallback instanceof \Closure ? $fallback() : $fallback;
1844
                }
1845
            }
1846
        }
1847
1848 23
        if (!$usePath && !isset($usedArray[$key])) {
1849 16
            return $fallback instanceof \Closure ? $fallback() : $fallback;
1850
        }
1851
1852 7
        if (\is_array($usedArray)) {
1853 1
            return static::create(
1854 1
                $usedArray,
1855 1
                $this->iteratorClass,
1856 1
                false
1857
            );
1858
        }
1859
1860 7
        return $usedArray;
1861
    }
1862
1863
    /**
1864
     * Get the current array from the "Arrayy"-object.
1865
     *
1866
     * @return array
1867
     */
1868 802
    public function getArray(): array
1869
    {
1870
        // init
1871 802
        $array = [];
1872
1873 802
        foreach ($this->getGenerator() as $key => $value) {
1874 693
            $array[$key] = $value;
1875
        }
1876
1877 802
        return $array;
1878
    }
1879
1880
    /**
1881
     * Returns the values from a single column of the input array, identified by
1882
     * the $columnKey, can be used to extract data-columns from multi-arrays.
1883
     *
1884
     * Info: Optionally, you may provide an $indexKey to index the values in the returned
1885
     * array by the values from the $indexKey column in the input array.
1886
     *
1887
     * @param mixed $columnKey
1888
     * @param mixed $indexKey
1889
     *
1890
     * @return static
1891
     *                <p>(Immutable)</p>
1892
     */
1893 1
    public function getColumn($columnKey = null, $indexKey = null): self
1894
    {
1895 1
        return static::create(
1896 1
            \array_column($this->getArray(), $columnKey, $indexKey),
1897 1
            $this->iteratorClass,
1898 1
            false
1899
        );
1900
    }
1901
1902
    /**
1903
     * Get the current array from the "Arrayy"-object as generator.
1904
     *
1905
     * @return \Generator
1906
     */
1907 851
    public function getGenerator(): \Generator
1908
    {
1909 851
        if ($this->generator instanceof ArrayyRewindableGenerator) {
1910 31
            yield from $this->generator;
1911
        }
1912
1913 839
        yield from $this->array;
1914 824
    }
1915
1916
    /**
1917
     * alias: for "Arrayy->keys()"
1918
     *
1919
     * @return static
1920
     *                <p>(Immutable)</p>
1921
     *
1922
     * @see Arrayy::keys()
1923
     */
1924 1
    public function getKeys(): self
1925
    {
1926 1
        return $this->keys();
1927
    }
1928
1929
    /**
1930
     * Get the current array from the "Arrayy"-object as object.
1931
     *
1932
     * @return \stdClass
1933
     */
1934 4
    public function getObject(): \stdClass
1935
    {
1936 4
        return self::arrayToObject($this->getArray());
1937
    }
1938
1939
    /**
1940
     * alias: for "Arrayy->randomImmutable()"
1941
     *
1942
     * @return static
1943
     *                <p>(Immutable)</p>
1944
     *
1945
     * @see Arrayy::randomImmutable()
1946
     */
1947 4
    public function getRandom(): self
1948
    {
1949 4
        return $this->randomImmutable();
1950
    }
1951
1952
    /**
1953
     * alias: for "Arrayy->randomKey()"
1954
     *
1955
     * @return mixed
1956
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
1957
     *
1958
     * @see Arrayy::randomKey()
1959
     */
1960 3
    public function getRandomKey()
1961
    {
1962 3
        return $this->randomKey();
1963
    }
1964
1965
    /**
1966
     * alias: for "Arrayy->randomKeys()"
1967
     *
1968
     * @param int $number
1969
     *
1970
     * @return static
1971
     *                <p>(Immutable)</p>
1972
     *
1973
     * @see Arrayy::randomKeys()
1974
     */
1975 8
    public function getRandomKeys(int $number): self
1976
    {
1977 8
        return $this->randomKeys($number);
1978
    }
1979
1980
    /**
1981
     * alias: for "Arrayy->randomValue()"
1982
     *
1983
     * @return mixed
1984
     *               <p>Get a random value or null if there wasn't a value.</p>
1985
     *
1986
     * @see Arrayy::randomValue()
1987
     */
1988 3
    public function getRandomValue()
1989
    {
1990 3
        return $this->randomValue();
1991
    }
1992
1993
    /**
1994
     * alias: for "Arrayy->randomValues()"
1995
     *
1996
     * @param int $number
1997
     *
1998
     * @return static
1999
     *                <p>(Immutable)</p>
2000
     *
2001
     * @see Arrayy::randomValues()
2002
     */
2003 6
    public function getRandomValues(int $number): self
2004
    {
2005 6
        return $this->randomValues($number);
2006
    }
2007
2008
    /**
2009
     * Group values from a array according to the results of a closure.
2010
     *
2011
     * @param callable|string $grouper  <p>A callable function name.</p>
2012
     * @param bool            $saveKeys
2013
     *
2014
     * @return static
2015
     *                <p>(Immutable)</p>
2016
     */
2017 4
    public function group($grouper, bool $saveKeys = false): self
2018
    {
2019
        // init
2020 4
        $result = [];
2021
2022
        // Iterate over values, group by property/results from closure.
2023 4
        foreach ($this->getGenerator() as $key => $value) {
2024 4
            if (\is_callable($grouper)) {
2025 3
                $groupKey = $grouper($value, $key);
2026
            } else {
2027 1
                $groupKey = $this->get($grouper, null, $this->getArray());
2028
            }
2029
2030 4
            $newValue = $this->get($groupKey, null, $result);
2031
2032 4
            if ($groupKey instanceof self) {
2033
                $groupKey = $groupKey->getArray();
2034
            }
2035
2036 4
            if ($newValue instanceof self) {
2037 4
                $newValue = $newValue->getArray();
2038
            }
2039
2040
            // Add to results.
2041 4
            if ($groupKey !== null) {
2042 3
                if ($saveKeys) {
2043 2
                    $result[$groupKey] = $newValue;
2044 2
                    $result[$groupKey][$key] = $value;
2045
                } else {
2046 1
                    $result[$groupKey] = $newValue;
2047 4
                    $result[$groupKey][] = $value;
2048
                }
2049
            }
2050
        }
2051
2052 4
        return static::create(
2053 4
            $result,
2054 4
            $this->iteratorClass,
2055 4
            false
2056
        );
2057
    }
2058
2059
    /**
2060
     * Check if an array has a given key.
2061
     *
2062
     * @param mixed $key
2063
     *
2064
     * @return bool
2065
     */
2066 23
    public function has($key): bool
2067
    {
2068 23
        static $UN_FOUND = null;
2069
2070 23
        if ($UN_FOUND === null) {
2071
            // Generate unique string to use as marker.
2072 1
            $UN_FOUND = \uniqid('arrayy', true);
2073
        }
2074
2075 23
        return $this->get($key, $UN_FOUND) !== $UN_FOUND;
2076
    }
2077
2078
    /**
2079
     * Implodes the values of this array.
2080
     *
2081
     * @param string $glue
2082
     *
2083
     * @return string
2084
     */
2085 27
    public function implode(string $glue = ''): string
2086
    {
2087 27
        return $this->implode_recursive($glue, $this->getArray(), false);
2088
    }
2089
2090
    /**
2091
     * Implodes the keys of this array.
2092
     *
2093
     * @param string $glue
2094
     *
2095
     * @return string
2096
     */
2097 8
    public function implodeKeys(string $glue = ''): string
2098
    {
2099 8
        return $this->implode_recursive($glue, $this->getArray(), true);
2100
    }
2101
2102
    /**
2103
     * Given a list and an iterate-function that returns
2104
     * a key for each element in the list (or a property name),
2105
     * returns an object with an index of each item.
2106
     *
2107
     * @param mixed $key
2108
     *
2109
     * @return static
2110
     *                <p>(Immutable)</p>
2111
     */
2112 4
    public function indexBy($key): self
2113
    {
2114
        // init
2115 4
        $results = [];
2116
2117 4
        foreach ($this->getGenerator() as $a) {
2118 4
            if (\array_key_exists($key, $a) === true) {
2119 4
                $results[$a[$key]] = $a;
2120
            }
2121
        }
2122
2123 4
        return static::create(
2124 4
            $results,
2125 4
            $this->iteratorClass,
2126 4
            false
2127
        );
2128
    }
2129
2130
    /**
2131
     * alias: for "Arrayy->searchIndex()"
2132
     *
2133
     * @param mixed $value <p>The value to search for.</p>
2134
     *
2135
     * @return mixed
2136
     *
2137
     * @see Arrayy::searchIndex()
2138
     */
2139 4
    public function indexOf($value)
2140
    {
2141 4
        return $this->searchIndex($value);
2142
    }
2143
2144
    /**
2145
     * Get everything but the last..$to items.
2146
     *
2147
     * @param int $to
2148
     *
2149
     * @return static
2150
     *                <p>(Immutable)</p>
2151
     */
2152 12
    public function initial(int $to = 1): self
2153
    {
2154 12
        return $this->firstsImmutable(\count($this->getArray(), \COUNT_NORMAL) - $to);
2155
    }
2156
2157
    /**
2158
     * Return an array with all elements found in input array.
2159
     *
2160
     * @param array $search
2161
     * @param bool  $keepKeys
2162
     *
2163
     * @return static
2164
     *                <p>(Immutable)</p>
2165
     */
2166 3
    public function intersection(array $search, bool $keepKeys = false): self
2167
    {
2168 3
        if ($keepKeys) {
2169 1
            return static::create(
2170 1
                \array_uintersect(
2171 1
                    $this->array,
2172 1
                    $search,
2173
                    static function ($a, $b) {
2174 1
                        return $a === $b ? 0 : -1;
2175 1
                    }
2176
                ),
2177 1
                $this->iteratorClass,
2178 1
                false
2179
            );
2180
        }
2181
2182 2
        return static::create(
2183 2
            \array_values(\array_intersect($this->getArray(), $search)),
2184 2
            $this->iteratorClass,
2185 2
            false
2186
        );
2187
    }
2188
2189
    /**
2190
     * Return a boolean flag which indicates whether the two input arrays have any common elements.
2191
     *
2192
     * @param array $search
2193
     *
2194
     * @return bool
2195
     */
2196 1
    public function intersects(array $search): bool
2197
    {
2198 1
        return \count($this->intersection($search)->array, \COUNT_NORMAL) > 0;
2199
    }
2200
2201
    /**
2202
     * Invoke a function on all of an array's values.
2203
     *
2204
     * @param mixed $callable
2205
     * @param mixed $arguments
2206
     *
2207
     * @return static
2208
     *                <p>(Immutable)</p>
2209
     */
2210 1
    public function invoke($callable, $arguments = []): self
2211
    {
2212
        // If one argument given for each iteration, create an array for it.
2213 1
        if (!\is_array($arguments)) {
2214 1
            $arguments = StaticArrayy::repeat(
2215 1
                $arguments,
2216 1
                \count($this->getArray(), \COUNT_NORMAL)
2217 1
            )->getArray();
2218
        }
2219
2220
        // If the callable has arguments, pass them.
2221 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...
2222 1
            $array = \array_map($callable, $this->getArray(), $arguments);
2223
        } else {
2224 1
            $array = \array_map($callable, $this->getArray());
2225
        }
2226
2227 1
        return static::create(
2228 1
            $array,
2229 1
            $this->iteratorClass,
2230 1
            false
2231
        );
2232
    }
2233
2234
    /**
2235
     * Check whether array is associative or not.
2236
     *
2237
     * @param bool $recursive
2238
     *
2239
     * @return bool
2240
     *              <p>Returns true if associative, false otherwise.</p>
2241
     */
2242 15
    public function isAssoc(bool $recursive = false): bool
2243
    {
2244 15
        if ($this->isEmpty()) {
2245 3
            return false;
2246
        }
2247
2248 13
        foreach ($this->keys($recursive)->getGenerator() as $key) {
2249 13
            if (!\is_string($key)) {
2250 13
                return false;
2251
            }
2252
        }
2253
2254 3
        return true;
2255
    }
2256
2257
    /**
2258
     * Check whether the array is empty or not.
2259
     *
2260
     * @return bool
2261
     *              <p>Returns true if empty, false otherwise.</p>
2262
     */
2263 38
    public function isEmpty(): bool
2264
    {
2265 38
        if ($this->generator) {
2266
            return $this->getArray() === [];
2267
        }
2268
2269 38
        return $this->array === [];
2270
    }
2271
2272
    /**
2273
     * Check if the current array is equal to the given "$array" or not.
2274
     *
2275
     * @param array $array
2276
     *
2277
     * @return bool
2278
     */
2279
    public function isEqual(array $array): bool
2280
    {
2281
        return $this->getArray() === $array;
2282
    }
2283
2284
    /**
2285
     * Check if the current array is a multi-array.
2286
     *
2287
     * @return bool
2288
     */
2289 14
    public function isMultiArray(): bool
2290
    {
2291
        return !(
2292 14
            \count($this->getArray(), \COUNT_NORMAL)
2293
            ===
2294 14
            \count($this->getArray(), \COUNT_RECURSIVE)
2295
        );
2296
    }
2297
2298
    /**
2299
     * Check whether array is numeric or not.
2300
     *
2301
     * @return bool
2302
     *              <p>Returns true if numeric, false otherwise.</p>
2303
     */
2304 5
    public function isNumeric(): bool
2305
    {
2306 5
        if ($this->isEmpty()) {
2307 2
            return false;
2308
        }
2309
2310 4
        foreach ($this->keys()->getGenerator() as $key) {
2311 4
            if (!\is_int($key)) {
2312 4
                return false;
2313
            }
2314
        }
2315
2316 2
        return true;
2317
    }
2318
2319
    /**
2320
     * Check if the current array is sequential [0, 1, 2, 3, 4, 5 ...] or not.
2321
     *
2322
     * @param bool $recursive
2323
     *
2324
     * @return bool
2325
     */
2326 1
    public function isSequential(bool $recursive = false): bool
2327
    {
2328
2329
        // recursive
2330
2331 1
        if ($recursive === true) {
2332
            return $this->array_keys_recursive($this->getArray())
2333
                   ===
2334
                   \range(0, \count($this->getArray(), \COUNT_RECURSIVE) - 1);
2335
        }
2336
2337
        // non recursive
2338
2339 1
        return \array_keys($this->getArray())
2340
               ===
2341 1
               \range(0, \count($this->getArray(), \COUNT_NORMAL) - 1);
2342
    }
2343
2344
    /**
2345
     * @return array
2346
     */
2347
    public function jsonSerialize(): array
2348
    {
2349
        return $this->getArray();
2350
    }
2351
2352
    /**
2353
     * Get all keys from the current array.
2354
     *
2355
     * @param bool  $recursive    [optional] <p>
2356
     *                            Get all keys, also from all sub-arrays from an multi-dimensional array.
2357
     *                            </p>
2358
     * @param mixed $search_value [optional] <p>
2359
     *                            If specified, then only keys containing these values are returned.
2360
     *                            </p>
2361
     * @param bool  $strict       [optional] <p>
2362
     *                            Determines if strict comparison (===) should be used during the search.
2363
     *                            </p>
2364
     *
2365
     * @return static
2366
     *                <p>(Immutable) An array of all the keys in input.</p>
2367
     */
2368 26
    public function keys(bool $recursive = false, $search_value = null, bool $strict = true): self
2369
    {
2370
2371
        // recursive
2372
2373 26
        if ($recursive === true) {
2374 3
            if ($search_value === null) {
2375 3
                $array = $this->array_keys_recursive($this->getArray());
2376
            } else {
2377
                $array = $this->array_keys_recursive($this->getArray(), $search_value, $strict);
2378
            }
2379
2380 3
            return static::create(
2381 3
                $array,
2382 3
                $this->iteratorClass,
2383 3
                false
2384
            );
2385
        }
2386
2387
        // non recursive
2388
2389 25
        if ($search_value === null) {
2390
            $arrayFunction = function () {
2391 25
                foreach ($this->array as $key => $value) {
2392 24
                    yield $key;
2393
                }
2394 25
            };
2395
        } else {
2396
            $arrayFunction = function () use ($search_value, $strict) {
2397
                foreach ($this->array as $key => $value) {
2398
                    if ($strict) {
2399
                        if ($search_value === $value) {
2400
                            yield $key;
2401
                        }
2402
                    } else {
2403
                        /** @noinspection NestedPositiveIfStatementsInspection */
2404
                        if ($search_value == $value) {
2405
                            yield $key;
2406
                        }
2407
                    }
2408
                }
2409
            };
2410
        }
2411
2412 25
        return static::create(
2413 25
            $arrayFunction,
2414 25
            $this->iteratorClass,
2415 25
            false
2416
        );
2417
    }
2418
2419
    /**
2420
     * Sort an array by key in reverse order.
2421
     *
2422
     * @param int $sort_flags [optional] <p>
2423
     *                        You may modify the behavior of the sort using the optional
2424
     *                        parameter sort_flags, for details
2425
     *                        see sort.
2426
     *                        </p>
2427
     *
2428
     * @return static
2429
     *                <p>(Mutable) Return this Arrayy object.</p>
2430
     */
2431 4
    public function krsort(int $sort_flags = 0): self
2432
    {
2433 4
        $this->generatorToArray();
2434
2435 4
        \krsort($this->array, $sort_flags);
2436
2437 4
        return $this;
2438
    }
2439
2440
    /**
2441
     * Get the last value from the current array.
2442
     *
2443
     * @return mixed
2444
     *               <p>Return null if there wasn't a element.</p>
2445
     */
2446 4
    public function last()
2447
    {
2448 4
        return $this->pop();
2449
    }
2450
2451
    /**
2452
     * Get the last value(s) from the current array.
2453
     *
2454
     * @param int|null $number
2455
     *
2456
     * @return static
2457
     *                <p>(Immutable)</p>
2458
     */
2459 13
    public function lastsImmutable(int $number = null): self
2460
    {
2461 13
        if ($this->isEmpty()) {
2462 1
            return static::create(
2463 1
                [],
2464 1
                $this->iteratorClass,
2465 1
                false
2466
            );
2467
        }
2468
2469 12
        if ($number === null) {
2470 8
            $poppedValue = $this->pop();
2471
2472 8
            if ($poppedValue === null) {
2473 1
                $poppedValue = [$poppedValue];
2474
            } else {
2475 7
                $poppedValue = (array) $poppedValue;
2476
            }
2477
2478 8
            $arrayy = static::create(
2479 8
                $poppedValue,
2480 8
                $this->iteratorClass,
2481 8
                false
2482
            );
2483
        } else {
2484 4
            $number = (int) $number;
2485 4
            $arrayy = $this->rest(-$number);
2486
        }
2487
2488 12
        return $arrayy;
2489
    }
2490
2491
    /**
2492
     * Get the last value(s) from the current array.
2493
     *
2494
     * @param int|null $number
2495
     *
2496
     * @return static
2497
     *                <p>(Mutable)</p>
2498
     */
2499 13
    public function lastsMutable(int $number = null): self
2500
    {
2501 13
        if ($this->isEmpty()) {
2502 1
            return $this;
2503
        }
2504
2505 12
        if ($number === null) {
2506 8
            $poppedValue = $this->pop();
2507
2508 8
            if ($poppedValue === null) {
2509 1
                $poppedValue = [$poppedValue];
2510
            } else {
2511 7
                $poppedValue = (array) $poppedValue;
2512
            }
2513
2514 8
            $this->array = static::create(
2515 8
                $poppedValue,
2516 8
                $this->iteratorClass,
2517 8
                false
2518 8
            )->getArray();
2519
        } else {
2520 4
            $number = (int) $number;
2521 4
            $this->array = $this->rest(-$number)->getArray();
2522
        }
2523
2524 12
        $this->generator = null;
2525
2526 12
        return $this;
2527
    }
2528
2529
    /**
2530
     * Count the values from the current array.
2531
     *
2532
     * alias: for "Arrayy->count()"
2533
     *
2534
     * @param int $mode
2535
     *
2536
     * @return int
2537
     *
2538
     * @see Arrayy::count()
2539
     */
2540 20
    public function length(int $mode = \COUNT_NORMAL): int
2541
    {
2542 20
        return $this->count($mode);
2543
    }
2544
2545
    /**
2546
     * Apply the given function to the every element of the array,
2547
     * collecting the results.
2548
     *
2549
     * @param callable $callable
2550
     *
2551
     * @return static
2552
     *                <p>(Immutable) Arrayy object with modified elements.</p>
2553
     */
2554 4
    public function map(callable $callable): self
2555
    {
2556 4
        return static::create(
2557 4
            \array_map($callable, $this->getArray()),
2558 4
            $this->iteratorClass,
2559 4
            false
2560
        );
2561
    }
2562
2563
    /**
2564
     * Check if all items in current array match a truth test.
2565
     *
2566
     * @param \Closure $closure
2567
     *
2568
     * @return bool
2569
     */
2570 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...
2571
    {
2572 15
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2573 2
            return false;
2574
        }
2575
2576 13
        foreach ($this->getGenerator() as $key => $value) {
2577 13
            $value = $closure($value, $key);
2578
2579 13
            if ($value === false) {
2580 13
                return false;
2581
            }
2582
        }
2583
2584 7
        return true;
2585
    }
2586
2587
    /**
2588
     * Check if any item in the current array matches a truth test.
2589
     *
2590
     * @param \Closure $closure
2591
     *
2592
     * @return bool
2593
     */
2594 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...
2595
    {
2596 14
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2597 2
            return false;
2598
        }
2599
2600 12
        foreach ($this->getGenerator() as $key => $value) {
2601 12
            $value = $closure($value, $key);
2602
2603 12
            if ($value === true) {
2604 12
                return true;
2605
            }
2606
        }
2607
2608 4
        return false;
2609
    }
2610
2611
    /**
2612
     * Get the max value from an array.
2613
     *
2614
     * @return mixed
2615
     */
2616 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...
2617
    {
2618 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2619 1
            return false;
2620
        }
2621
2622 9
        return \max($this->getArray());
2623
    }
2624
2625
    /**
2626
     * Merge the new $array into the current array.
2627
     *
2628
     * - keep key,value from the current array, also if the index is in the new $array
2629
     *
2630
     * @param array $array
2631
     * @param bool  $recursive
2632
     *
2633
     * @return static
2634
     *                <p>(Immutable)</p>
2635
     */
2636 25 View Code Duplication
    public function mergeAppendKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2637
    {
2638 25
        if ($recursive === true) {
2639 4
            $result = \array_replace_recursive($this->getArray(), $array);
2640
        } else {
2641 21
            $result = \array_replace($this->getArray(), $array);
2642
        }
2643
2644 25
        return static::create(
2645 25
            $result,
2646 25
            $this->iteratorClass,
2647 25
            false
2648
        );
2649
    }
2650
2651
    /**
2652
     * Merge the new $array into the current array.
2653
     *
2654
     * - replace duplicate assoc-keys from the current array with the key,values from the new $array
2655
     * - create new indexes
2656
     *
2657
     * @param array $array
2658
     * @param bool  $recursive
2659
     *
2660
     * @return static
2661
     *                <p>(Immutable)</p>
2662
     */
2663 16 View Code Duplication
    public function mergeAppendNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2664
    {
2665 16
        if ($recursive === true) {
2666 4
            $result = \array_merge_recursive($this->getArray(), $array);
2667
        } else {
2668 12
            $result = \array_merge($this->getArray(), $array);
2669
        }
2670
2671 16
        return static::create(
2672 16
            $result,
2673 16
            $this->iteratorClass,
2674 16
            false
2675
        );
2676
    }
2677
2678
    /**
2679
     * Merge the the current array into the $array.
2680
     *
2681
     * - use key,value from the new $array, also if the index is in the current array
2682
     *
2683
     * @param array $array
2684
     * @param bool  $recursive
2685
     *
2686
     * @return static
2687
     *                <p>(Immutable)</p>
2688
     */
2689 16 View Code Duplication
    public function mergePrependKeepIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2690
    {
2691 16
        if ($recursive === true) {
2692 4
            $result = \array_replace_recursive($array, $this->getArray());
2693
        } else {
2694 12
            $result = \array_replace($array, $this->getArray());
2695
        }
2696
2697 16
        return static::create(
2698 16
            $result,
2699 16
            $this->iteratorClass,
2700 16
            false
2701
        );
2702
    }
2703
2704
    /**
2705
     * Merge the current array into the new $array.
2706
     *
2707
     * - replace duplicate assoc-keys from new $array with the key,values from the current array
2708
     * - create new indexes
2709
     *
2710
     * @param array $array
2711
     * @param bool  $recursive
2712
     *
2713
     * @return static
2714
     *                <p>(Immutable)</p>
2715
     */
2716 17 View Code Duplication
    public function mergePrependNewIndex(array $array = [], bool $recursive = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2717
    {
2718 17
        if ($recursive === true) {
2719 4
            $result = \array_merge_recursive($array, $this->getArray());
2720
        } else {
2721 13
            $result = \array_merge($array, $this->getArray());
2722
        }
2723
2724 17
        return static::create(
2725 17
            $result,
2726 17
            $this->iteratorClass,
2727 17
            false
2728
        );
2729
    }
2730
2731
    /**
2732
     * @return ArrayyMeta|static
2733
     */
2734 14
    public static function meta()
2735
    {
2736 14
        return (new ArrayyMeta())->getMetaObject(static::class);
2737
    }
2738
2739
    /**
2740
     * Get the min value from an array.
2741
     *
2742
     * @return mixed
2743
     */
2744 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...
2745
    {
2746 10
        if (\count($this->getArray(), \COUNT_NORMAL) === 0) {
2747 1
            return false;
2748
        }
2749
2750 9
        return \min($this->getArray());
2751
    }
2752
2753
    /**
2754
     * Move an array element to a new index.
2755
     *
2756
     * cherry-picked from: http://stackoverflow.com/questions/12624153/move-an-array-element-to-a-new-index-in-php
2757
     *
2758
     * @param int|string $from
2759
     * @param int        $to
2760
     *
2761
     * @return static
2762
     *                <p>(Immutable)</p>
2763
     */
2764 1
    public function moveElement($from, $to): self
2765
    {
2766 1
        $array = $this->getArray();
2767
2768 1
        if (\is_int($from)) {
2769 1
            $tmp = \array_splice($array, $from, 1);
2770 1
            \array_splice($array, (int) $to, 0, $tmp);
2771 1
            $output = $array;
2772 1
        } elseif (\is_string($from)) {
2773 1
            $indexToMove = \array_search($from, \array_keys($array), true);
2774 1
            $itemToMove = $array[$from];
2775 1
            if ($indexToMove !== false) {
2776 1
                \array_splice($array, $indexToMove, 1);
2777
            }
2778 1
            $i = 0;
2779 1
            $output = [];
2780 1
            foreach ($array as $key => $item) {
2781 1
                if ($i === $to) {
2782 1
                    $output[$from] = $itemToMove;
2783
                }
2784 1
                $output[$key] = $item;
2785 1
                ++$i;
2786
            }
2787
        } else {
2788
            $output = [];
2789
        }
2790
2791 1
        return static::create(
2792 1
            $output,
2793 1
            $this->iteratorClass,
2794 1
            false
2795
        );
2796
    }
2797
2798
    /**
2799
     * Get a subset of the items from the given array.
2800
     *
2801
     * @param mixed[] $keys
2802
     *
2803
     * @return static
2804
     *                <p>(Immutable)</p>
2805
     */
2806
    public function only(array $keys): self
2807
    {
2808
        $array = $this->getArray();
2809
2810
        return static::create(
2811
            \array_intersect_key($array, \array_flip($keys)),
2812
            $this->iteratorClass,
2813
            false
2814
        );
2815
    }
2816
2817
    /**
2818
     * Pad array to the specified size with a given value.
2819
     *
2820
     * @param int   $size  <p>Size of the result array.</p>
2821
     * @param mixed $value <p>Empty value by default.</p>
2822
     *
2823
     * @return static
2824
     *                <p>(Immutable) Arrayy object padded to $size with $value.</p>
2825
     */
2826 4
    public function pad(int $size, $value): self
2827
    {
2828 4
        return static::create(
2829 4
            \array_pad($this->getArray(), $size, $value),
2830 4
            $this->iteratorClass,
2831 4
            false
2832
        );
2833
    }
2834
2835
    /**
2836
     * Pop a specified value off the end of the current array.
2837
     *
2838
     * @return mixed
2839
     *               <p>(Mutable) The popped element from the current array.</p>
2840
     */
2841 16
    public function pop()
2842
    {
2843 16
        $this->generatorToArray();
2844
2845 16
        return \array_pop($this->array);
2846
    }
2847
2848
    /**
2849
     * Prepend a (key) + value to the current array.
2850
     *
2851
     * @param mixed $value
2852
     * @param mixed $key
2853
     *
2854
     * @return static
2855
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
2856
     */
2857 8
    public function prepend($value, $key = null): self
2858
    {
2859 8
        $this->generatorToArray();
2860
2861 8
        if ($key === null) {
2862 8
            \array_unshift($this->array, $value);
2863
        } else {
2864
            /** @noinspection AdditionOperationOnArraysInspection */
2865 1
            $this->array = [$key => $value] + $this->array;
2866
        }
2867
2868 8
        return $this;
2869
    }
2870
2871
    /**
2872
     * Add a suffix to each key.
2873
     *
2874
     * @param mixed $suffix
2875
     *
2876
     * @return static
2877
     *                <p>(Immutable) Return an Arrayy object, with the prepended keys.</p>
2878
     */
2879 10 View Code Duplication
    public function prependToEachKey($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2880
    {
2881
        // init
2882 10
        $result = [];
2883
2884 10
        foreach ($this->getGenerator() as $key => $item) {
2885 9
            if ($item instanceof self) {
2886
                $result[$key] = $item->prependToEachKey($suffix);
2887 9
            } elseif (\is_array($item)) {
2888
                $result[$key] = self::create(
2889
                    $item,
2890
                    $this->iteratorClass,
2891
                    false
2892
                )->prependToEachKey($suffix)
2893
                    ->toArray();
2894
            } else {
2895 9
                $result[$key . $suffix] = $item;
2896
            }
2897
        }
2898
2899 10
        return self::create(
2900 10
            $result,
2901 10
            $this->iteratorClass,
2902 10
            false
2903
        );
2904
    }
2905
2906
    /**
2907
     * Add a suffix to each value.
2908
     *
2909
     * @param mixed $suffix
2910
     *
2911
     * @return static
2912
     *                <p>(Immutable) Return an Arrayy object, with the prepended values.</p>
2913
     */
2914 10 View Code Duplication
    public function prependToEachValue($suffix): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2915
    {
2916
        // init
2917 10
        $result = [];
2918
2919 10
        foreach ($this->getGenerator() as $key => $item) {
2920 9
            if ($item instanceof self) {
2921
                $result[$key] = $item->prependToEachValue($suffix);
2922 9
            } elseif (\is_array($item)) {
2923
                $result[$key] = self::create(
2924
                    $item,
2925
                    $this->iteratorClass,
2926
                    false
2927
                )->prependToEachValue($suffix)
2928
                    ->toArray();
2929 9
            } elseif (\is_object($item)) {
2930 1
                $result[$key] = $item;
2931
            } else {
2932 9
                $result[$key] = $item . $suffix;
2933
            }
2934
        }
2935
2936 10
        return self::create(
2937 10
            $result,
2938 10
            $this->iteratorClass,
2939 10
            false
2940
        );
2941
    }
2942
2943
    /**
2944
     * Push one or more values onto the end of array at once.
2945
     *
2946
     * @return static
2947
     *                <p>(Mutable) Return this Arrayy object, with pushed elements to the end of array.</p>
2948
     */
2949 4 View Code Duplication
    public function push(/* variadic arguments allowed */): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2950
    {
2951 4
        $this->generatorToArray();
2952
2953 4
        if (\func_num_args()) {
2954 4
            $args = \func_get_args();
2955 4
            \array_push(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_push() as the parameter $array expects a reference.
Loading history...
2956
        }
2957
2958 4
        return $this;
2959
    }
2960
2961
    /**
2962
     * Get a random value from the current array.
2963
     *
2964
     * @param int|null $number <p>How many values you will take?</p>
2965
     *
2966
     * @return static
2967
     *                <p>(Immutable)</p>
2968
     */
2969 18
    public function randomImmutable(int $number = null): self
2970
    {
2971 18
        $this->generatorToArray();
2972
2973 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...
2974 1
            return static::create(
2975 1
                [],
2976 1
                $this->iteratorClass,
2977 1
                false
2978
            );
2979
        }
2980
2981 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...
2982
            /** @noinspection NonSecureArrayRandUsageInspection */
2983 13
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
2984
2985 13
            return static::create(
2986 13
                $arrayRandValue,
2987 13
                $this->iteratorClass,
2988 13
                false
2989
            );
2990
        }
2991
2992 5
        $arrayTmp = $this->array;
2993
        /** @noinspection NonSecureShuffleUsageInspection */
2994 5
        \shuffle($arrayTmp);
2995
2996 5
        return static::create(
2997 5
            $arrayTmp,
2998 5
            $this->iteratorClass,
2999 5
            false
3000 5
        )->firstsImmutable($number);
3001
    }
3002
3003
    /**
3004
     * Pick a random key/index from the keys of this array.
3005
     *
3006
     * @throws \RangeException If array is empty
3007
     *
3008
     * @return mixed
3009
     *               <p>Get a key/index or null if there wasn't a key/index.</p>
3010
     */
3011 4
    public function randomKey()
3012
    {
3013 4
        $result = $this->randomKeys(1);
3014
3015 4
        if (!isset($result[0])) {
3016
            $result[0] = null;
3017
        }
3018
3019 4
        return $result[0];
3020
    }
3021
3022
    /**
3023
     * Pick a given number of random keys/indexes out of this array.
3024
     *
3025
     * @param int $number <p>The number of keys/indexes (should be <= \count($this->array))</p>
3026
     *
3027
     * @throws \RangeException If array is empty
3028
     *
3029
     * @return static
3030
     *                <p>(Immutable)</p>
3031
     */
3032 13
    public function randomKeys(int $number): self
3033
    {
3034 13
        $this->generatorToArray();
3035
3036 13
        $count = \count($this->array, \COUNT_NORMAL);
3037
3038 13
        if ($number === 0 || $number > $count) {
3039 2
            throw new \RangeException(
3040 2
                \sprintf(
3041 2
                    'Number of requested keys (%s) must be equal or lower than number of elements in this array (%s)',
3042 2
                    $number,
3043 2
                    $count
3044
                )
3045
            );
3046
        }
3047
3048 11
        $result = (array) \array_rand($this->array, $number);
3049
3050 11
        return static::create(
3051 11
            $result,
3052 11
            $this->iteratorClass,
3053 11
            false
3054
        );
3055
    }
3056
3057
    /**
3058
     * Get a random value from the current array.
3059
     *
3060
     * @param int|null $number <p>How many values you will take?</p>
3061
     *
3062
     * @return static
3063
     *                <p>(Mutable)</p>
3064
     */
3065 17
    public function randomMutable(int $number = null): self
3066
    {
3067 17
        $this->generatorToArray();
3068
3069 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...
3070
            return static::create(
3071
                [],
3072
                $this->iteratorClass,
3073
                false
3074
            );
3075
        }
3076
3077 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...
3078
            /** @noinspection NonSecureArrayRandUsageInspection */
3079 7
            $arrayRandValue = [$this->array[\array_rand($this->array)]];
3080 7
            $this->array = $arrayRandValue;
3081
3082 7
            return $this;
3083
        }
3084
3085
        /** @noinspection NonSecureShuffleUsageInspection */
3086 11
        \shuffle($this->array);
3087
3088 11
        return $this->firstsMutable($number);
3089
    }
3090
3091
    /**
3092
     * Pick a random value from the values of this array.
3093
     *
3094
     * @return mixed
3095
     *               <p>Get a random value or null if there wasn't a value.</p>
3096
     */
3097 4
    public function randomValue()
3098
    {
3099 4
        $result = $this->randomImmutable();
3100
3101 4
        if (!isset($result[0])) {
3102
            $result[0] = null;
3103
        }
3104
3105 4
        return $result[0];
3106
    }
3107
3108
    /**
3109
     * Pick a given number of random values out of this array.
3110
     *
3111
     * @param int $number
3112
     *
3113
     * @return static
3114
     *                <p>(Mutable)</p>
3115
     */
3116 7
    public function randomValues(int $number): self
3117
    {
3118 7
        return $this->randomMutable($number);
3119
    }
3120
3121
    /**
3122
     * Get a random value from an array, with the ability to skew the results.
3123
     *
3124
     * Example: randomWeighted(['foo' => 1, 'bar' => 2]) has a 66% chance of returning bar.
3125
     *
3126
     * @param array    $array
3127
     * @param int|null $number <p>How many values you will take?</p>
3128
     *
3129
     * @return static
3130
     *                <p>(Immutable)</p>
3131
     */
3132 9
    public function randomWeighted(array $array, int $number = null): self
3133
    {
3134
        // init
3135 9
        $options = [];
3136
3137 9
        foreach ($array as $option => $weight) {
3138 9
            if ($this->searchIndex($option) !== false) {
3139 9
                for ($i = 0; $i < $weight; ++$i) {
3140 1
                    $options[] = $option;
3141
                }
3142
            }
3143
        }
3144
3145 9
        return $this->mergeAppendKeepIndex($options)->randomImmutable($number);
3146
    }
3147
3148
    /**
3149
     * Reduce the current array via callable e.g. anonymous-function.
3150
     *
3151
     * @param callable $callable
3152
     * @param array    $init
3153
     *
3154
     * @return static
3155
     *                <p>(Immutable)</p>
3156
     */
3157 16
    public function reduce($callable, array $init = []): self
3158
    {
3159 16
        if ($this->generator) {
3160 1
            $result = $init;
3161
3162 1
            foreach ($this->getGenerator() as $value) {
3163 1
                $result = $callable($result, $value);
3164
            }
3165
3166 1
            return static::create(
3167 1
                $result,
3168 1
                $this->iteratorClass,
3169 1
                false
3170
            );
3171
        }
3172
3173 16
        $result = \array_reduce($this->array, $callable, $init);
3174
3175 16
        if ($result === null) {
3176
            $this->array = [];
3177
        } else {
3178 16
            $this->array = (array) $result;
3179
        }
3180
3181 16
        return static::create(
3182 16
            $this->array,
3183 16
            $this->iteratorClass,
3184 16
            false
3185
        );
3186
    }
3187
3188
    /**
3189
     * Create a numerically re-indexed Arrayy object.
3190
     *
3191
     * @return static
3192
     *                <p>(Mutable) Return this Arrayy object, with re-indexed array-elements.</p>
3193
     */
3194 9
    public function reindex(): self
3195
    {
3196 9
        $this->generatorToArray();
3197
3198 9
        $this->array = \array_values($this->array);
3199
3200 9
        return $this;
3201
    }
3202
3203
    /**
3204
     * Return all items that fail the truth test.
3205
     *
3206
     * @param \Closure $closure
3207
     *
3208
     * @return static
3209
     *                <p>(Immutable)</p>
3210
     */
3211 1 View Code Duplication
    public function reject(\Closure $closure): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3212
    {
3213
        // init
3214 1
        $filtered = [];
3215
3216 1
        foreach ($this->getGenerator() as $key => $value) {
3217 1
            if (!$closure($value, $key)) {
3218 1
                $filtered[$key] = $value;
3219
            }
3220
        }
3221
3222 1
        return static::create(
3223 1
            $filtered,
3224 1
            $this->iteratorClass,
3225 1
            false
3226
        );
3227
    }
3228
3229
    /**
3230
     * Remove a value from the current array (optional using dot-notation).
3231
     *
3232
     * @param mixed $key
3233
     *
3234
     * @return static
3235
     *                <p>(Mutable)</p>
3236
     */
3237 18
    public function remove($key): self
3238
    {
3239
        // recursive call
3240 18
        if (\is_array($key)) {
3241
            foreach ($key as $k) {
3242
                $this->internalRemove($k);
3243
            }
3244
3245
            return static::create(
3246
                $this->getArray(),
3247
                $this->iteratorClass,
3248
                false
3249
            );
3250
        }
3251
3252 18
        $this->internalRemove($key);
3253
3254 18
        return static::create(
3255 18
            $this->getArray(),
3256 18
            $this->iteratorClass,
3257 18
            false
3258
        );
3259
    }
3260
3261
    /**
3262
     * Remove the first value from the current array.
3263
     *
3264
     * @return static
3265
     *                <p>(Immutable)</p>
3266
     */
3267 7 View Code Duplication
    public function removeFirst(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3268
    {
3269 7
        $tmpArray = $this->getArray();
3270
3271 7
        \array_shift($tmpArray);
3272
3273 7
        return static::create(
3274 7
            $tmpArray,
3275 7
            $this->iteratorClass,
3276 7
            false
3277
        );
3278
    }
3279
3280
    /**
3281
     * Remove the last value from the current array.
3282
     *
3283
     * @return static
3284
     *                <p>(Immutable)</p>
3285
     */
3286 7 View Code Duplication
    public function removeLast(): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3287
    {
3288 7
        $tmpArray = $this->getArray();
3289
3290 7
        \array_pop($tmpArray);
3291
3292 7
        return static::create(
3293 7
            $tmpArray,
3294 7
            $this->iteratorClass,
3295 7
            false
3296
        );
3297
    }
3298
3299
    /**
3300
     * Removes a particular value from an array (numeric or associative).
3301
     *
3302
     * @param mixed $value
3303
     *
3304
     * @return static
3305
     *                <p>(Immutable)</p>
3306
     */
3307 7
    public function removeValue($value): self
3308
    {
3309 7
        $this->generatorToArray();
3310
3311
        // init
3312 7
        $isNumericArray = true;
3313
3314 7
        foreach ($this->getGenerator() as $key => $item) {
3315 6
            if ($item === $value) {
3316 6
                if (!\is_int($key)) {
3317
                    $isNumericArray = false;
3318
                }
3319 6
                unset($this->array[$key]);
3320
            }
3321
        }
3322
3323 7
        if ($isNumericArray) {
3324 7
            $this->array = \array_values($this->array);
3325
        }
3326
3327 7
        return static::create(
3328 7
            $this->array,
3329 7
            $this->iteratorClass,
3330 7
            false
3331
        );
3332
    }
3333
3334
    /**
3335
     * Generate array of repeated arrays.
3336
     *
3337
     * @param int $times <p>How many times has to be repeated.</p>
3338
     *
3339
     * @return static
3340
     *                <p>(Immutable)</p>
3341
     */
3342 1
    public function repeat($times): self
3343
    {
3344 1
        if ($times === 0) {
3345 1
            return new static();
3346
        }
3347
3348 1
        return static::create(
3349 1
            \array_fill(0, (int) $times, $this->getArray()),
3350 1
            $this->iteratorClass,
3351 1
            false
3352
        );
3353
    }
3354
3355
    /**
3356
     * Replace a key with a new key/value pair.
3357
     *
3358
     * @param mixed $replace
3359
     * @param mixed $key
3360
     * @param mixed $value
3361
     *
3362
     * @return static
3363
     *                <p>(Immutable)</p>
3364
     */
3365 2
    public function replace($replace, $key, $value): self
3366
    {
3367 2
        $that = clone $this;
3368
3369 2
        return $that->remove($replace)
3370 2
            ->set($key, $value);
3371
    }
3372
3373
    /**
3374
     * Create an array using the current array as values and the other array as keys.
3375
     *
3376
     * @param array $keys <p>An array of keys.</p>
3377
     *
3378
     * @return static
3379
     *                <p>(Immutable) Arrayy object with keys from the other array.</p>
3380
     */
3381 2
    public function replaceAllKeys(array $keys): self
3382
    {
3383 2
        return static::create(
3384 2
            \array_combine($keys, $this->getArray()),
3385 2
            $this->iteratorClass,
3386 2
            false
3387
        );
3388
    }
3389
3390
    /**
3391
     * Create an array using the current array as keys and the other array as values.
3392
     *
3393
     * @param array $array <p>An array o values.</p>
3394
     *
3395
     * @return static
3396
     *                <p>(Immutable) Arrayy object with values from the other array.</p>
3397
     */
3398 2
    public function replaceAllValues(array $array): self
3399
    {
3400 2
        return static::create(
3401 2
            \array_combine($this->array, $array),
3402 2
            $this->iteratorClass,
3403 2
            false
3404
        );
3405
    }
3406
3407
    /**
3408
     * Replace the keys in an array with another set.
3409
     *
3410
     * @param array $keys <p>An array of keys matching the array's size</p>
3411
     *
3412
     * @return static
3413
     *                <p>(Immutable)</p>
3414
     */
3415 1
    public function replaceKeys(array $keys): self
3416
    {
3417 1
        $values = \array_values($this->getArray());
3418 1
        $result = \array_combine($keys, $values);
3419
3420 1
        return static::create(
3421 1
            $result,
3422 1
            $this->iteratorClass,
3423 1
            false
3424
        );
3425
    }
3426
3427
    /**
3428
     * Replace the first matched value in an array.
3429
     *
3430
     * @param mixed $search      <p>The value to replace.</p>
3431
     * @param mixed $replacement <p>The value to replace.</p>
3432
     *
3433
     * @return static
3434
     *                <p>(Immutable)</p>
3435
     */
3436 3
    public function replaceOneValue($search, $replacement = ''): self
3437
    {
3438 3
        $array = $this->getArray();
3439 3
        $key = \array_search($search, $array, true);
3440
3441 3
        if ($key !== false) {
3442 3
            $array[$key] = $replacement;
3443
        }
3444
3445 3
        return static::create(
3446 3
            $array,
3447 3
            $this->iteratorClass,
3448 3
            false
3449
        );
3450
    }
3451
3452
    /**
3453
     * Replace values in the current array.
3454
     *
3455
     * @param mixed $search      <p>The value to replace.</p>
3456
     * @param mixed $replacement <p>What to replace it with.</p>
3457
     *
3458
     * @return static
3459
     *                <p>(Immutable)</p>
3460
     */
3461 1
    public function replaceValues($search, $replacement = ''): self
3462
    {
3463 1
        $array = $this->each(
3464
            static function ($value) use ($search, $replacement) {
3465 1
                return \str_replace($search, $replacement, $value);
3466 1
            }
3467
        );
3468
3469 1
        return $array;
3470
    }
3471
3472
    /**
3473
     * Get the last elements from index $from until the end of this array.
3474
     *
3475
     * @param int $from
3476
     *
3477
     * @return static
3478
     *                <p>(Immutable)</p>
3479
     */
3480 15 View Code Duplication
    public function rest(int $from = 1): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3481
    {
3482 15
        $tmpArray = $this->getArray();
3483
3484 15
        return static::create(
3485 15
            \array_splice($tmpArray, $from),
3486 15
            $this->iteratorClass,
3487 15
            false
3488
        );
3489
    }
3490
3491
    /**
3492
     * Return the array in the reverse order.
3493
     *
3494
     * @return static
3495
     *                <p>(Mutable) Return this Arrayy object.</p>
3496
     */
3497 8
    public function reverse(): self
3498
    {
3499 8
        $this->generatorToArray();
3500
3501 8
        $this->array = \array_reverse($this->array);
3502
3503 8
        return $this;
3504
    }
3505
3506
    /**
3507
     * Sort an array in reverse order.
3508
     *
3509
     * @param int $sort_flags [optional] <p>
3510
     *                        You may modify the behavior of the sort using the optional
3511
     *                        parameter sort_flags, for details
3512
     *                        see sort.
3513
     *                        </p>
3514
     *
3515
     * @return static
3516
     *                <p>(Mutable) Return this Arrayy object.</p>
3517
     */
3518 4
    public function rsort(int $sort_flags = 0): self
3519
    {
3520 4
        $this->generatorToArray();
3521
3522 4
        \rsort($this->array, $sort_flags);
3523
3524 4
        return $this;
3525
    }
3526
3527
    /**
3528
     * Search for the first index of the current array via $value.
3529
     *
3530
     * @param mixed $value
3531
     *
3532
     * @return false|float|int|string
3533
     *                                <p>Will return <b>FALSE</b> if the value can't be found.</p>
3534
     */
3535 20
    public function searchIndex($value)
3536
    {
3537 20
        foreach ($this->getGenerator() as $keyFromArray => $valueFromArray) {
3538 19
            if ($value === $valueFromArray) {
3539 19
                return $keyFromArray;
3540
            }
3541
        }
3542
3543 11
        return false;
3544
    }
3545
3546
    /**
3547
     * Search for the value of the current array via $index.
3548
     *
3549
     * @param mixed $index
3550
     *
3551
     * @return static
3552
     *                <p>(Immutable) Will return a empty Arrayy if the value wasn't found.</p>
3553
     */
3554 9
    public function searchValue($index): self
3555
    {
3556 9
        $this->generatorToArray();
3557
3558
        // init
3559 9
        $return = [];
3560
3561 9
        if ($this->array === []) {
3562
            return static::create(
3563
                [],
3564
                $this->iteratorClass,
3565
                false
3566
            );
3567
        }
3568
3569
        // php cast "bool"-index into "int"-index
3570 9
        if ((bool) $index === $index) {
3571 1
            $index = (int) $index;
3572
        }
3573
3574 9
        if (\array_key_exists($index, $this->array) === true) {
3575 7
            $return = [$this->array[$index]];
3576
        }
3577
3578 9
        return static::create(
3579 9
            $return,
3580 9
            $this->iteratorClass,
3581 9
            false
3582
        );
3583
    }
3584
3585
    /**
3586
     * Set a value for the current array (optional using dot-notation).
3587
     *
3588
     * @param string $key   <p>The key to set.</p>
3589
     * @param mixed  $value <p>Its value.</p>
3590
     *
3591
     * @return static
3592
     *                <p>(Mutable)</p>
3593
     */
3594 18
    public function set($key, $value): self
3595
    {
3596 18
        $this->generatorToArray();
3597
3598 18
        $this->internalSet($key, $value);
3599
3600 18
        return $this;
3601
    }
3602
3603
    /**
3604
     * Get a value from a array and set it if it was not.
3605
     *
3606
     * WARNING: this method only set the value, if the $key is not already set
3607
     *
3608
     * @param mixed $key      <p>The key</p>
3609
     * @param mixed $fallback <p>The default value to set if it isn't.</p>
3610
     *
3611
     * @return mixed
3612
     *               <p>(Mutable)</p>
3613
     */
3614 11
    public function setAndGet($key, $fallback = null)
3615
    {
3616 11
        $this->generatorToArray();
3617
3618
        // If the key doesn't exist, set it.
3619 11
        if (!$this->has($key)) {
3620 4
            $this->array = $this->set($key, $fallback)->getArray();
3621
        }
3622
3623 11
        return $this->get($key);
3624
    }
3625
3626
    /**
3627
     * Shifts a specified value off the beginning of array.
3628
     *
3629
     * @return mixed
3630
     *               <p>(Mutable) A shifted element from the current array.</p>
3631
     */
3632 4
    public function shift()
3633
    {
3634 4
        $this->generatorToArray();
3635
3636 4
        return \array_shift($this->array);
3637
    }
3638
3639
    /**
3640
     * Shuffle the current array.
3641
     *
3642
     * @param bool  $secure <p>using a CSPRNG | @link https://paragonie.com/b/JvICXzh_jhLyt4y3</p>
3643
     * @param array $array  [optional]
3644
     *
3645
     * @return static
3646
     *                <p>(Immutable)</p>
3647
     */
3648 1
    public function shuffle(bool $secure = false, array $array = null): self
3649
    {
3650 1
        if ($array === null) {
3651 1
            $array = $this->getArray();
3652
        }
3653
3654 1
        if ($secure !== true) {
3655
            /** @noinspection NonSecureShuffleUsageInspection */
3656 1
            \shuffle($array);
3657
        } else {
3658 1
            $size = \count($array, \COUNT_NORMAL);
3659 1
            $keys = \array_keys($array);
3660 1
            for ($i = $size - 1; $i > 0; --$i) {
3661
                try {
3662 1
                    $r = \random_int(0, $i);
3663
                } catch (\Exception $e) {
3664
                    /** @noinspection RandomApiMigrationInspection */
3665
                    $r = \mt_rand(0, $i);
3666
                }
3667 1
                if ($r !== $i) {
3668
                    $temp = $array[$keys[$r]];
3669
                    $array[$keys[$r]] = $array[$keys[$i]];
3670
                    $array[$keys[$i]] = $temp;
3671
                }
3672
            }
3673
3674
            // reset indices
3675 1
            $array = \array_values($array);
3676
        }
3677
3678 1
        foreach ($array as $key => $value) {
3679
            // check if recursive is needed
3680 1
            if (\is_array($value) === true) {
3681 1
                $array[$key] = $this->shuffle($secure, $value);
3682
            }
3683
        }
3684
3685 1
        return static::create(
3686 1
            $array,
3687 1
            $this->iteratorClass,
3688 1
            false
3689
        );
3690
    }
3691
3692
    /**
3693
     * Checks whether array has exactly $size items.
3694
     *
3695
     * @param int $size
3696
     *
3697
     * @return bool
3698
     */
3699 1 View Code Duplication
    public function sizeIs(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3700
    {
3701
        // init
3702 1
        $itemsTempCount = 0;
3703
3704 1
        foreach ($this->getGenerator() as $key => $value) {
3705 1
            ++$itemsTempCount;
3706 1
            if ($itemsTempCount > $size) {
3707 1
                return false;
3708
            }
3709
        }
3710
3711 1
        return $itemsTempCount === $size;
3712
    }
3713
3714
    /**
3715
     * Checks whether array has less than $size items.
3716
     *
3717
     * @param int $size
3718
     *
3719
     * @return bool
3720
     */
3721 1 View Code Duplication
    public function sizeIsLessThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3722
    {
3723
        // init
3724 1
        $itemsTempCount = 0;
3725
3726 1
        foreach ($this->getGenerator() as $key => $value) {
3727 1
            ++$itemsTempCount;
3728 1
            if ($itemsTempCount > $size) {
3729 1
                return false;
3730
            }
3731
        }
3732
3733 1
        return $itemsTempCount < $size;
3734
    }
3735
3736
    /**
3737
     * Checks whether array has more than $size items.
3738
     *
3739
     * @param int $size
3740
     *
3741
     * @return bool
3742
     */
3743 1 View Code Duplication
    public function sizeIsGreaterThan(int $size): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
3744
    {
3745
        // init
3746 1
        $itemsTempCount = 0;
3747
3748 1
        foreach ($this->getGenerator() as $key => $value) {
3749 1
            ++$itemsTempCount;
3750 1
            if ($itemsTempCount > $size) {
3751 1
                return true;
3752
            }
3753
        }
3754
3755 1
        return $itemsTempCount > $size;
3756
    }
3757
3758
    /**
3759
     * Checks whether array has between $fromSize to $toSize items. $toSize can be
3760
     * smaller than $fromSize.
3761
     *
3762
     * @param int $fromSize
3763
     * @param int $toSize
3764
     *
3765
     * @return bool
3766
     */
3767 1
    public function sizeIsBetween(int $fromSize, int $toSize): bool
3768
    {
3769 1
        if ($fromSize > $toSize) {
3770 1
            $tmp = $toSize;
3771 1
            $toSize = $fromSize;
3772 1
            $fromSize = $tmp;
3773
        }
3774
3775
        // init
3776 1
        $itemsTempCount = 0;
3777
3778 1
        foreach ($this->getGenerator() as $key => $value) {
3779 1
            ++$itemsTempCount;
3780 1
            if ($itemsTempCount > $toSize) {
3781 1
                return false;
3782
            }
3783
        }
3784
3785 1
        return $fromSize < $itemsTempCount && $itemsTempCount < $toSize;
3786
    }
3787
3788
    /**
3789
     * Count the values from the current array.
3790
     *
3791
     * alias: for "Arrayy->count()"
3792
     *
3793
     * @param int $mode
3794
     *
3795
     * @return int
3796
     */
3797 20
    public function size(int $mode = \COUNT_NORMAL): int
3798
    {
3799 20
        return $this->count($mode);
3800
    }
3801
3802
    /**
3803
     * Counts all elements in an array, or something in an object.
3804
     *
3805
     * <p>
3806
     * For objects, if you have SPL installed, you can hook into count() by implementing interface {@see Countable}.
3807
     * The interface has exactly one method, {@see Countable::count()}, which returns the return value for the count()
3808
     * function. Please see the {@see Array} section of the manual for a detailed explanation of how arrays are
3809
     * implemented and used in PHP.
3810
     * </p>
3811
     *
3812
     * @return int
3813
     *             <p>
3814
     *             The number of elements in var, which is
3815
     *             typically an array, since anything else will have one
3816
     *             element.
3817
     *             </p>
3818
     *             <p>
3819
     *             If var is not an array or an object with
3820
     *             implemented Countable interface,
3821
     *             1 will be returned.
3822
     *             There is one exception, if var is &null;,
3823
     *             0 will be returned.
3824
     *             </p>
3825
     *             <p>
3826
     *             Caution: count may return 0 for a variable that isn't set,
3827
     *             but it may also return 0 for a variable that has been initialized with an
3828
     *             empty array. Use isset to test if a variable is set.
3829
     *             </p>
3830
     */
3831 10
    public function sizeRecursive(): int
3832
    {
3833 10
        return \count($this->getArray(), \COUNT_RECURSIVE);
3834
    }
3835
3836
    /**
3837
     * Extract a slice of the array.
3838
     *
3839
     * @param int      $offset       <p>Slice begin index.</p>
3840
     * @param int|null $length       <p>Length of the slice.</p>
3841
     * @param bool     $preserveKeys <p>Whether array keys are preserved or no.</p>
3842
     *
3843
     * @return static
3844
     *                <p>A slice of the original array with length $length.</p>
3845
     */
3846 4
    public function slice(int $offset, int $length = null, bool $preserveKeys = false): self
3847
    {
3848 4
        return static::create(
3849 4
            \array_slice(
3850 4
                $this->getArray(),
3851 4
                $offset,
3852 4
                $length,
3853 4
                $preserveKeys
3854
            ),
3855 4
            $this->iteratorClass,
3856 4
            false
3857
        );
3858
    }
3859
3860
    /**
3861
     * Sort the current array and optional you can keep the keys.
3862
     *
3863
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3864
     * @param int        $strategy  <p>sort_flags => use e.g.: <strong>SORT_REGULAR</strong> (default) or
3865
     *                              <strong>SORT_NATURAL</strong></p>
3866
     * @param bool       $keepKeys
3867
     *
3868
     * @return static
3869
     *                <p>(Mutable) Return this Arrayy object.</p>
3870
     */
3871 20
    public function sort($direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
3872
    {
3873 20
        $this->generatorToArray();
3874
3875 20
        return $this->sorting(
3876 20
            $this->array,
3877 20
            $direction,
3878 20
            $strategy,
3879 20
            $keepKeys
3880
        );
3881
    }
3882
3883
    /**
3884
     * Sort the current array by key.
3885
     *
3886
     * @see http://php.net/manual/en/function.ksort.php
3887
     * @see http://php.net/manual/en/function.krsort.php
3888
     *
3889
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3890
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3891
     *                              <strong>SORT_NATURAL</strong></p>
3892
     *
3893
     * @return static
3894
     *                <p>(Mutable) Return this Arrayy object.</p>
3895
     */
3896 18
    public function sortKeys($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3897
    {
3898 18
        $this->generatorToArray();
3899
3900 18
        $this->sorterKeys($this->array, $direction, $strategy);
3901
3902 18
        return $this;
3903
    }
3904
3905
    /**
3906
     * Sort the current array by value.
3907
     *
3908
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3909
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3910
     *                              <strong>SORT_NATURAL</strong></p>
3911
     *
3912
     * @return static
3913
     *                <p>(Mutable)</p>
3914
     */
3915 1
    public function sortValueKeepIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3916
    {
3917 1
        return $this->sort($direction, $strategy, true);
3918
    }
3919
3920
    /**
3921
     * Sort the current array by value.
3922
     *
3923
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3924
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3925
     *                              <strong>SORT_NATURAL</strong></p>
3926
     *
3927
     * @return static
3928
     *                <p>(Mutable)</p>
3929
     */
3930 1
    public function sortValueNewIndex($direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3931
    {
3932 1
        return $this->sort($direction, $strategy, false);
3933
    }
3934
3935
    /**
3936
     * Sort a array by value, by a closure or by a property.
3937
     *
3938
     * - If the sorter is null, the array is sorted naturally.
3939
     * - Associative (string) keys will be maintained, but numeric keys will be re-indexed.
3940
     *
3941
     * @param callable|string|null $sorter
3942
     * @param int|string           $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
3943
     * @param int                  $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
3944
     *                                        <strong>SORT_NATURAL</strong></p>
3945
     *
3946
     * @return static
3947
     *                <p>(Immutable)</p>
3948
     */
3949 1
    public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
3950
    {
3951 1
        $array = $this->getArray();
3952 1
        $direction = $this->getDirection($direction);
3953
3954
        // Transform all values into their results.
3955 1
        if ($sorter) {
3956 1
            $arrayy = static::create(
3957 1
                $array,
3958 1
                $this->iteratorClass,
3959 1
                false
3960
            );
3961
3962 1
            $results = $arrayy->each(
3963
                function ($value) use ($sorter) {
3964 1
                    if (\is_callable($sorter)) {
3965 1
                        return $sorter($value);
3966
                    }
3967
3968 1
                    return $this->get($sorter, null, $this->getArray());
3969 1
                }
3970
            );
3971
3972 1
            $results = $results->getArray();
3973
        } else {
3974 1
            $results = $array;
3975
        }
3976
3977
        // Sort by the results and replace by original values
3978 1
        \array_multisort($results, $direction, $strategy, $array);
3979
3980 1
        return static::create(
3981 1
            $array,
3982 1
            $this->iteratorClass,
3983 1
            false
3984
        );
3985
    }
3986
3987
    /**
3988
     * Split an array in the given amount of pieces.
3989
     *
3990
     * @param int  $numberOfPieces
3991
     * @param bool $keepKeys
3992
     *
3993
     * @return static
3994
     *                <p>(Immutable)</p>
3995
     */
3996 1
    public function split(int $numberOfPieces = 2, bool $keepKeys = false): self
3997
    {
3998 1
        $this->generatorToArray();
3999
4000 1
        $arrayCount = \count($this->array, \COUNT_NORMAL);
4001
4002 1
        if ($arrayCount === 0) {
4003 1
            $result = [];
4004
        } else {
4005 1
            $splitSize = (int) \ceil($arrayCount / $numberOfPieces);
4006 1
            $result = \array_chunk($this->array, $splitSize, $keepKeys);
4007
        }
4008
4009 1
        return static::create(
4010 1
            $result,
4011 1
            $this->iteratorClass,
4012 1
            false
4013
        );
4014
    }
4015
4016
    /**
4017
     * Stripe all empty items.
4018
     *
4019
     * @return static
4020
     *                <p>(Immutable)</p>
4021
     */
4022 1
    public function stripEmpty(): self
4023
    {
4024 1
        return $this->filter(
4025
            static function ($item) {
4026 1
                if ($item === null) {
4027 1
                    return false;
4028
                }
4029
4030 1
                return (bool) \trim((string) $item);
4031 1
            }
4032
        );
4033
    }
4034
4035
    /**
4036
     * Swap two values between positions by key.
4037
     *
4038
     * @param int|string $swapA <p>a key in the array</p>
4039
     * @param int|string $swapB <p>a key in the array</p>
4040
     *
4041
     * @return static
4042
     *                <p>(Immutable)</p>
4043
     */
4044 1
    public function swap($swapA, $swapB): self
4045
    {
4046 1
        $array = $this->getArray();
4047
4048 1
        list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
4049
4050 1
        return static::create(
4051 1
            $array,
4052 1
            $this->iteratorClass,
4053 1
            false
4054
        );
4055
    }
4056
4057
    /**
4058
     * alias: for "Arrayy->getArray()"
4059
     *
4060
     * @see Arrayy::getArray()
4061
     */
4062 198
    public function toArray()
4063
    {
4064 198
        return $this->getArray();
4065
    }
4066
4067
    /**
4068
     * Convert the current array to JSON.
4069
     *
4070
     * @param int $options [optional] <p>e.g. JSON_PRETTY_PRINT</p>
4071
     * @param int $depth   [optional] <p>Set the maximum depth. Must be greater than zero.</p>
4072
     *
4073
     * @return string
4074
     */
4075 6
    public function toJson(int $options = 0, int $depth = 512): string
4076
    {
4077
        /** @noinspection PhpComposerExtensionStubsInspection */
4078 6
        $return = \json_encode($this->getArray(), $options, $depth);
4079 6
        if ($return === false) {
4080
            return '';
4081
        }
4082
4083 6
        return $return;
4084
    }
4085
4086
    /**
4087
     * Implodes array to a string with specified separator.
4088
     *
4089
     * @param string $separator [optional] <p>The element's separator.</p>
4090
     *
4091
     * @return string
4092
     *                <p>The string representation of array, separated by ",".</p>
4093
     */
4094 19
    public function toString(string $separator = ','): string
4095
    {
4096 19
        return $this->implode($separator);
4097
    }
4098
4099
    /**
4100
     * Return a duplicate free copy of the current array.
4101
     *
4102
     * @return static
4103
     *                <p>(Mutable)</p>
4104
     */
4105 12
    public function unique(): self
4106
    {
4107
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4108
4109 12
        $this->array = $this->reduce(
4110
            static function ($resultArray, $value) {
4111 11
                if (!\in_array($value, $resultArray, true)) {
4112 11
                    $resultArray[] = $value;
4113
                }
4114
4115 11
                return $resultArray;
4116 12
            },
4117 12
            []
4118 12
        )->getArray();
4119 12
        $this->generator = null;
4120
4121 12
        return $this;
4122
    }
4123
4124
    /**
4125
     * Return a duplicate free copy of the current array. (with the old keys)
4126
     *
4127
     * @return static
4128
     *                <p>(Mutable)</p>
4129
     */
4130 11
    public function uniqueKeepIndex(): self
4131
    {
4132
        // INFO: \array_unique() can't handle e.g. "stdClass"-values in an array
4133
4134
        // init
4135 11
        $array = $this->getArray();
4136
4137 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...
4138 11
            \array_keys($array),
4139
            static function ($resultArray, $key) use ($array) {
4140 10
                if (!\in_array($array[$key], $resultArray, true)) {
4141 10
                    $resultArray[$key] = $array[$key];
4142
                }
4143
4144 10
                return $resultArray;
4145 11
            },
4146 11
            []
4147
        );
4148 11
        $this->generator = null;
4149
4150 11
        if ($this->array === null) {
4151
            $this->array = [];
4152
        } else {
4153 11
            $this->array = (array) $this->array;
4154
        }
4155
4156 11
        return $this;
4157
    }
4158
4159
    /**
4160
     * @param bool $unique
4161
     *
4162
     * @return static
4163
     *                <p>(Immutable)</p>
4164
     */
4165 14
    public function reduce_dimension(bool $unique = true): self
4166
    {
4167
        // init
4168 14
        $result = [[]];
4169
4170 14
        foreach ($this->array as $val) {
4171 12
            if (\is_array($val)) {
4172 5
                $result[] = (new self($val))->reduce_dimension($unique)->getArray();
4173
            } else {
4174 12
                $result[] = [$val];
4175
            }
4176
        }
4177 14
        $result = \array_merge(...$result);
4178
4179 14
        $resultArrayy = new self($result);
4180
4181 14
        return $unique ? $resultArrayy->unique() : $resultArrayy;
4182
    }
4183
4184
    /**
4185
     * alias: for "Arrayy->unique()"
4186
     *
4187
     * @return static
4188
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
4189
     *
4190
     * @see Arrayy::unique()
4191
     */
4192 10
    public function uniqueNewIndex(): self
4193
    {
4194 10
        return $this->unique();
4195
    }
4196
4197
    /**
4198
     * Prepends one or more values to the beginning of array at once.
4199
     *
4200
     * @return static
4201
     *                <p>(Mutable) Return this Arrayy object, with prepended elements to the beginning of array.</p>
4202
     */
4203 4 View Code Duplication
    public function unshift(/* variadic arguments allowed */): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
4204
    {
4205 4
        $this->generatorToArray();
4206
4207 4
        if (\func_num_args()) {
4208 4
            $args = \func_get_args();
4209 4
            \array_unshift(...[&$this->array], ...$args);
0 ignored issues
show
Bug introduced by
array(&$this->array) cannot be passed to array_unshift() as the parameter $array expects a reference.
Loading history...
4210
        }
4211
4212 4
        return $this;
4213
    }
4214
4215
    /**
4216
     * Get all values from a array.
4217
     *
4218
     * @return static
4219
     *                <p>(Immutable)</p>
4220
     */
4221 2
    public function values(): self
4222
    {
4223 2
        return static::create(
4224
            function () {
4225
                /** @noinspection YieldFromCanBeUsedInspection */
4226 2
                foreach ($this->getGenerator() as $value) {
4227 2
                    yield $value;
4228
                }
4229 2
            },
4230 2
            $this->iteratorClass,
4231 2
            false
4232
        );
4233
    }
4234
4235
    /**
4236
     * Apply the given function to every element in the array, discarding the results.
4237
     *
4238
     * @param callable $callable
4239
     * @param bool     $recursive <p>Whether array will be walked recursively or no</p>
4240
     *
4241
     * @return static
4242
     *                <p>(Mutable) Return this Arrayy object, with modified elements.</p>
4243
     */
4244 37
    public function walk($callable, bool $recursive = false): self
4245
    {
4246 37
        $this->generatorToArray();
4247
4248 37
        if ($recursive === true) {
4249 32
            \array_walk_recursive($this->array, $callable);
4250
        } else {
4251 18
            \array_walk($this->array, $callable);
4252
        }
4253
4254 37
        return $this;
4255
    }
4256
4257
    /**
4258
     * Convert an array into a object.
4259
     *
4260
     * @param array $array PHP array
4261
     *
4262
     * @return \stdClass
4263
     */
4264 4
    protected static function arrayToObject(array $array = []): \stdClass
4265
    {
4266
        // init
4267 4
        $object = new \stdClass();
4268
4269 4
        if (\count($array, \COUNT_NORMAL) <= 0) {
4270 1
            return $object;
4271
        }
4272
4273 3
        foreach ($array as $name => $value) {
4274 3
            if (\is_array($value)) {
4275 1
                $object->{$name} = self::arrayToObject($value);
4276
            } else {
4277 3
                $object->{$name} = $value;
4278
            }
4279
        }
4280
4281 3
        return $object;
4282
    }
4283
4284
    /**
4285
     * @param array|\Generator|null $input        <p>
4286
     *                                            An array containing keys to return.
4287
     *                                            </p>
4288
     * @param mixed                 $search_value [optional] <p>
4289
     *                                            If specified, then only keys containing these values are returned.
4290
     *                                            </p>
4291
     * @param bool                  $strict       [optional] <p>
4292
     *                                            Determines if strict comparison (===) should be used during the
4293
     *                                            search.
4294
     *                                            </p>
4295
     *
4296
     * @return array
4297
     *               <p>an array of all the keys in input</p>
4298
     */
4299 10
    protected function array_keys_recursive(
4300
        $input = null,
4301
        $search_value = null,
4302
        bool $strict = true
4303
    ): array {
4304
        // init
4305 10
        $keys = [];
4306 10
        $keysTmp = [[]]; // the inner empty array covers cases when no loops were made
4307
4308 10
        if ($input === null) {
4309
            $input = $this->getGenerator();
4310
        }
4311
4312 10
        foreach ($input as $key => $value) {
4313
            if (
4314 10
                $search_value === null
4315
                ||
4316
                (
4317
                    \is_array($search_value) === true
4318
                    &&
4319 10
                    \in_array($key, $search_value, $strict)
4320
                )
4321
            ) {
4322 10
                $keys[] = $key;
4323
            }
4324
4325
            // check if recursive is needed
4326 10
            if (\is_array($value) === true) {
4327 10
                $keysTmp[] = $this->array_keys_recursive($value);
4328
            }
4329
        }
4330
4331 10
        return \array_merge($keys, ...$keysTmp);
4332
    }
4333
4334
    /**
4335
     * @param mixed      $path
4336
     * @param callable   $callable
4337
     * @param array|null $currentOffset
4338
     */
4339 4
    protected function callAtPath($path, $callable, &$currentOffset = null)
4340
    {
4341 4
        $this->generatorToArray();
4342
4343 4
        if ($currentOffset === null) {
4344 4
            $currentOffset = &$this->array;
4345
        }
4346
4347 4
        $explodedPath = \explode($this->pathSeparator, $path);
4348 4
        if ($explodedPath === false) {
4349
            return;
4350
        }
4351
4352 4
        $nextPath = \array_shift($explodedPath);
4353
4354 4
        if (!isset($currentOffset[$nextPath])) {
4355
            return;
4356
        }
4357
4358 4
        if (!empty($explodedPath)) {
4359 1
            $this->callAtPath(
4360 1
                \implode($this->pathSeparator, $explodedPath),
4361 1
                $callable,
4362 1
                $currentOffset[$nextPath]
4363
            );
4364
        } else {
4365 4
            $callable($currentOffset[$nextPath]);
4366
        }
4367 4
    }
4368
4369
    /**
4370
     * create a fallback for array
4371
     *
4372
     * 1. use the current array, if it's a array
4373
     * 2. fallback to empty array, if there is nothing
4374
     * 3. call "getArray()" on object, if there is a "Arrayy"-object
4375
     * 4. call "createFromObject()" on object, if there is a "\Traversable"-object
4376
     * 5. call "__toArray()" on object, if the method exists
4377
     * 6. cast a string or object with "__toString()" into an array
4378
     * 7. throw a "InvalidArgumentException"-Exception
4379
     *
4380
     * @param mixed $data
4381
     *
4382
     * @throws \InvalidArgumentException
4383
     *
4384
     * @return array
4385
     */
4386 940
    protected function fallbackForArray(&$data): array
4387
    {
4388 940
        if (\is_array($data)) {
4389 937
            return $data;
4390
        }
4391
4392 41
        if (!$data) {
4393 6
            return [];
4394
        }
4395
4396 40
        $isObject = \is_object($data);
4397
4398 40
        if ($isObject && $data instanceof self) {
4399 1
            return $data->getArray();
4400
        }
4401
4402 39
        if ($isObject && $data instanceof \ArrayObject) {
4403
            return $data->getArrayCopy();
4404
        }
4405
4406 39
        if ($isObject && $data instanceof \Generator) {
4407
            return static::createFromGeneratorImmutable($data)->getArray();
4408
        }
4409
4410 39
        if ($isObject && $data instanceof \Traversable) {
4411
            return static::createFromObject($data)->getArray();
4412
        }
4413
4414 39
        if (\is_callable($data)) {
4415 32
            $this->generator = new ArrayyRewindableGenerator($data);
4416
4417 32
            return [];
4418
        }
4419
4420 9
        if ($isObject && \method_exists($data, '__toArray')) {
4421
            return (array) $data->__toArray();
4422
        }
4423
4424
        if (
4425 9
            \is_string($data)
4426
            ||
4427 9
            ($isObject && \method_exists($data, '__toString'))
4428
        ) {
4429 7
            return [(string) $data];
4430
        }
4431
4432 2
        throw new \InvalidArgumentException(
4433 2
            'Passed value should be a array'
4434
        );
4435
    }
4436
4437
    /**
4438
     * Get correct PHP constant for direction.
4439
     *
4440
     * @param int|string $direction
4441
     *
4442
     * @return int
4443
     */
4444 39
    protected function getDirection($direction): int
4445
    {
4446 39
        if (\is_string($direction)) {
4447 10
            $direction = \strtolower($direction);
4448
4449 10
            if ($direction === 'desc') {
4450 2
                $direction = \SORT_DESC;
4451
            } else {
4452 8
                $direction = \SORT_ASC;
4453
            }
4454
        }
4455
4456
        if (
4457 39
            $direction !== \SORT_DESC
4458
            &&
4459 39
            $direction !== \SORT_ASC
4460
        ) {
4461
            $direction = \SORT_ASC;
4462
        }
4463
4464 39
        return $direction;
4465
    }
4466
4467
    /**
4468
     * @param mixed               $glue
4469
     * @param array|static|string $pieces
4470
     * @param bool                $useKeys
4471
     *
4472
     * @return string
4473
     */
4474 35
    protected function implode_recursive($glue = '', $pieces = [], bool $useKeys = false): string
4475
    {
4476 35
        if ($pieces instanceof self) {
4477 1
            $pieces = $pieces->getArray();
4478
        }
4479
4480 35
        if (\is_array($pieces)) {
4481 35
            $pieces_count = \count($pieces, \COUNT_NORMAL);
4482 35
            $pieces_count_not_zero = $pieces_count > 0;
4483
4484 35
            return \implode(
4485 35
                $glue,
4486 35
                \array_map(
4487 35
                    [$this, 'implode_recursive'],
4488 35
                    \array_fill(0, ($pieces_count_not_zero ? $pieces_count : 1), $glue),
4489 35
                    ($useKeys === true && $pieces_count_not_zero ? $this->array_keys_recursive($pieces) : $pieces)
4490
                )
4491
            );
4492
        }
4493
4494 35
        return (string) $pieces;
4495
    }
4496
4497
    /**
4498
     * @param mixed                 $needle   <p>
4499
     *                                        The searched value.
4500
     *                                        </p>
4501
     *                                        <p>
4502
     *                                        If needle is a string, the comparison is done
4503
     *                                        in a case-sensitive manner.
4504
     *                                        </p>
4505
     * @param array|\Generator|null $haystack <p>
4506
     *                                        The array.
4507
     *                                        </p>
4508
     * @param bool                  $strict   [optional] <p>
4509
     *                                        If the third parameter strict is set to true
4510
     *                                        then the in_array function will also check the
4511
     *                                        types of the
4512
     *                                        needle in the haystack.
4513
     *                                        </p>
4514
     *
4515
     * @return bool
4516
     *              <p>true if needle is found in the array, false otherwise</p>
4517
     */
4518 44
    protected function in_array_recursive($needle, $haystack = null, $strict = true): bool
4519
    {
4520 44
        if ($haystack === null) {
4521
            $haystack = $this->getGenerator();
4522
        }
4523
4524 44
        foreach ($haystack as $item) {
4525 36
            if (\is_array($item) === true) {
4526 8
                $returnTmp = $this->in_array_recursive($needle, $item, $strict);
4527
            } else {
4528 36
                $returnTmp = ($strict === true ? $item === $needle : $item == $needle);
4529
            }
4530
4531 36
            if ($returnTmp === true) {
4532 36
                return true;
4533
            }
4534
        }
4535
4536 18
        return false;
4537
    }
4538
4539
    /**
4540
     * @param mixed $value
4541
     */
4542
    protected function internalGetArray(&$value)
4543
    {
4544
        if ($value instanceof self) {
4545
            $valueTmp = $value->getArray();
4546
            if (\count($valueTmp, \COUNT_NORMAL) === 0) {
4547
                $value = [];
4548
            } else {
4549
                /** @noinspection PhpUnusedLocalVariableInspection */
4550
                $value = &$valueTmp;
4551
            }
4552
        }
4553
4554
        /** @noinspection PhpComposerExtensionStubsInspection */
4555
        /** @noinspection NotOptimalIfConditionsInspection */
4556
        if (
4557
            \class_exists('JsonSerializable')
4558
            &&
4559
            $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...
4560
        ) {
4561
4562
            /** @noinspection PhpUnusedLocalVariableInspection */
4563
            $value = &$value->jsonSerialize();
4564
        }
4565
    }
4566
4567
    /**
4568
     * Internal mechanics of remove method.
4569
     *
4570
     * @param mixed $key
4571
     *
4572
     * @return bool
4573
     */
4574 18
    protected function internalRemove($key): bool
4575
    {
4576 18
        $this->generatorToArray();
4577
4578
        if (
4579 18
            $this->pathSeparator
4580
            &&
4581 18
            \is_string($key)
4582
            &&
4583 18
            \strpos($key, $this->pathSeparator) !== false
4584
        ) {
4585
            $path = \explode($this->pathSeparator, (string) $key);
4586
4587
            if ($path !== false) {
4588
                // crawl though the keys
4589
                while (\count($path, \COUNT_NORMAL) > 1) {
4590
                    $key = \array_shift($path);
4591
4592
                    if (!$this->has($key)) {
4593
                        return false;
4594
                    }
4595
4596
                    $this->array = &$this->array[$key];
4597
                }
4598
4599
                $key = \array_shift($path);
4600
            }
4601
        }
4602
4603 18
        unset($this->array[$key]);
4604
4605 18
        return true;
4606
    }
4607
4608
    /**
4609
     * Internal mechanic of set method.
4610
     *
4611
     * @param int|string|null $key
4612
     * @param mixed           $value
4613
     * @param bool            $checkProperties
4614
     *
4615
     * @return bool
4616
     */
4617 823
    protected function internalSet($key, $value, $checkProperties = true): bool
4618
    {
4619
        if (
4620 823
            $checkProperties === true
4621
            &&
4622 823
            $this->properties !== []
4623
        ) {
4624 13
            if (isset($this->properties[$key]) === false) {
4625
                throw new \InvalidArgumentException('The key ' . $key . ' does not exists as @property in the class (' . \get_class($this) . ').');
4626
            }
4627
4628 13
            $this->properties[$key]->checkType($value);
4629
        }
4630
4631 822
        if ($key === null) {
4632
            return false;
4633
        }
4634
4635 822
        $this->generatorToArray();
4636
4637 822
        $array = &$this->array;
4638
4639
        if (
4640 822
            $this->pathSeparator
4641
            &&
4642 822
            \is_string($key)
4643
            &&
4644 822
            \strpos($key, $this->pathSeparator) !== false
4645
        ) {
4646 3
            $path = \explode($this->pathSeparator, (string) $key);
4647
4648 3
            if ($path !== false) {
4649
                // crawl through the keys
4650 3
                while (\count($path, \COUNT_NORMAL) > 1) {
4651 3
                    $key = \array_shift($path);
4652
4653 3
                    $array = &$array[$key];
4654
                }
4655
4656 3
                $key = \array_shift($path);
4657
            }
4658
        }
4659
4660 822
        $array[$key] = $value;
4661
4662 822
        return true;
4663
    }
4664
4665
    /**
4666
     * Convert a object into an array.
4667
     *
4668
     * @param object $object
4669
     *
4670
     * @return mixed
4671
     */
4672 5
    protected static function objectToArray($object)
4673
    {
4674 5
        if (!\is_object($object)) {
4675 4
            return $object;
4676
        }
4677
4678 5
        if (\is_object($object)) {
4679 5
            $object = \get_object_vars($object);
4680
        }
4681
4682 5
        return \array_map(['static', 'objectToArray'], $object);
4683
    }
4684
4685
    /**
4686
     * sorting keys
4687
     *
4688
     * @param array      $elements
4689
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4690
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4691
     *                              <strong>SORT_NATURAL</strong></p>
4692
     *
4693
     * @return static
4694
     *                <p>(Mutable) Return this Arrayy object.</p>
4695
     */
4696 18
    protected function sorterKeys(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR): self
4697
    {
4698 18
        $direction = $this->getDirection($direction);
4699
4700
        switch ($direction) {
4701 18
            case 'desc':
4702 18
            case \SORT_DESC:
4703 6
                \krsort($elements, $strategy);
4704
4705 6
                break;
4706 13
            case 'asc':
4707 13
            case \SORT_ASC:
4708
            default:
4709 13
                \ksort($elements, $strategy);
4710
        }
4711
4712 18
        return $this;
4713
    }
4714
4715
    /**
4716
     * @param array      $elements  <p>Warning: used as reference</p>
4717
     * @param int|string $direction <p>use <strong>SORT_ASC</strong> (default) or <strong>SORT_DESC</strong></p>
4718
     * @param int        $strategy  <p>use e.g.: <strong>SORT_REGULAR</strong> (default) or
4719
     *                              <strong>SORT_NATURAL</strong></p>
4720
     * @param bool       $keepKeys
4721
     *
4722
     * @return static
4723
     *                <p>(Mutable) Return this Arrayy object.</p>
4724
     */
4725 20
    protected function sorting(array &$elements, $direction = \SORT_ASC, int $strategy = \SORT_REGULAR, bool $keepKeys = false): self
4726
    {
4727 20
        $direction = $this->getDirection($direction);
4728
4729 20
        if (!$strategy) {
4730 20
            $strategy = \SORT_REGULAR;
4731
        }
4732
4733
        switch ($direction) {
4734 20
            case 'desc':
4735 20
            case \SORT_DESC:
4736 9
                if ($keepKeys) {
4737 5
                    \arsort($elements, $strategy);
4738
                } else {
4739 4
                    \rsort($elements, $strategy);
4740
                }
4741
4742 9
                break;
4743 11
            case 'asc':
4744 11
            case \SORT_ASC:
4745
            default:
4746 11
                if ($keepKeys) {
4747 4
                    \asort($elements, $strategy);
4748
                } else {
4749 7
                    \sort($elements, $strategy);
4750
                }
4751
        }
4752
4753 20
        return $this;
4754
    }
4755
4756
    /**
4757
     * @return bool
4758
     */
4759 862
    private function generatorToArray(): bool
4760
    {
4761 862
        if ($this->generator) {
4762 1
            $this->array = $this->getArray();
4763 1
            $this->generator = null;
4764
4765 1
            return true;
4766
        }
4767
4768 862
        return false;
4769
    }
4770
4771
    /**
4772
     * @return Property[]
4773
     */
4774 15
    private function getPropertiesFromPhpDoc(): array
4775
    {
4776 15
        static $PROPERTY_CACHE = [];
4777 15
        $cacheKey = 'Class::' . static::class;
4778
4779 15
        if (isset($PROPERTY_CACHE[$cacheKey])) {
4780 14
            return $PROPERTY_CACHE[$cacheKey];
4781
        }
4782
4783
        // init
4784 2
        $properties = [];
4785
4786 2
        $reflector = new \ReflectionClass($this);
4787 2
        $factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
4788 2
        $docComment = $reflector->getDocComment();
4789 2
        if ($docComment) {
4790 2
            $docblock = $factory->create($docComment);
4791 2
            foreach ($docblock->getTagsByName('property') as $tag) {
4792
                /* @var $tag \phpDocumentor\Reflection\DocBlock\Tags\Property */
4793 2
                $properties[$tag->getVariableName()] = Property::fromPhpDocumentorProperty($tag);
4794
            }
4795
        }
4796
4797 2
        return $PROPERTY_CACHE[$cacheKey] = $properties;
4798
    }
4799
}
4800