Arrays::leastOccurring()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
/**
4
 * This file is part of the alphaz Framework.
5
 *
6
 * @author Muhammad Umer Farooq <[email protected]>
7
 *
8
 * @link https://github.com/alphazframework/framework
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 * @since 1.0.0
13
 *
14
 * @license MIT
15
 */
16
17
namespace alphaz\Data;
18
19
use alphaz\Data\Contracts\ArraysContract;
20
21
class Arrays implements ArraysContract
22
{
23
    /**
24
     * Determine is given value is really, array?.
25
     *
26
     * @param mixed $value Value to be checked.
27
     *
28
     * @since 1.0.0
29
     *
30
     * @return bool
31
     */
32
    public static function isReallyArray($value)
33
    {
34
        return is_array($value) && count($value) !== 0;
35
    }
36
37
    /**
38
     * Determine array is (sqquential).
39
     *
40
     * @param array $array Value to be check.
41
     *
42
     * @since 1.0.0
43
     *
44
     * @return bool
45
     */
46
    public static function isSequential($array): bool
47
    {
48
        return is_array($array) && !self::isAssoc($array) && !self::isMulti($array);
49
    }
50
51
    /**
52
     * Determine array is Assoc.
53
     *
54
     * @param array $value Value to be check.
55
     *
56
     * @since 1.0.0
57
     *
58
     * @return bool
59
     */
60
    public static function isAssoc(array $array): bool
61
    {
62
        return array_keys($array) !== range(0, count($array) - 1) && !self::isMulti($array);
63
    }
64
65
    /**
66
     * Determine array is multi-dimensional.
67
     *
68
     * @param array $value Value to be check.
69
     *
70
     * @since 1.0.0
71
     *
72
     * @return bool
73
     */
74
    public static function isMulti(array $array): bool
75
    {
76
        sort($array, SORT_REGULAR);
77
78
        return isset($array[0]) && is_array($array[0]);
79
    }
80
81
    /**
82
     * Get type of array.
83
     *
84
     * @param array $array The array to work on.
85
     *
86
     * @since 1.0.0
87
     *
88
     * @return mixed
89
     */
90
    public static function getType(array $array)
91
    {
92
        if (self::isReallyArray($array)) {
93
            if (self::isSequential($array)) {
94
                $type = 'indexes';
95
            } elseif (self::isAssoc($array)) {
96
                $type = 'assoc';
97
            } elseif (self::isMulti($array)) {
98
                $type = 'multi';
99
            }
100
101
            return isset($type) ? $type : false;
102
        }
103
104
        throw new \InvalidArgumentException('The given array should not be empty', 500);
105
    }
106
107
    /**
108
     * Add an element to an array using "dot" notation if it doesn't exist.
109
     *
110
     * @param array  $array Array to be evaluated
111
     * @param string $key   Key
112
     * @param string $opr   Notation like 'dot'
113
     * @param
114
     *
115
     * @since 1.0.0
116
     *
117
     * @return array
118
     */
119
    public static function add($array, $key, $value, $opr = null)
120
    {
121
        if (!self::has($array, $key, $opr)) {
122
            self::set($array, $key, $value, $opr);
123
        }
124
125
        return $array;
126
    }
127
128
    /**
129
     * Set an array item to a given value using "operator" notation.
130
     *
131
     * @param array  $array Array to be evaluated.
132
     * @param string $key   Key
133
     * @param mixed  $value Value
134
     * @param string $opr   Notation like 'dot'
135
     *
136
     * @since 1.0.0
137
     *
138
     * @return array
139
     */
140
    public static function set(&$array, $key = null, $value = null, $opr = null)
141
    {
142
        if (null === $value) {
143
            return $array;
144
        }
145
        if (null === $key) {
146
            return $array = $value;
147
        }
148
        if (null === $opr) {
149
            return $array[$key] = $value;
150
        }
151
152
        $keys = explode($opr, $key);
153
        foreach ($keys as $key) {
154
            if (!isset($array[$key]) || !is_array($array[$key])) {
155
                $array[$key] = [];
156
            }
157
158
            $array = &$array[$key];
159
        }
160
161
        $array = $value;
162
163
        return $array;
164
    }
165
166
    /**
167
     * Get an item from an array using "operator" notation.
168
     *
169
     * @param array        $array Default array.
170
     * @param array|string $keys  Keys to search.
171
     * @param string       $opr   Operator notaiton.
172
     *
173
     * @since 1.0.0
174
     *
175
     * @return array
176
     */
177
    public static function get($array, $key = null, $default = null, $opr = null)
178
    {
179
        if (!self::isReallyArray($array)) {
180
            return $default;
181
        }
182
        if (null === $key) {
183
            return $array;
184
        }
185
        if (array_key_exists($key, $array)) {
186
            return $array[$key];
187
        }
188
189
        if (null !== $opr) {
190
            if (strpos($key, $opr) === false) {
191
                return $array[$key] ?? $default;
192
            }
193
194
            foreach (explode($opr, $key) as $k) {
195
                if (self::isReallyArray($array) && array_key_exists($k, $array)) {
196
                    $array = $array[$k];
197
                } else {
198
                    return $default;
199
                }
200
            }
201
202
            return $array;
203
        }
204
205
        return $default;
206
    }
207
208
    /**
209
     * Determine if an item or items exist in an array using 'Operator' notation.
210
     *
211
     * @param array        $array Default array.
212
     * @param array|string $keys  Keys to search.
213
     * @param string       $opr   Operator notaiton.
214
     *
215
     * @since 1.0.0
216
     *
217
     * @return bool
218
     */
219
    public static function has($array, $keys = null, $opr = null)
220
    {
221
        $keys = (array) $keys;
222
223
        if (count($keys) === 0) {
224
            return false;
225
        }
226
227
        foreach ($keys as $key) {
228
            $get = self::get($array, $key, null, $opr);
229
            if (self::isReallyArray($get) || $get === null) {
230
                return false;
231
            }
232
        }
233
234
        return true;
235
    }
236
237
    /**
238
     * Convert a multi-dimensional array to assoc.
239
     *
240
     * @param array $array Value to be converted.
241
     *
242
     * @since 1.0.0
243
     *
244
     * @return array
245
     */
246
    public static function multiToAssoc(array $arrays)
247
    {
248
        $results = [];
249
        foreach ($arrays as $key => $value) {
250
            if (self::isReallyArray($value) === true) {
251
                $results = array_merge($results, self::multiToAssoc($value));
252
            } else {
253
                $results[$key] = $value;
254
            }
255
        }
256
257
        return $results;
258
    }
259
260
    /**
261
     * Converted a multi-dimensional associative array with `dot`.
262
     *
263
     * @param array $arrays      Arrays.
264
     * @param bool  $assocOutput Switch to output assoc arrays.
265
     *
266
     * @since 1.0.0
267
     *
268
     * @return array
269
     */
270
    public static function dot(array $arrays, bool $assocOutput = false)
271
    {
272
        return self::multiToAssocWithSpecificOpr($arrays, '.', $assocOutput);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::multiToAsso...ays, '.', $assocOutput) returns the type array which is incompatible with the return type mandated by alphaz\Data\Contracts\ArraysContract::dot() of boolean.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
273
    }
