Reaching deep into arrays using array_reduce in PHP
Recently saw an article on doing this in JavaScript, so figured I’d try it out in PHP.
The original article:
Safely Accessing Deeply Nested Values In JavaScript
Many Ways To Safely Access Nested Data.
medium.com
The idea is you have an deeply nested array:
$x = [
'a' => 1,
'b' => [
'c' => 2,
'd' => [3, 4, 5],
],
];
You want to easily get the nested value at the path b/d/0.
The old-school way to do this without producing warnings or notices would be to do something like this:
if (
array_key_exists('b', $x) &&
is_array($x['b']) &&
array_key_exists('d', $x['b']) &&
is_array($x['b']['d']) &&
array_key_exists(0, $x['b']['d']
) { return $x['b']['d'][0]; }
I know, pain in the butt, right? Let’s see if we can do that more easily. PHP has a function called array_reduce which will iterate over an array with a callback until it gets a null or there is nothing left to iterate, in which case it returns the result of the last call to the callback function.
In order to use this, we need to have a callback which will simply check to see if the value is an array and if a specific key exists.
function keyReduce(array $arr, $idx) {
return (
array_key_exists($idx, $arr)
) ? $arr[$idx] : null;
}
In the above, we’re just using a ternary to check and return a value if it is available, or null.
Now, we can plug this into array_reduce with our path split into an array [‘b’, ‘d’, 0] and our original array.
$result = array_reduce(['b', 'd', 0], 'keyReduce', $x);
echo $result;
We get our result:
3
What if we give it a path that doesn’t exist? We just get null back. No errors, no warnings, no notices.
We can also use this to get any section of the original array using a path. If we leave off the 0 from the path requested, we get that chunk.
$result = array_reduce(['b', 'd'], 'keyReduce', $x);
print_r($result);
We get…
Array
(
[0] => 3,
[1] => 4,
[2] => 5
)
So there you go. An easy way to recurse through a deep array to fetch a value. You could also wrap the whole thing in a function to make it a bit easier.
function getArrayPath(array $path, array $deepArray) {
$reduce = function(array $xs, $x) {
return (
array_key_exists($x, $xs)
) ? $xs[$x] : null;
}; return array_reduce($path, $reduce, $deepArray);
}print_r(getArrayPath(['b','d', 0], $x));3