Completed
Push — master ( 7cb054...584ba8 )
by Tim
44:11 queued 26:38
created

Arrays   D

Complexity

Total Complexity 74

Size/Duplication

Total Lines 1040
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 1040
rs 4.9551
wmc 74
lcom 1
cbo 1

How to fix   Complexity   

Complex Class

Complex classes like Arrays often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Arrays, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * Copyright (c) Ouzo contributors, http://ouzoframework.org
4
 * This file is made available under the MIT License (view the LICENSE file for more information).
5
 */
6
namespace Ouzo\Utilities;
7
8
use Exception;
9
use InvalidArgumentException;
10
11
/**
12
 * Class Arrays
13
 * @package Ouzo\Utilities
14
 */
15
class Arrays
16
{
17
    const TREAT_NULL_AS_VALUE = 1;
18
19
    /**
20
     * Returns true if every element in array satisfies the predicate.
21
     *
22
     * Example:
23
     * <code>
24
     * $array = array(1, 2);
25
     * $all = Arrays::all($array, function ($element) {
26
     *      return $element < 3;
27
     * });
28
     * </code>
29
     * Result:
30
     * <code>
31
     * true
32
     * </code>
33
     *
34
     * @param array $elements
35
     * @param callable $predicate
36
     * @return bool
37
     */
38
    public static function all(array $elements, $predicate)
39
    {
40
        foreach ($elements as $element) {
41
            if (!Functions::call($predicate, $element)) {
42
                return false;
43
            }
44
        }
45
        return true;
46
    }
47
48
    /**
49
     * This method creates associative array using key and value functions on array elements.
50
     *
51
     * Example:
52
     * <code>
53
     * $array = range(1, 2);
54
     * $map = Arrays::toMap($array, function ($elem) {
55
     *      return $elem * 10;
56
     * }, function ($elem) {
57
     *      return $elem + 1;
58
     * });
59
     * </code>
60
     * Result:
61
     * <code>
62
     * Array
63
     * (
64
     *      [10] => 2
65
     *      [20] => 3
66
     * )
67
     * </code>
68
     *
69
     * @param array $elements
70
     * @param callable $keyFunction
71
     * @param callable|null $valueFunction
72
     * @return array
73
     */
74
    public static function toMap(array $elements, $keyFunction, $valueFunction = null)
75
    {
76
        if ($valueFunction == null) {
77
            $valueFunction = Functions::identity();
78
        }
79
80
        $keys = array_map($keyFunction, $elements);
81
        $values = array_map($valueFunction, $elements);
82
        return empty($keys) ? array() : array_combine($keys, $values);
83
    }
84
85
    /**
86
     * Returns a new array that is a one-dimensional flattening of the given array.
87
     *
88
     * Example:
89
     * <code>
90
     * $array = array(
91
     *      'names' => array(
92
     *          'john',
93
     *          'peter',
94
     *          'bill'
95
     *      ),
96
     *      'products' => array(
97
     *          'cheese',
98
     *          array(
99
     *              'natural' => 'milk',
100
     *              'brie'
101
     *          )
102
     *      )
103
     * );
104
     * $flatten = Arrays::flatten($array);
105
     * </code>
106
     * Result:
107
     * <code>
108
     * Array
109
     * (
110
     *      [0] => john
111
     *      [1] => peter
112
     *      [2] => bill
113
     *      [3] => cheese
114
     *      [4] => milk
115
     *      [5] => brie
116
     * )
117
     * </code>
118
     *
119
     * @param array $array
120
     * @return array
121
     */
122
    public static function flatten(array $array)
123
    {
124
        $return = array();
125
        array_walk_recursive($array, function ($a) use (&$return) {
126
            $return[] = $a;
127
        });
128
        return $return;
129
    }
130
131
    /**
132
     * This method returns a key for the given value.
133
     *
134
     * Example:
135
     * <code>
136
     * $array = array(
137
     *      'k1' => 4,
138
     *      'k2' => 'd',
139
     *      'k3' => 0,
140
     *      9 => 'p'
141
     * );
142
     * $key = Arrays::findKeyByValue($array, 0);
143
     * </code>
144
     * Result:
145
     * <code>
146
     * k3
147
     * </code>
148
     *
149
     * @param array $elements
150
     * @param string $value
151
     * @return bool|int|string
152
     */
153
    public static function findKeyByValue(array $elements, $value)
154
    {
155
        if ($value === 0) {
156
            $value = '0';
157
        }
158
        foreach ($elements as $key => $item) {
159
            if ($item == $value) {
160
                return $key;
161
            }
162
        }
163
        return false;
164
    }
165
166
    /**
167
     * Returns true if at least one element in the array satisfies the predicate.
168
     *
169
     * Example:
170
     * <code>
171
     * $array = array('a', true, 'c');
172
     * $any = Arrays::any($array, function ($element) {
173
     *      return is_bool($element);
174
     * });
175
     * </code>
176
     * Result:
177
     * <code>
178
     * true
179
     * </code>
180
     *
181
     * @param array $elements
182
     * @param callable $predicate
183
     * @return bool
184
     */
185
    public static function any(array $elements, $predicate)
186
    {
187
        foreach ($elements as $element) {
188
            if (Functions::call($predicate, $element)) {
189
                return true;
190
            }
191
        }
192
        return false;
193
    }
194
195
    /**
196
     * This method returns the first value in the given array.
197
     *
198
     * Example:
199
     * <code>
200
     * $array = array('one', 'two' 'three');
201
     * $first = Arrays::first($array);
202
     * </code>
203
     * Result:
204
     * <code>one</code>
205
     *
206
     * @param array $elements
207
     * @return mixed
208
     * @throws InvalidArgumentException
209
     */
210
    public static function first(array $elements)
211
    {
212
        if (empty($elements)) {
213
            throw new InvalidArgumentException('empty array');
214
        }
215
        $keys = array_keys($elements);
216
        return $elements[$keys[0]];
217
    }
218
219
    /**
220
     * This method returns the last value in the given array.
221
     *
222
     * Example:
223
     * <code>
224
     * $array = array('a', 'b', 'c');
225
     * $last = Arrays::last($array);
226
     * </code>
227
     * Result:
228
     * <code>c</code>
229
     *
230
     * @param array $elements
231
     * @return mixed
232
     * @throws InvalidArgumentException
233
     */
234
    public static function last(array $elements)
235
    {
236
        if (empty($elements)) {
237
            throw new InvalidArgumentException('empty array');
238
        }
239
        return end($elements);
240
    }
241
242
    /**
243
     * This method returns the first value or null if array is empty.
244
     *
245
     * Example:
246
     * <code>
247
     * $array = array();
248
     * $return = Arrays::firstOrNull($array);
249
     * </code>
250
     * Result:
251
     * <code>null</code>
252
     *
253
     * @param array $elements
254
     * @return mixed|null
255
     */
256
    public static function firstOrNull(array $elements)
257
    {
258
        return empty($elements) ? null : self::first($elements);
259
    }
260
261
    /**
262
     * Returns the element for the given key or a default value otherwise.
263
     *
264
     * Example:
265
     * <code>
266
     * $array = array('id' => 1, 'name' => 'john');
267
     * $value = Arrays::getValue($array, 'name');
268
     * </code>
269
     * Result:
270
     * <code>john</code>
271
     *
272
     * Example:
273
     * <code>
274
     * $array = array('id' => 1, 'name' => 'john');
275
     * $value = Arrays::getValue($array, 'surname', '--not found--');
276
     * </code>
277
     * Result:
278
     * <code>--not found--</code>
279
     *
280
     * @param array $elements
281
     * @param string|int $key
282
     * @param mixed|null $default
283
     * @return mixed|null
284
     */
285
    public static function getValue(array $elements, $key, $default = null)
286
    {
287
        return isset($elements[$key]) ? $elements[$key] : $default;
288
    }
289
290
    /**
291
     * Returns an array containing only the given keys.
292
     *
293
     * Example:
294
     * <code>
295
     * $array = array('a' => 1, 'b' => 2, 'c' => 3);
296
     * $filtered = Arrays::filterByAllowedKeys($array, array('a', 'b'));
297
     * </code>
298
     * Result:
299
     * <code>
300
     * Array
301
     * (
302
     *      [a] => 1
303
     *      [b] => 2
304
     * )
305
     * </code>
306
     *
307
     * @param array $elements
308
     * @param array $allowedKeys
309
     * @return array
310
     */
311
    public static function filterByAllowedKeys(array $elements, array $allowedKeys)
312
    {
313
        return array_intersect_key($elements, array_flip($allowedKeys));
314
    }
315
316
    /**
317
     * Filters array by keys using the predicate.
318
     *
319
     * Example:
320
     * <code>
321
     * $array = array('a1' => 1, 'a2' => 2, 'c' => 3);
322
     * $filtered = Arrays::filterByKeys($array, function ($elem) {
323
     *      return $elem[0] == 'a';
324
     * });
325
     * </code>
326
     * Result:
327
     * <code>
328
     * Array
329
     * (
330
     *      [a1] => 1
331
     *      [b2] => 2
332
     * )
333
     * </code>
334
     *
335
     * @param array $elements
336
     * @param callable $predicate
337
     * @return array
338
     */
339
    public static function filterByKeys(array $elements, $predicate)
340
    {
341
        $allowedKeys = array_filter(array_keys($elements), $predicate);
342
        return self::filterByAllowedKeys($elements, $allowedKeys);
343
    }
344
345
    /**
346
     * Group elements in array by result of the given function. If $orderField is set grouped elements will be also sorted.
347
     *
348
     * Example:
349
     * <code>
350
     * $obj1 = new stdClass();
351
     * $obj1->name = 'a';
352
     * $obj1->description = '1';
353
     *
354
     * $obj2 = new stdClass();
355
     * $obj2->name = 'b';
356
     * $obj2->description = '2';
357
     *
358
     * $obj3 = new stdClass();
359
     * $obj3->name = 'b';
360
     * $obj3->description = '3';
361
     *
362
     * $array = array($obj1, $obj2, $obj3);
363
     * $grouped = Arrays::groupBy($array, Functions::extractField('name'));
364
     * </code>
365
     * Result:
366
     * <code>
367
     * Array
368
     * (
369
     *      [a] => Array
370
     *      (
371
     *          [0] => stdClass Object
372
     *          (
373
     *              [name] => a
374
     *              [description] => 1
375
     *          )
376
     *      )
377
     *      [b] => Array
378
     *      (
379
     *          [0] => stdClass Object
380
     *          (
381
     *              [name] => b
382
     *              [description] => 2
383
     *          )
384
     *          [1] => stdClass Object
385
     *          (
386
     *              [name] => b
387
     *              [description] => 3
388
     *          )
389
     *      )
390
     * )
391
     * </code>
392
     *
393
     * @param array $elements
394
     * @param callable $keyFunction
395
     * @param string|null $orderField
396
     * @return array
397
     */
398
    public static function groupBy(array $elements, $keyFunction, $orderField = null)
399
    {
400
        $map = array();
401
        if (!empty($orderField)) {
402
            $elements = self::orderBy($elements, $orderField);
403
        }
404
        foreach ($elements as $element) {
405
            $key = Functions::call($keyFunction, $element);
406
            $map[$key][] = $element;
407
        }
408
        return $map;
409
    }
410
411
    /**
412
     * This method sorts elements in array using order field.
413
     *
414
     * Example:
415
     * <code>
416
     * $obj1 = new stdClass();
417
     * $obj1->name = 'a';
418
     * $obj1->description = '1';
419
     *
420
     * $obj2 = new stdClass();
421
     * $obj2->name = 'c';
422
     * $obj2->description = '2';
423
     *
424
     * $obj3 = new stdClass();
425
     * $obj3->name = 'b';
426
     * $obj3->description = '3';
427
     *
428
     * $array = array($obj1, $obj2, $obj3);
429
     * $sorted = Arrays::orderBy($array, 'name');
430
     * </code>
431
     * Result:
432
     * <code>
433
     * Array
434
     * (
435
     *      [0] => stdClass Object
436
     *      (
437
     *          [name] => a
438
     *          [description] => 1
439
     *      )
440
     *      [1] => stdClass Object
441
     *      (
442
     *          [name] => b
443
     *          [description] => 3
444
     *      )
445
     *      [2] => stdClass Object
446
     *      (
447
     *          [name] => c
448
     *          [description] => 2
449
     *      )
450
     * )
451
     * </code>
452
     *
453
     * @param array $elements
454
     * @param string $orderField
455
     * @return array
456
     */
457
    public static function orderBy(array $elements, $orderField)
458
    {
459
        usort($elements, function ($a, $b) use ($orderField) {
460
            return $a->$orderField < $b->$orderField ? -1 : 1;
461
        });
462
        return $elements;
463
    }
464
465
    /**
466
     * This method maps array keys using the function.
467
     * Invokes the function for each key in the array. Creates a new array containing the keys returned by the function.
468
     *
469
     * Example:
470
     * <code>
471
     * $array = array(
472
     *      'k1' => 'v1',
473
     *      'k2' => 'v2',
474
     *      'k3' => 'v3'
475
     * );
476
     * $arrayWithNewKeys = Arrays::mapKeys($array, function ($key) {
477
     *      return 'new_' . $key;
478
     * });
479
     * </code>
480
     * Result:
481
     * <code>
482
     * Array
483
     * (
484
     *      [new_k1] => v1
485
     *      [new_k2] => v2
486
     *      [new_k3] => v3
487
     * )
488
     * </code>
489
     *
490
     * @param array $elements
491
     * @param callable $function
492
     * @return array
493
     */
494
    public static function mapKeys(array $elements, $function)
495
    {
496
        $newArray = array();
497
        foreach ($elements as $oldKey => $value) {
498
            $newKey = Functions::call($function, $oldKey);
499
            $newArray[$newKey] = $value;
500
        }
501
        return $newArray;
502
    }
503
504
    /**
505
     * This method maps array values using the function.
506
     * Invokes the function for each value in the array. Creates a new array containing the values returned by the function.
507
     *
508
     * Example:
509
     * <code>
510
     * $array = array('k1', 'k2', 'k3');
511
     * $result = Arrays::map($array, function ($value) {
512
     *      return 'new_' . $value;
513
     * });
514
     * </code>
515
     * Result:
516
     * <code>
517
     * Array
518
     * (
519
     *      [0] => new_k1
520
     *      [1] => new_k2
521
     *      [2] => new_k3
522
     * )
523
     * </code>
524
     *
525
     * @param array $elements
526
     * @param callable $function
527
     * @return array
528
     */
529
    public static function map(array $elements, $function)
530
    {
531
        return array_map($function, $elements);
532
    }
533
534
    /**
535
     * This method filters array using function. Result contains all elements for which function returns true.
536
     *
537
     * Example:
538
     * <code>
539
     * $array = array(1, 2, 3, 4);
540
     * $result = Arrays::filter($array, function ($value) {
541
     *      return $value > 2;
542
     * });
543
     * </code>
544
     * Result:
545
     * <code>
546
     * Array
547
     * (
548
     *      [2] => 3
549
     *      [3] => 4
550
     * )
551
     * </code>
552
     *
553
     * @param array $elements
554
     * @param callable $function
555
     * @return array
556
     */
557
    public static function filter(array $elements, $function)
558
    {
559
        return array_filter($elements, $function);
560
    }
561
562
    /**
563
     * This method filter array will remove all values that are blank.
564
     *
565
     * @param array $elements
566
     * @return array
567
     */
568
    public static function filterNotBlank(array $elements)
569
    {
570
        return array_filter($elements);
571
    }
572
573
    /**
574
     * Make array from element. Returns the given argument if it's already an array.
575
     *
576
     * Example:
577
     * <code>
578
     * $result = Arrays::toArray('test');
579
     * </code>
580
     * Result:
581
     * <code>
582
     * Array
583
     * (
584
     *      [0] => test
585
     * )
586
     * </code>
587
     *
588
     * @param mixed $element
589
     * @return array
590
     */
591
    public static function toArray($element)
592
    {
593
        return $element ? is_array($element) ? $element : array($element) : array();
594
    }
595
596
    /**
597
     * Returns a random element from the given array.
598
     *
599
     * Example:
600
     * <code>
601
     * $array = array('john', 'city', 'small');
602
     * $rand = Arrays::randElement($array);
603
     * </code>
604
     * Result: <i>rand element from array</i>
605
     *
606
     * @param array $elements
607
     * @return null
608
     */
609
    public static function randElement($elements)
610
    {
611
        return $elements ? $elements[array_rand($elements)] : null;
612
    }
613
614
    /**
615
     * Returns a new array with $keys as array keys and $values as array values.
616
     *
617
     * Example:
618
     * <code>
619
     * $keys = array('id', 'name', 'surname');
620
     * $values = array(1, 'john', 'smith');
621
     * $combined = Arrays::combine($keys, $values);
622
     * </code>
623
     * Result:
624
     * <code>
625
     * Array
626
     * (
627
     *      [id] => 1
628
     *      [name] => john
629
     *      [surname] => smith
630
     * )
631
     * </code>
632
     *
633
     * @param array $keys
634
     * @param array $values
635
     * @return array
636
     */
637
    public static function combine(array $keys, array $values)
638
    {
639
        if (!empty($keys) && !empty($values)) {
640
            return array_combine($keys, $values);
641
        }
642
        return array();
643
    }
644
645
    /**
646
     * Checks is key exists in an array.
647
     *
648
     * Example:
649
     * <code>
650
     * $array = array('id' => 1, 'name' => 'john');
651
     * $return = Arrays::keyExists($array, 'name');
652
     * </code>
653
     * Result:
654
     * <code>true</code>
655
     *
656
     * @param array $elements
657
     * @param string|int $key
658
     * @return bool
659
     */
660
    public static function keyExists(array $elements, $key)
661
    {
662
        return array_key_exists($key, $elements);
663
    }
664
665
    /**
666
     * Method to reduce an array elements to a string value.
667
     *
668
     * @param array $elements
669
     * @param callable $function
670
     * @return mixed
671
     */
672
    public static function reduce(array $elements, $function)
673
    {
674
        return array_reduce($elements, $function);
675
    }
676
677
    /**
678
     * Finds first element in array that is matched by function.
679
     * Returns null if element was not found.
680
     *
681
     * @param array $elements
682
     * @param callable $function
683
     * @return mixed
684
     */
685
    public static function find(array $elements, $function)
686
    {
687
        foreach ($elements as $element) {
688
            if ($function($element)) {
689
                return $element;
690
            }
691
        }
692
        return null;
693
    }
694
695
    /**
696
     * Computes the intersection of arrays.
697
     *
698
     * @param array $array1
699
     * @param array $array2
700
     * @return array
701
     */
702
    public static function intersect(array $array1, array $array2)
703
    {
704
        return array_intersect($array1, $array2);
705
    }
706
707
    /**
708
     * Setting nested value.
709
     *
710
     * Example:
711
     * <code>
712
     * $array = array();
713
     * Arrays::setNestedValue($array, array('1', '2', '3'), 'value');
714
     * </code>
715
     * Result:
716
     * <code>
717
     * Array
718
     * (
719
     *      [1] => Array
720
     *          (
721
     *              [2] => Array
722
     *                  (
723
     *                      [3] => value
724
     *                  )
725
     *          )
726
     * )
727
     * </code>
728
     *
729
     * @param array $array
730
     * @param array $keys
731
     * @param $value
732
     */
733
    public static function setNestedValue(array &$array, array $keys, $value)
734
    {
735
        $current = &$array;
736
        foreach ($keys as $key) {
737
            if (!isset($current[$key])) {
738
                $current[$key] = array();
739
            }
740
            $current = &$current[$key];
741
        }
742
        $current = $value;
743
    }
744
745
    /**
746
     * Returns a new array with is sorted using given comparator.
747
     * The comparator function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
748
     * To obtain comparator one may use <code>Comparator</code> class (for instance <code>Comparator::natural()</code> which yields ordering using comparison operators).
749
     *
750
     * Example:
751
     * <code>
752
     * class Foo
753
     * {
754
     *      private $value;
755
     *      function __construct($value)
756
     *      {
757
     *          $this->value = $value;
758
     *      }
759
     *      public function getValue()
760
     *      {
761
     *          return $this->value;
762
     *      }
763
     * }
764
     * $values = array(new Foo(1), new Foo(3), new Foo(2));
765
     * $sorted = Arrays::sort($values, Comparator::compareBy('getValue()'));
766
     * </code>
767
     * Result:
768
     * <code>
769
     * Array
770
     * (
771
     *      [0] =>  class Foo (1) {
772
     *                  private $value => int(1)
773
     *              }
774
     *      [1] =>  class Foo (1) {
775
     *                  private $value => int(2)
776
     *              }
777
     *      [2] =>  class Foo (1) {
778
     *                  private $value => int(3)
779
     *              }
780
     * )
781
     * </code>
782
     *
783
     * @param array $array
784
     * @param $comparator
785
     * @return array sorted according to the comparator
786
     * @throws InvalidArgumentException
787
     */
788
    public static function sort(array $array, $comparator)
789
    {
790
        usort($array, $comparator);
791
        return $array;
792
    }
793
794
    /**
795
     * Return nested value when found, otherwise return <i>null</i> value.
796
     *
797
     * Example:
798
     * <code>
799
     * $array = array('1' => array('2' => array('3' => 'value')));
800
     * $value = Arrays::getNestedValue($array, array('1', '2', '3'));
801
     * </code>
802
     * Result:
803
     * <code>
804
     * value
805
     * </code>
806
     *
807
     * @param array $array
808
     * @param array $keys
809
     * @return array|mixed|null
810
     */
811
    public static function getNestedValue(array $array, array $keys)
812
    {
813
        foreach ($keys as $key) {
814
            $array = self::getValue(self::toArray($array), $key);
815
            if (!$array) {
816
                return $array;
817
            }
818
        }
819
        return $array;
820
    }
821
822
    /**
823
     * @deprecated
824
     * @param array $array
825
     * @param array $keys
826
     */
827
    public static function removeNestedValue(array &$array, array $keys)
828
    {
829
        trigger_error('Use Arrays::removeNestedKey instead', E_USER_DEPRECATED);
830
        self::removeNestedKey($array, $keys);
831
    }
832
833
    /**
834
     * Removes nested keys in array.
835
     *
836
     * Example:
837
     * <code>
838
     * $array = array('1' => array('2' => array('3' => 'value')));$array = array('1' => array('2' => array('3' => 'value')));
839
     * Arrays::removeNestedKey($array, array('1', '2'));
840
     * </code>
841
     * Result:
842
     * <code>
843
     * Array
844
     * (
845
     *      [1] => Array
846
     *          (
847
     *          )
848
     * )
849
     * </code>
850
     *
851
     * @param array $array
852
     * @param array $keys
853
     */
854
    public static function removeNestedKey(array &$array, array $keys)
855
    {
856
        $key = array_shift($keys);
857
        if (count($keys) == 0) {
858
            unset($array[$key]);
859
        } else if ($array[$key] !== null) {
860
            self::removeNestedKey($array[$key], $keys);
861
        }
862
    }
863
864
    /**
865
     * Checks if array has nested key. It's possible to check array with null values using flag <i>Arrays::TREAT_NULL_AS_VALUE</i>.
866
     *
867
     * Example:
868
     * <code>
869
     * $array = array('1' => array('2' => array('3' => 'value')));
870
     * $value = Arrays::hasNestedKey($array, array('1', '2', '3'));
871
     * </code>
872
     * Result:
873
     * <code>
874
     * true
875
     * </code>
876
     *
877
     * Example with null values:
878
     * <code>
879
     * $array = array('1' => array('2' => array('3' => null)));
880
     * $value = Arrays::hasNestedKey($array, array('1', '2', '3'), Arrays::TREAT_NULL_AS_VALUE);
881
     * </code>
882
     * Result:
883
     * <code>
884
     * true
885
     * </code>
886
     *
887
     * @param array $array
888
     * @param array $keys
889
     * @param null $flags
890
     * @return bool
891
     */
892
    public static function hasNestedKey(array $array, array $keys, $flags = null)
893
    {
894
        foreach ($keys as $key) {
895
            if (!array_key_exists($key, $array) || (!($flags & self::TREAT_NULL_AS_VALUE) && !isset($array[$key]))) {
896
                return false;
897
            }
898
            $array = self::getValue($array, $key);
899
        }
900
        return true;
901
    }
902
903
    /**
904
     * Returns maps of the flatten keys with corresponding values.
905
     *
906
     * Example:
907
     * <code>
908
     * $array = array(
909
     *      'customer' => array(
910
     *          'name' => 'Name',
911
     *          'phone' => '123456789'
912
     *      ),
913
     *      'other' => array(
914
     *          'ids_map' => array(
915
     *              '1qaz' => 'qaz',
916
     *              '2wsx' => 'wsx'
917
     *          ),
918
     *          'first' => array(
919
     *              'second' => array(
920
     *                  'third' => 'some value'
921
     *              )
922
     *          )
923
     *      )
924
     * );
925
     * $flatten = Arrays::flattenKeysRecursively($array)
926
     * </code>
927
     * Result:
928
     * <code>
929
     * Array
930
     * (
931
     *      [customer.name] => Name
932
     *      [customer.phone] => 123456789
933
     *      [other.ids_map.1qaz] => qaz
934
     *      [other.ids_map.2wsx] => wsx
935
     *      [other.first.second.third] => some value
936
     * )
937
     * </code>
938
     *
939
     * @param array $array
940
     * @return array
941
     */
942
    public static function flattenKeysRecursively(array $array)
943
    {
944
        $result = array();
945
        self::_flattenKeyRecursively($array, $result, '');
946
        return $result;
947
    }
948
949
    private static function _flattenKeyRecursively($array, &$result, $parentKey)
950
    {
951
        foreach ($array as $key => $value) {
952
            $itemKey = ($parentKey ? $parentKey . '.' : '') . $key;
953
            if (is_array($value)) {
954
                self::_flattenKeyRecursively($value, $result, $itemKey);
955
            } else {
956
                $result[$itemKey] = $value;
957
            }
958
        }
959
    }
960
961
    /**
962
     * Returns the number of elements for which the predicate returns true.
963
     *
964
     * Example:
965
     * <code>
966
     * $array = array(1, 2, 3);
967
     * $count = Arrays::count($array, function ($element) {
968
     *      return $element < 3;
969
     * });
970
     * </code>
971
     * Result:
972
     * <code>
973
     * 2
974
     * </code>
975
     *
976
     * @param array $elements
977
     * @param callable $predicate
978
     * @return int
979
     */
980
    public static function count(array $elements, $predicate)
981
    {
982
        $count = 0;
983
        foreach ($elements as $element) {
984
            if (Functions::call($predicate, $element)) {
985
                $count++;
986
            }
987
        }
988
        return $count;
989
    }
990
991
    /**
992
     * This method maps array values using the function which takes key and value as parameters.
993
     * Invokes the function for each value in the array. Creates a new array containing the values returned by the function.
994
     *
995
     * Example:
996
     * <code>
997
     * $array = array('a' => '1', 'b' => '2', 'c' => '3');
998
     * $result = Arrays::mapEntries($array, function ($key, $value) {
999
     *      return $key . '_' . $value;
1000
     * });
1001
     * </code>
1002
     * Result:
1003
     * <code>
1004
     * Array
1005
     * (
1006
     *      [a] => a_1
1007
     *      [b] => b_2
1008
     *      [c] => c_3
1009
     * )
1010
     * </code>
1011
     *
1012
     * @param array $elements
1013
     * @param callable $function
1014
     * @return array
1015
     */
1016
    public static function mapEntries(array $elements, $function)
1017
    {
1018
        $keys = array_keys($elements);
1019
        $values = array_values($elements);
1020
        return array_combine($keys, array_map($function, $keys, $values));
1021
    }
1022
1023
    /**
1024
     * Removes duplicate values from an array. It uses the given expression to extract value that is compared.
1025
     *
1026
     * Example:
1027
     * <code>
1028
     * $a = new stdClass();
1029
     * $a->name = 'bob';
1030
     *
1031
     * $b = new stdClass();
1032
     * $b->name = 'bob';
1033
     *
1034
     * $array = [$a, $b];
1035
     * $result = Arrays::uniqueBy($array, 'name');
1036
     * </code>
1037
     * Result:
1038
     * <code>
1039
     * Array
1040
     * (
1041
     *      [0] => $b
1042
     * )
1043
     * </code>
1044
     *
1045
     * @param array $elements
1046
     * @param $selector
1047
     * @return array
1048
     * @throws Exception
1049
     */
1050
    public static function uniqueBy(array $elements, $selector)
1051
    {
1052
        return array_values(self::toMap($elements, Functions::extractExpression($selector)));
1053
    }
1054
}
1055