Skip to content

Instantly share code, notes, and snippets.

@Isinlor
Last active June 21, 2017 12:33
Show Gist options
  • Save Isinlor/260113a810addaa34cbc053709a0ce6a to your computer and use it in GitHub Desktop.
Save Isinlor/260113a810addaa34cbc053709a0ce6a to your computer and use it in GitHub Desktop.
DeepArray to simplifies work with arbitrarily deeply nested arrays.
<?php
/**
* Simplifies work with deep nested arrays.
*
* @author Tomasz Darmetko <tomasz.darmetko@gmail.com>
*/
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