alexandernst
alexandernst

Reputation: 15099

Create an array from the keys of an array

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

Answers (3)

axiac
axiac

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.

A simple solution that iterates two times over the array (behind the scene)

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 solution that generates a hierarchy of objects

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.

A recursive solution

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

feeela
feeela

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

AbraCadaver
AbraCadaver

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

Related Questions