274
275
    /**
276
     * Converted a multi-dimensional associative array with `operator`.
277
     *
278
     * @param array  $arrays      Arrays.
279
     * @param string $opr         Operator.
280
     * @param bool   $assocOutput Switch to output assoc arrays.
281
     * @param string $_key        the previous key of the object
282
     *
283
     * @since 1.0.0
284
     *
285
     * @return array
286
     */
287
    public static function multiToAssocWithSpecificOpr(array $arrays, $opr = null, bool $assocOutput = false, string $_key = null)
288
    {
289
        $results = [];
290
        foreach ($arrays as $key => $value) {
291
            $key = ($assocOutput === true ? $_key.$key : $key);
292
            if (self::isReallyArray($value) === true) {
293
                $assocKey = ($assocOutput === true ? $opr : $key.$opr);
294
                $results = array_merge(
295
                    $results,
296
                    self::multiToAssocWithSpecificOpr(
297
                        $value,
298
                        $assocKey,
299
                        $assocOutput,
300
                        $key.$opr
301
                    )
302
                );
303
            } elseif ($assocOutput === true) {
304
                // gets pull off the last $opr that is added
305
                $regex = '/(\\'.$opr.'$)/';
306
                $_key = preg_replace($regex, '', $_key);
307
                $results[$_key][] = $value;
308
            } else {
309
                $key = $opr.$key;
310
                $key = ($key[0] === $opr ? substr($key, 1) : $key);
311
                $results[$key] = $value;
312
            }
313
        }
314
315
        return $results;
316
    }
