/
arr.php
763 lines (682 loc) · 18.3 KB
/
arr.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
<?php
/**
* Part of the Fuel framework.
*
* @package Fuel
* @version 1.0
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2012 Fuel Development Team
* @link http://fuelphp.com
*/
namespace Fuel\Core;
/**
* The Arr class provides a few nice functions for making
* dealing with arrays easier
*
* @package Fuel
* @subpackage Core
*/
class Arr
{
/**
* Gets a dot-notated key from an array, with a default value if it does
* not exist.
*
* @param array $array The search array
* @param mixed $key The dot-notated key or array of keys
* @param string $default The default value
* @return mixed
*/
public static function get($array, $key, $default = null)
{
if ( ! is_array($array) and ! $array instanceof \ArrayAccess)
{
throw new \InvalidArgumentException('First parameter must be an array or ArrayAccess object.');
}
if (is_null($key))
{
return $array;
}
if (is_array($key))
{
$return = array();
foreach ($key as $k)
{
$return[$k] = static::get($array, $k, $default);
}
return $return;
}
foreach (explode('.', $key) as $key_part)
{
if (($array instanceof \ArrayAccess and isset($array[$key_part])) === false)
{
if ( ! is_array($array) or ! array_key_exists($key_part, $array))
{
return \Fuel::value($default);
}
}
$array = $array[$key_part];
}
return $array;
}
/**
* Set an array item (dot-notated) to the value.
*
* @param array $array The array to insert it into
* @param mixed $key The dot-notated key to set or array of keys
* @param mixed $value The value
* @return void
*/
public static function set(&$array, $key, $value = null)
{
if (is_null($key))
{
$array = $value;
return;
}
if (is_array($key))
{
foreach ($key as $k => $v)
{
static::set($array, $k, $v);
}
}
else
{
$keys = explode('.', $key);
while (count($keys) > 1)
{
$key = array_shift($keys);
if ( ! isset($array[$key]) or ! is_array($array[$key]))
{
$array[$key] = array();
}
$array =& $array[$key];
}
$array[array_shift($keys)] = $value;
}
}
/**
* Pluck an array of values from an array.
*
* @param array $array collection of arrays to pluck from
* @param string $key key of the value to pluck
* @param string $index optional return array index key, true for original index
* @return array array of plucked values
*/
public static function pluck($array, $key, $index = null)
{
$return = array();
$get_deep = strpos($key, '.') !== false;
if ( ! $index)
{
foreach ($array as $i => $a)
{
$return[] = (is_object($a) and ! ($a instanceof \ArrayAccess)) ? $a->{$key} :
($get_deep ? static::get($a, $key) : $a[$key]);
}
}
else
{
foreach ($array as $i => $a)
{
$index !== true and $i = (is_object($a) and ! ($a instanceof \ArrayAccess)) ? $a->{$index} : $a[$index];
$return[$i] = (is_object($a) and ! ($a instanceof \ArrayAccess)) ? $a->{$key} :
($get_deep ? static::get($a, $key) : $a[$key]);
}
}
return $return;
}
/**
* Array_key_exists with a dot-notated key from an array.
*
* @param array $array The search array
* @param mixed $key The dot-notated key or array of keys
* @return mixed
*/
public static function key_exists($array, $key)
{
foreach (explode('.', $key) as $key_part)
{
if ( ! is_array($array) or ! array_key_exists($key_part, $array))
{
return false;
}
$array = $array[$key_part];
}
return true;
}
/**
* Unsets dot-notated key from an array
*
* @param array $array The search array
* @param mixed $key The dot-notated key or array of keys
* @return mixed
*/
public static function delete(&$array, $key)
{
if (is_null($key))
{
return false;
}
if (is_array($key))
{
$return = array();
foreach ($key as $k)
{
$return[$k] = static::delete($array, $k);
}
return $return;
}
$key_parts = explode('.', $key);
if ( ! is_array($array) or ! array_key_exists($key_parts[0], $array))
{
return false;
}
$this_key = array_shift($key_parts);
if ( ! empty($key_parts))
{
$key = implode('.', $key_parts);
return static::delete($array[$this_key], $key);
}
else
{
unset($array[$this_key]);
}
return true;
}
/**
* Converts a multi-dimensional associative array into an array of key => values with the provided field names
*
* @param array $assoc the array to convert
* @param string $key_field the field name of the key field
* @param string $val_field the field name of the value field
* @return array
* @throws \InvalidArgumentException
*/
public static function assoc_to_keyval($assoc, $key_field, $val_field)
{
if ( ! is_array($assoc) or $assoc instanceof \Iterator)
{
throw new \InvalidArgumentException('The first parameter must be an array.');
}
$output = array();
foreach ($assoc as $row)
{
if (isset($row[$key_field]) and isset($row[$val_field]))
{
$output[$row[$key_field]] = $row[$val_field];
}
}
return $output;
}
/**
* Converts the given 1 dimensional non-associative array to an associative
* array.
*
* The array given must have an even number of elements or null will be returned.
*
* Arr::to_assoc(array('foo','bar'));
*
* @param string $arr the array to change
* @return array|null the new array or null
* @throws \BadMethodCallException
*/
public static function to_assoc($arr)
{
if (($count = count($arr)) % 2 > 0)
{
throw new \BadMethodCallException('Number of values in to_assoc must be even.');
}
$keys = $vals = array();
for ($i = 0; $i < $count - 1; $i += 2)
{
$keys[] = array_shift($arr);
$vals[] = array_shift($arr);
}
return array_combine($keys, $vals);
}
/**
* Checks if the given array is an assoc array.
*
* @param array $arr the array to check
* @return bool true if its an assoc array, false if not
*/
public static function is_assoc($arr)
{
if ( ! is_array($arr))
{
throw new \InvalidArgumentException('The parameter must be an array.');
}
$counter = 0;
foreach ($arr as $key => $unused)
{
if ( ! is_int($key) or $key !== $counter++)
{
return true;
}
}
return false;
}
/**
* Flattens a multi-dimensional associative array down into a 1 dimensional
* associative array.
*
* @param array the array to flatten
* @param string what to glue the keys together with
* @param bool whether to reset and start over on a new array
* @param bool whether to flatten only associative array's, or also indexed ones
* @return array
*/
public static function flatten($array, $glue = ':', $reset = true, $indexed = true)
{
static $return = array();
static $curr_key = array();
if ($reset)
{
$return = array();
$curr_key = array();
}
foreach ($array as $key => $val)
{
$curr_key[] = $key;
if (is_array($val) and ($indexed or array_values($val) !== $val))
{
static::flatten_assoc($val, $glue, false);
}
else
{
$return[implode($glue, $curr_key)] = $val;
}
array_pop($curr_key);
}
return $return;
}
/**
* Flattens a multi-dimensional associative array down into a 1 dimensional
* associative array.
*
* @param array the array to flatten
* @param string what to glue the keys together with
* @param bool whether to reset and start over on a new array
* @return array
*/
public static function flatten_assoc($array, $glue = ':', $reset = true)
{
return static::flatten($array, $glue, $reset, false);
}
/**
* Reverse a flattened array in its original form.
*
* @param array $array flattened array
* @param string $glue glue used in flattening
* @return array the unflattened array
*/
public static function reverse_flatten($array, $glue = ':')
{
$return = array();
foreach ($array as $key => $value)
{
if (stripos($key, $glue) !== false)
{
$keys = explode($glue, $key);
$temp =& $return;
while (count($keys) > 1)
{
$key = array_shift($keys);
$key = is_numeric($key) ? (int) $key : $key;
if ( ! isset($temp[$key]) or ! is_array($temp[$key]))
{
$temp[$key] = array();
}
$temp =& $temp[$key];
}
$key = array_shift($keys);
$key = is_numeric($key) ? (int) $key : $key;
$temp[$key] = $value;
}
else
{
$key = is_numeric($key) ? (int) $key : $key;
$return[$key] = $value;
}
}
return $return;
}
/**
* Filters an array on prefixed associative keys.
*
* @param array the array to filter.
* @param string prefix to filter on.
* @param bool whether to remove the prefix.
* @return array
*/
public static function filter_prefixed($array, $prefix = 'prefix_', $remove_prefix = true)
{
$return = array();
foreach ($array as $key => $val)
{
if (preg_match('/^'.$prefix.'/', $key))
{
if ($remove_prefix === true)
{
$key = preg_replace('/^'.$prefix.'/','',$key);
}
$return[$key] = $val;
}
}
return $return;
}
/**
* Filters an array by an array of keys
*
* @param array the array to filter.
* @param array the keys to filter
* @param bool if true, removes the matched elements.
* @return array
*/
public static function filter_keys($array, $keys, $remove = false)
{
$return = array();
foreach ($keys as $key)
{
if (array_key_exists($key, $array))
{
$remove or $return[$key] = $array[$key];
if($remove)
{
unset($array[$key]);
}
}
}
return $remove ? $array : $return;
}
/**
* Insert value(s) into an array, mostly an array_splice alias
* WARNING: original array is edited by reference, only boolean success is returned
*
* @param array the original array (by reference)
* @param array|mixed the value(s) to insert, if you want to insert an array it needs to be in an array itself
* @param int the numeric position at which to insert, negative to count from the end backwards
* @return bool false when array shorter then $pos, otherwise true
*/
public static function insert(array &$original, $value, $pos)
{
if (count($original) < abs($pos))
{
\Error::notice('Position larger than number of elements in array in which to insert.');
return false;
}
array_splice($original, $pos, 0, $value);
return true;
}
/**
* Insert value(s) into an array before a specific key
* WARNING: original array is edited by reference, only boolean success is returned
*
* @param array the original array (by reference)
* @param array|mixed the value(s) to insert, if you want to insert an array it needs to be in an array itself
* @param string|int the key before which to insert
* @return bool false when key isn't found in the array, otherwise true
*/
public static function insert_before_key(array &$original, $value, $key)
{
$pos = array_search($key, array_keys($original));
if ($pos === false)
{
\Error::notice('Unknown key before which to insert the new value into the array.');
return false;
}
return static::insert($original, $value, $pos);
}
/**
* Insert value(s) into an array after a specific key
* WARNING: original array is edited by reference, only boolean success is returned
*
* @param array the original array (by reference)
* @param array|mixed the value(s) to insert, if you want to insert an array it needs to be in an array itself
* @param string|int the key after which to insert
* @return bool false when key isn't found in the array, otherwise true
*/
public static function insert_after_key(array &$original, $value, $key)
{
$pos = array_search($key, array_keys($original));
if ($pos === false)
{
\Error::notice('Unknown key after which to insert the new value into the array.');
return false;
}
return static::insert($original, $value, $pos + 1);
}
/**
* Insert value(s) into an array after a specific value (first found in array)
*
* @param array the original array (by reference)
* @param array|mixed the value(s) to insert, if you want to insert an array it needs to be in an array itself
* @param string|int the value after which to insert
* @return bool false when value isn't found in the array, otherwise true
*/
public static function insert_after_value(array &$original, $value, $search)
{
$key = array_search($search, $original);
if ($key === false)
{
\Error::notice('Unknown value after which to insert the new value into the array.');
return false;
}
return static::insert_after_key($original, $value, $key);
}
/**
* Sorts a multi-dimensional array by it's values.
*
* @access public
* @param array The array to fetch from
* @param string The key to sort by
* @param string The order (asc or desc)
* @param int The php sort type flag
* @return array
*/
public static function sort($array, $key, $order = 'asc', $sort_flags = SORT_REGULAR)
{
if ( ! is_array($array))
{
throw new \InvalidArgumentException('Arr::sort() - $array must be an array.');
}
if (empty($array))
{
return $array;
}
foreach ($array as $k => $v)
{
$b[$k] = static::get($v, $key);
}
switch ($order)
{
case 'asc':
asort($b, $sort_flags);
break;
case 'desc':
arsort($b, $sort_flags);
break;
default:
throw new \InvalidArgumentException('Arr::sort() - $order must be asc or desc.');
break;
}
foreach ($b as $key => $val)
{
$c[] = $array[$key];
}
return $c;
}
/**
* Sorts an array on multitiple values, with deep sorting support.
*
* @param array $array collection of arrays/objects to sort
* @param array $conditions sorting conditions
* @param bool @ignore_case wether to sort case insensitive
*/
public static function multisort($array, $conditions, $ignore_case = false)
{
$temp = array();
$keys = array_keys($conditions);
foreach($keys as $key)
{
$temp[$key] = static::pluck($array, $key, true);
is_array($conditions[$key]) or $conditions[$key] = array($conditions[$key]);
}
$args = array();
foreach ($keys as $key)
{
$args[] = $ignore_case ? array_map('strtolower', $temp[$key]) : $temp[$key];
foreach($conditions[$key] as $flag)
{
$args[] = $flag;
}
}
$args[] = &$array;
call_user_func_array('array_multisort', $args);
return $array;
}
/**
* Find the average of an array
*
* @param array the array containing the values
* @return numeric the average value
*/
public static function average($array)
{
// No arguments passed, lets not divide by 0
if ( ! ($count = count($array)) > 0)
{
return 0;
}
return (array_sum($array) / $count);
}
/**
* Replaces key names in an array by names in $replace
*
* @param array the array containing the key/value combinations
* @param array|string key to replace or array containing the replacement keys
* @param string the replacement key
* @return array the array with the new keys
*/
public static function replace_key($source, $replace, $new_key = null)
{
if(is_string($replace))
{
$replace = array($replace => $new_key);
}
if ( ! is_array($source) or ! is_array($replace))
{
throw new \InvalidArgumentException('Arr::replace_key() - $source must an array. $replace must be an array or string.');
}
$result = array();
foreach ($source as $key => $value)
{
if (array_key_exists($key, $replace))
{
$result[$replace[$key]] = $value;
}
else
{
$result[$key] = $value;
}
}
return $result;
}
/**
* Merge 2 arrays recursively, differs in 2 important ways from array_merge_recursive()
* - When there's 2 different values and not both arrays, the latter value overwrites the earlier
* instead of merging both into an array
* - Numeric keys that don't conflict aren't changed, only when a numeric key already exists is the
* value added using array_push()
*
* @param array multiple variables all of which must be arrays
* @return array
* @throws \InvalidArgumentException
*/
public static function merge()
{
$array = func_get_arg(0);
$arrays = array_slice(func_get_args(), 1);
if ( ! is_array($array))
{
throw new \InvalidArgumentException('Arr::merge() - all arguments must be arrays.');
}
foreach ($arrays as $arr)
{
if ( ! is_array($arr))
{
throw new \InvalidArgumentException('Arr::merge() - all arguments must be arrays.');
}
foreach ($arr as $k => $v)
{
// numeric keys are appended
if (is_int($k))
{
array_key_exists($k, $array) ? array_push($array, $v) : $array[$k] = $v;
}
elseif (is_array($v) and array_key_exists($k, $array) and is_array($array[$k]))
{
$array[$k] = static::merge($array[$k], $v);
}
else
{
$array[$k] = $v;
}
}
}
return $array;
}
/**
* Prepends a value with an asociative key to an array.
* Will overwrite if the value exists.
*
* @param array $arr the array to prepend to
* @param string|array $key the key or array of keys and values
* @param mixed $valye the value to prepend
*/
public static function prepend(&$arr, $key, $value = null)
{
$arr = (is_array($key) ? $key : array($key => $value)) + $arr;
}
/**
* Recursive in_array
*
* @param mixed $needle what to search for
* @param array $haystack array to search in
* @return bool wether the needle is found in the haystack.
*/
public static function in_array_recursive($needle, $haystack, $strict = false)
{
foreach ($haystack as $value)
{
if ( ! $strict and $needle == $value)
{
return true;
}
elseif ($needle === $value)
{
return true;
}
elseif (is_array($value) and static::in_array_recursive($needle, $value, $strict))
{
return true;
}
}
return false;
}
/**
* Checks if the given array is a multidimensional array.
*
* @param array $arr the array to check
* @param array $all_keys if true, check that all elements are arrays
* @return bool true if its a multidimensional array, false if not
*/
public static function is_multi($arr, $all_keys = false)
{
$values = array_filter($arr, 'is_array');
return $all_keys ? count($arr) === count($values) : count($values) > 0;
}
}