Last active
June 21, 2017 12:33
-
-
Save Isinlor/260113a810addaa34cbc053709a0ce6a to your computer and use it in GitHub Desktop.
DeepArray to simplifies work with arbitrarily deeply nested arrays.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Simplifies work with deep nested arrays. | |
* | |
* @author Tomasz Darmetko <[email protected]> | |
*/ | |
class DeepArray | |
{ | |
/** | |
* Set an value in nested array. | |
* | |
* @param array $array Nested array where value will be set | |
* @param array $keys Keys pointing to the place where new value must be set | |
* @param mixed $value The value to set | |
*/ | |
static public function set(array &$array, array $keys, $value) | |
{ | |
$reference =& static::getReference($array, $keys); | |
/** @noinspection SuspiciousAssignmentsInspection */ | |
$reference = $value; | |
} | |
/** | |
* Get an value from the nested array. | |
* | |
* @param array $array Nested array whose value will be returned | |
* @param array $keys Keys pointing to the value | |
* @param mixed|null $default Default value to return if no value have been found | |
* | |
* @return mixed Value found in the array or default value | |
*/ | |
static public function get(array $array, array $keys, $default = null) | |
{ | |
// PHP does not have a syntax to dynamically access arbitrary nested arrays | |
// use a simple loop to do this programmatically | |
$value =& $array; | |
foreach ($keys as $key) { | |
// check if combination contains any value | |
if (isset($value[$key]) || array_key_exists($key, $value)) { | |
// use a reference as a simple trick to not create new arrays over and over | |
$value =& $value[$key]; | |
} else { | |
return $default; // array does not contain one of the keys, return default value | |
} | |
} | |
return $value; | |
} | |
/** | |
* Returns reference to the value on specified position in the array. | |
* | |
* @param array $array Nested array to whose value an reference will be returned | |
* @param array $keys Keys pointing to the value | |
* @param mixed|null $default Default value to set if no value have been found | |
* | |
* @return mixed Reference to the value on specified position in the array. | |
*/ | |
static public function &getReference(array &$array, array $keys, $default = null) | |
{ | |
// spacial case when no keys are given | |
if (empty($keys)) { | |
$array = $default; | |
return $array; | |
} | |
// pop last key from given keys, if the key does not exists in the stored array | |
// the default value must be set for this key instead of standard empty array as in the loop below | |
$lastKey = array_pop($keys); | |
// go trough all nested arrays (without the deepest reference, this array will contain the value) | |
foreach ($keys as $key) { | |
// check if nested array exists, create one if not | |
if (!isset($array[$key])) { | |
$array[$key] = []; | |
} | |
// get a reference to the next nested array | |
$array =& $array[$key]; | |
} | |
// check if value was found, set the default value if not | |
if (!array_key_exists($lastKey, $array)) { | |
$array[$lastKey] = $default; | |
} | |
// return reference to the value | |
return $array[$lastKey]; | |
} | |
/** | |
* Recursive walk trough given array on required level. | |
* This function will recurse into structure of the array up to specified level and invoke given callable with two | |
* parameters. The first parameter are keys of nested arrays and the second is value stored in the arrays on | |
* specified level. | |
* | |
* @param array $array Array to walk trough | |
* @param int $level Level on which array must be walked trough | |
* @param callable $callable Signature of callable, function(array $keys, $value) : void | |
* @param array $keys @internal Allows to remember keys of nested arrays up to required level | |
*/ | |
static public function recursiveWalkForLevel(array &$array, int $level, callable $callable, $keys = []) | |
{ | |
if ($level > 1) { | |
foreach ($array as $key => &$nestedArray) { | |
$currentKeys = $keys; | |
$currentKeys[] = $key; | |
static::recursiveWalkForLevel($nestedArray, $level - 1, $callable, $currentKeys); | |
} | |
} else { | |
foreach ($array as $key => &$value) { | |
$currentKeys = $keys; | |
$currentKeys[] = $key; | |
$callable($currentKeys, $value); | |
} | |
} | |
} | |
/** | |
* Creates recursive iterator that iterates trough given array on required level. Iterator will recurse into | |
* structure of the array up to specified level. Iterator for each value of the array will provide as a key an | |
* array consisting of keys of nested arrays leading to the value. | |
* | |
* Warning! Iterator on deeply nested arrays is around 2 times slower than walking trough the array. Although it is | |
* marginally faster tough on flat arrays. @see DeepArrayBench | |
* | |
* @param array $array Array to iterate on | |
* @param int $level Level on which array must be iterated on | |
* @param bool $revertKeys @internal Allows to revert order of keys passed as a key for value during iteration | |
* | |
* @return \Generator | |
*/ | |
static public function createIteratorForLevel(array $array, int $level, $revertKeys = true): \Generator | |
{ | |
if ($level > 1) { | |
foreach ($array as $key => $nestedArray) { | |
foreach (self::createIteratorForLevel($nestedArray, $level - 1, false) as $keys => $value) { | |
$keys[] = $key; | |
if ($revertKeys) { | |
$keys = array_reverse($keys); | |
} | |
yield $keys => $value; | |
} | |
} | |
} else { | |
foreach ($array as $key => $value) { | |
yield [$key] => $value; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment