问题
Given I have an array:
$array = array(
'a' => array(
'b' => array(
'c' => 'hello',
),
),
'd' => array(
'e' => array(
'f' => 'world',
),
),
);
I want to "flatten" it to a single dimension look-up of references, concatenating keys with a delimiter (in the case of this example, a forward slash /
)
Performing a var_dump()
on a successful output would yield: (note all the references)
array(6) {
["a"]=>
&array(1) {
["b"]=>
&array(1) {
["c"]=>
&string(5) "hello"
}
}
["a/b"]=>
&array(1) {
["c"]=>
&string(5) "hello"
}
["a/b/c"]=>
&string(5) "hello"
["d"]=>
&array(1) {
["e"]=>
&array(1) {
["f"]=>
&string(5) "world"
}
}
["d/e"]=>
&array(1) {
["f"]=>
&string(5) "world"
}
["d/e/f"]=>
&string(5) "world"
}
array(2) {
["a"]=>
&array(1) {
["b"]=>
&array(1) {
["c"]=>
&string(5) "hello"
}
}
["d"]=>
&array(1) {
["e"]=>
&array(1) {
["f"]=>
&string(5) "world"
}
}
}
As it stands, I'm using this:
function build_lookup(&$array, $keys = array()){
$lookup = array();
foreach($array as $key => &$value){
$path = array_merge($keys, (Array) $key);
$lookup[implode('/', $path)] = &$value;
if(is_array($value)){
$lookup = array_merge($lookup, build_lookup($value, $path));
}
}
return $lookup;
}
However, I'm trying to improve on it by removing the element of recursion (switching to a stack/pop approach) The problem with doing so is reference preservation, as the typical recursion-to-non-recursion approach of:
$stack = $input;
while(!empty($stack)){
$current = array_pop($stack);
// do stuff and push to stack;
}
...fails with references.
I've seen a few similar questions/answers on SO, though none of which dealt appropriately with references (as it wasn't the asker's intent)
Is there a better (read faster) approach here?
The eventual solution (thanks @chris):
/**
*
* @return array
*/
public function get_lookup_array()
{
$stack = $lookup = array();
try
{
foreach($this->_array as $key => &$value)
{
$stack[$key] = &$value;
}
while(!empty($stack))
{
$path = key($stack);
$lookup[$path] = &$stack[$path];
if(is_array($lookup[$path]))
{
foreach($lookup[$path] as $key => &$value)
{
$stack[$path . $this->_separator . $key] = &$value;
}
}
unset($stack[$path]);
}
}
catch(\Exception $exception)
{
return false;
}
return $lookup;
}
回答1:
header('content-type:text/plain');
$arr = array(
'a' => array(
'b' => array(
'c' => 'hello',
),
),
'd' => array(
'e' => array(
'f' => 'world',
),
),
);
//prime the stack using our format
$stack = array();
foreach ($arr as $k => &$v) {
$stack[] = array(
'keyPath' => array($k),
'node' => &$v
);
}
$lookup = array();
while ($stack) {
$frame = array_pop($stack);
$lookup[join('/', $frame['keyPath'])] = &$frame['node'];
if (is_array($frame['node'])) {
foreach ($frame['node'] as $key => &$node) {
$keyPath = array_merge($frame['keyPath'], array($key));
$stack[] = array(
'keyPath' => $keyPath,
'node' => &$node
);
$lookup[join('/', $keyPath)] = &$node;
}
}
}
var_dump($lookup);
// check functionality
$lookup['a'] = 0;
$lookup['d/e/f'] = 1;
var_dump($arr);
Alternatively you could have done stuff like this to get a reference /w array_pop functionality
end($stack);
$k = key($stack);
$v = &$stack[$k];
unset($stack[$k]);
That works because php arrays have elements ordered by key creation time. unset() deletes the key, and thus resets the creation time for that key.
来源:https://stackoverflow.com/questions/8376602/flatten-multidimensional-associative-array-to-one-one-dimensional-array-of-refer