317
318
    /**
319
     * Push an item onto the beginning of an array.
320
     *
321
     * @param array $array Dafult array.
322
     * @param mixed $value Value to be append.
323
     * @param mixed $key   Key.
324
     *
325
     * @since 1.0.0
326
     *
327
     * @return array
328
     */
329
    public static function prepend($array, $value, $key = null)
330
    {
331
        if ($key === null) {
332
            array_unshift($array, $value);
333
        } else {
334
            $array = array_merge([$key => $value], $array);
335
        }
336
337
        return $array;
338
    }
339
340
    /**
341
     * Push an item onto the end of an array.
342
     *
343
     * @param array $array Dafult array, where value to append.
344
     * @param mixed $value Value to be append.
345
     * @param mixed $key   Key.
346
     *
347
     * @since 1.0.0
348
     *
349
     * @return array
350
     */
351
    public static function append($array, $value, $key = null)
352
    {
353
        return array_merge($array, [$key => $value]);
354
    }
355
356
    /**
357
     * Get the unique elements from array.
358
     *
359
     * @param array $array Array ot evaulated
360
     *
361
     * @since 1.0.0
362
     *
363
     * @return array
364
     */
365
    public static function unique($array)
366
    {
367
        $results = [];
368
        if (self::isMulti($array)) {
369
            $array = self::multiToAssoc($array);
370
            foreach ($array as $key => $value) {
371
                $results[] = $value;
372
            }
373
374
            return array_unique($array);
375
        }
376
377
        return array_unique($array);
378
    }
379
380
    /**
381
     * Get a subset of the items from the given array.
382
     *
383
     * @param array $array Array to be evaulated.
384
     * @param mixed $keys  Keys
385
     *
386
     * @since 1.0.0
387
     *
388
     * @return array
389
     */
390
    public static function subSetOfArray(array $array, $keys)
391
    {
392
        return array_intersect_key($array, array_flip((array) $keys));
393
    }
394
395
    /**
396
     * Remove one or many array items from a given array using "operator" notation.
397
     *
398
     * @param array        $array Array to be evaluated.
399
     * @param array|string $keys  Keys.
400
     *
401
     * Note: Adapted from laravel\framework.
402
     *
403
     * @see https://github.com/laravel/framework/blob/5.8/LICENSE.md
404
     * @since 1.0.0
405
     *
406
     * @return mixed
407
     */
408
    public static function forget(&$array, $keys, $opr = null)
409
    {
410
        $original = &$array;
411
412
        $keys = (array) $keys;
413
414
        if (count($keys) === 0) {
415
            return false;
416
        }
417
        foreach ($keys as $key) {
418
            // if the exact key exists in the top-level, remove it
419
            if (array_key_exists($key, $array)) {
420
                unset($array[$key]);
421
                continue;
422
            }
423
424
            if (null !== $opr) {
425
                $parts = explode($opr, $key);
426
427
                // clean up before each pass
428
                $array = &$original;
429
430
                while (count($parts) > 1) {
431
                    $part = array_shift($parts);
432
433
                    if (isset($array[$part]) && is_array($array[$part])) {
434
                        $array = &$array[$part];
435
                    } else {
436
                        continue 2;
437
                    }
438
                }
439
440
                unset($array[array_shift($parts)]);
441
            }
442
        }
443
    }
444
445
    /**
446
     * Get all of the given array except for a specified array of keys.
447
     *
448
     * @param array        $array Default array.
449
     * @param array|string $keys  Keys
450
     *
451
     * @since 1.0.0
452
     *
453
     * @return array
454
     */
455
    public static function except($array, $keys)
456
    {
457
        self::forget($array, $keys);
458
459
        return $array;
460
    }
461
462
    /**
463
     * Get a value from the array, and remove it.
464
     *
465
     * @param array  $array   Default Array.
466
     * @param string $key     Keys
467
     * @param mixed  $default Default value
468
     *
469
     * @since 1.0.0
470
     *
471
     * @return mixed
472
     */
