Completed
Push — master ( 2a86f1...70e886 )
by Lars
02:23 queued 10s
created

Arrayy::createFromGeneratorFunction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

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

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

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

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