Reputation: 15099
Let's pretend I have this:
$str = "/a/b/c/d/";
$arr = array_filter( explode("/", $str );
At this point $arr
contains 4 elements. Is there a way I could create a path in an array with those 4 elements, such as:
$result = [
"a" => [
"b" => [
"c" => [
"d" => [
]
]
]
]
]
...without iterating over $arr
?
I know that
$x["a"]["b"]["c"]["d"] = 1;
is perfectly valid and it will create a 4 levels array even if $x
wasn't declared as an array, so what I'm asking should be possible.
Upvotes: 0
Views: 76
Reputation: 72177
There is no way to use all the values of $arr
without iterating over it.
I guess you don't want to write a foreach
loop but use some PHP function that does the iteration for you.
This is a possible solution:
$x = array_reduce(
array_reverse($arr),
function ($carry, $item) {
return [$item => $carry];
},
1
);
It generates the same result as:
$x = [];
$x['a']['b']['c']['d'] = 1;
Unfortunately it iterates over $arr
two times (array_reverse()
and array_reduce()
).
Another approach that generates the required embedding using objects (stdClass
) instead of arrays:
$out = new stdClass;
array_reduce(
$arr,
function ($carry, $item) {
$v = new stdClass;
$carry->{$item} = $v;
return $v;
},
$out
);
It works using a single iteration over $arr
but it relies on the way the objects are handled in PHP to work (and this doesn't work with arrays).
PHP handles the objects in a way that makes them look like they are passed by reference. It's a common misconception that the objects are "passed by reference" in PHP but this is not true. A double indirection is responsible for this behaviour.
function makeArray(array $arr, $initial)
{
if (! count($arr)) {
return $initial;
} else {
$key = array_shift($arr);
return [ $key => makeArray($arr, $initial) ];
}
}
$out = makeArray($arr, 1);
This solution iterates only once over the array and generates a hierarchy of arrays but recursivity is disastrous for large input arrays because it uses a lot of memory.
Upvotes: 0
Reputation: 29932
I wrote a function once, that had this behaviour as a side effect. It doesn't iterate, but uses recursion.
See: https://github.com/feeela/php-utils/blob/master/function.getArrayValueReference.php
You may call like that:
<?php
$newArray = array();
$keys = 'a/b/c/d';
$referenceToD =& getArrayValueReference( $newArray, explode( '/', $keys ), true );
$referenceToD[0] = 'foo';
$referenceToD[1] = 'bar';
print_r( $newArray );
This modifies the array $newArray
and creates all the levels. The functions return value is a reference to the last key ('d' in that example).
…which results in:
Array (
[a] => Array (
[b] => Array (
[c] => Array (
[d] => Array (
[0] => foo
[1] => bar
)
)
)
)
)
Upvotes: 1
Reputation: 78984
I DO NOT recommend this as there are security implications when using eval()
. However, because I stated in the comments that it couldn't be done without iteration, I felt compelled to post this as an answer (yes, I know implode()
iterates internally).
$str = "/a/b/c/d/";
$arr = array_filter( explode("/", $str ));
$keys = '["'.implode('"]["', $arr).'"]';
eval('$x'.$keys.' = 1;');
print_r($x);
For a more practical way see How to write getter/setter to access multi-leveled array by dot separated key names?
Upvotes: 1