473
    public static function pull(&$array, $key, $default = null, $opr = null)
474
    {
475
        $value = self::get($array, $key, $default, $opr);
476
        self::forget($array, $key);
477
478
        return $value;
479
    }
480
481
    /**
482
     * Changes the case of all keys in an array.
483
     *
484
     * @param array $array The array to work on.
485
     * @param int   $case  Either CASE_UPPER or CASE_LOWER (default).
486
     *
487
     * @since 1.0.0
488
     *
489
     * @return array
490
     */
491
    public static function arrayChangeCaseKey($array, $case = CASE_LOWER)
492
    {
493
        return array_map(function ($item) use ($case) {
494
            if (is_array($item)) {
495
                $item = self::arrayChangeCaseKey($item, $case);
496
            }
497
498
            return $item;
499
        }, array_change_key_case($array, $case));
500
    }
501
502
    /**
503
     * Changes the case of all values in an array.
504
     *
505
     * @param array  $array The array to work on.
506
     * @param string $case  Either CASE_UPPER or CASE_LOWER (default).
507
     *
508
     * @since 1.0.0
509
     *
510
     * @return array
511
     */
512
    public static function arrayChangeCaseValue($array, $case = CASE_LOWER)
513
    {
514
        $results = [];
515
        foreach ($array as $key => $value) {
516
            if (self::isReallyArray($value)) {
517
                $array[$key] = array_merge($results, self::arrayChangeCaseValue($value, $case));
518
            } else {
519
                $array[$key] = ($case == CASE_UPPER) ? strtoupper($array[$key]) : strtolower($array[$key]);
520
            }
521
        }
522
523
        return $array;
524
    }
525
526
    /**
527
     * Remove duplicate values from array.
528
     *
529
     * @param array      $array The array to work on.
530
     * @param string|int $key   Key that need to evaulate.
531
     *
532
     * @since 1.0.0
533
     *
534
     * @return mixed
535
     */
536
    public static function removeDuplicates(array $array, $key = '')
537
    {
538
        if (!self::isReallyArray($array)) {
539
            return false;
540
        }
541
        if (self::isSequential($array) || self::isAssoc($array)) {
542
            return array_unique($array);
543
        }
544
        if (self::isMulti($array) && empty($key)) {
545
            return false;
546
        }
547
        $dataSet = [];
548
        $i = 0;
549
        $keys = [];
550
        foreach ($array as $k) {
551
            if (in_array($k[$key], $keys)) {
552
                continue;
553
            } else {
554
                $keys[$i] = $k[$key];
555
                $dataSet[$i] = $k;
556
            }
557
558
            $i++;
559
        }
560
561
        return $dataSet;
562
    }
563
564
    /**
565
     * Get the most|least occurring value from array.
566
     *
567
     * @param string     $type  The type most|least
568
     * @param array      $array The array to work on.
569
     * @param string|int $key   Key that need to evaulate.
570
     *
571
     * @since 1.0.0
572
     *
573
     * @return array
574
     */
575
    private static function mostOrLeastOccurring(string $type, array $array, $key = '')
576
    {
577
        $occurring = [];
578
579
        if (self::isAssoc($array) || self::isMulti($array)) {
580
            $values = array_count_values(array_column($array, $key));
581
        } else {
582
            $values = array_count_values($array);
583
        }
584
585
        $tmp = $type === 'most' ? current($values) : current($values);
586
        unset($values[$tmp]);
587
        foreach ($values as $key => $value) {
588
            if ($type === 'most') {
589
                if ($tmp <= $value) {
590
                    $tmp = $key;
591
                    $occurring[] = $key;
592
                }
593
            } elseif ($type === 'least') {
594
                if ($tmp > $value) {
595
                    $tmp = $key;
596
                    $occurring[] = $key;
597
                }
598
            }
599
        }
600
601
        return $occurring;
602
    }
603
604
    /**
605
     * Get the most occurring value from array.
606
     *
607
     * @param array      $array The array to work on.
608
     * @param string|int $key   Key that need to evaulate.
609
     *
610
     * @since 1.0.0
611
     *
612
     * @return array
613
     */
