I'm building a small template system and i'm looking for a way to invoke multidimensional associative arrays using dots. For example:
$animals = array(
'four-legged' => array (
'cute' => 'no',
'ugly' => 'no',
'smart' => array('best' => 'dog','worst' => 'willy')
),
'123' => '456',
'abc' => 'def'
);
Then, in my template, if I wanted to show 'dog', I would put:
{a.four-legged.smart.best}
Well, given a string with four-legged.smart.worst
:
function getElementFromPath(array $array, $path) {
$parts = explode('.', $path);
$tmp = $array;
foreach ($parts as $part) {
if (!isset($tmp[$part])) {
return ''; //Path is invalid
} else {
$tmp = $tmp[$part];
}
}
return $tmp; //If we reached this far, $tmp has the result of the path
}
So you can call:
$foo = getElementFromPath($array, 'four-legged.smart.worst');
echo $foo; // willy
And if you want to write elements, it's not much harder (you just need to use references, and a few checks to default the values if the path doesn't exist)...:
function setElementFromPath(array &$array, $path, $value) {
$parts = explode('.', $path);
$tmp =& $array;
foreach ($parts as $part) {
if (!isset($tmp[$part]) || !is_array($tmp[$part])) {
$tmp[$part] = array();
}
$tmp =& $tmp[$part];
}
$tmp = $value;
}
Edit: Since this is in a template system, it may be worth while "compiling" the array down to a single dimension once, rather than traversing it each time (for performance reasons)...
function compileWithDots(array $array) {
$newArray = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$tmpArray = compileWithDots($value);
foreach ($tmpArray as $tmpKey => $tmpValue) {
$newArray[$key . '.' . $tmpKey] = $tmpValue;
}
} else {
$newArray[$key] = $value;
}
}
return $newArray;
}
So that would convert:
$animals = array(
'four-legged' => array (
'cute' => 'no',
'ugly' => 'no',
'smart' => array(
'best' => 'dog',
'worst' => 'willy'
)
),
'123' => '456',
'abc' => 'def'
);
Into
array(
'four-legged.cute' => 'no',
'four-legged.ugly' => 'no',
'four-legged.smart.best' => 'dog',
'four-legged.smart.worst' => 'willy',
'123' => '456',
'abc' => 'def',
);
Then your lookup just becomes $value = isset($compiledArray[$path]) ? $compiledArray[$path] : '';
instead of $value = getElementFromPath($array, $path);
It trades pre-computing for inline speed (speed within the loop)...