614
    public static function mostOccurring(array $array, $key = '')
615
    {
616
        return self::mostOrLeastOccurring('most', $array, $key);
617
    }
618
619
    /**
620
     * Get the least occurring value from array.
621
     *
622
     * @param array      $array The array to work on.
623
     * @param string|int $key   Key that need to evaulate.
624
     *
625
     * @since 1.0.0
626
     *
627
     * @return array
628
     */
629
    public static function leastOccurring(array $array, $key = '')
630
    {
631
        return self::mostOrLeastOccurring('least', $array, $key);
632
    }
633
634
    /**
635
     * Convert the array into a query string.
636
     *
637
     * @param array $array The array to work on.
638
     *
639
     * @since 1.0.0
640
     *
641
     * @return string
642
     */
643
    public static function query($array)
644
    {
645
        return http_build_query($array, null, '&', PHP_QUERY_RFC3986);
0 ignored issues
show
Bug introduced by
null of type null is incompatible with the type string expected by parameter $numeric_prefix of http_build_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

645
        return http_build_query($array, /** @scrutinizer ignore-type */ null, '&', PHP_QUERY_RFC3986);
Loading history...
646
    }
647
648
    /**
649
     * Filter the array using the given callback.
650
     *
651
     * @param array    $array    The array to work on.
652
     * @param callable $callback Callback function.
653
     *
654
     * @since 1.0.0
655
     *
656
     * @return array
657
     */
658
    public static function where(array $array, callable $callback)
659
    {
660
        return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
661
    }
662
663
    /**
664
     * Shuffle the given array for associative arrays, preserves key=>value pairs.
665
     * THIS METION WILL NOT WORKS WITH MULTIDIMESSIONAL ARRAY.
666
     *
667
     * @param array $array The array to work on.
668
     *
669
     * @since 1.0.0
670
     *
671
     * @return bool
672
     */
673
    public static function shuffle(array &$array)
674
    {
675
        $dataSet = [];
676
677
        $keys = array_keys($array);
678
679
        shuffle($keys);
680
681
        foreach ($keys as $key) {
682
            $dataSet[$key] = $array[$key];
683
        }
684
685
        $array = $dataSet;
686
687
        return true;
688
    }
689
690
    /**
691
     * Get one or a specified number of random values from an array.
692
     *
693
     * @param array    $array The array to work on.
694
     * @param int|null $i     Specifies how many entries should be picked.
695
     *
696
     * @since 1.0.0
697
     *
698
     * @return mixed
699
     */
700
    public static function random(array $array, int $i = null)
701
    {
702
        (int) $i = $i ?? 1;
703
        $countElement = count($array);
704
705
        if ($countElement < $i) {
706
            throw new \OutOfBoundsException("You requested {$i} items, but there are only {$countElement} items available.", 500);
707
        }
708
        if ($i === 0) {
709
            throw new \OutOfBoundsException('Second argument has to be between 1 and the number of elements in the array', 500);
710
        }
711
712
        $keys = array_rand($array, $i);
713
        $dataSet = [];
714
715
        foreach ((array) $keys as $key) {
716
            $dataSet[] = $array[$key];
717
        }
718
719
        return $dataSet;
720
    }
721
722
    /**
723
     * Get multiple values of same keys from multi-dimessional array.
724
     *
725
     * @param array $array The array to work on.
726
     * @param mixed $key   The specific key to search/get values.
727
     *
728
     * @since 1.0.0
729
     *
730
     * @return mixed
731
     */
732
    public static function pluck(array $array, $key)
733
    {
734
        if (self::isMulti($array)) {
735
            $dataSet = [];
736
            array_walk_recursive($array, function ($value, $k) use (&$dataSet, $key) {
737
                if ($k == $key) {
738
                    $dataSet[] = $value;
739
                }
740
            });
741
742
            return $dataSet;
743
        }
744
745
        throw new \InvalidArgumentException('The array given should be multi-dimensional array, '.self::getType($array).' given', 500);
0 ignored issues
show
Bug introduced by
Are you sure self::getType($array) of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

745
        throw new \InvalidArgumentException('The array given should be multi-dimensional array, './** @scrutinizer ignore-type */ self::getType($array).' given', 500);
Loading history...
746
    }
747
}
748