0

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.

8
  • For what reason don't you want to iterate over the array in the first place ? Commented May 19, 2015 at 14:55
  • 2
    "...without iterating over $arr?" NO... Commented May 19, 2015 at 14:55
  • @Virus721 I'll be processing quite a huge amount of data, I was hoping I could skip some iterations. Commented May 19, 2015 at 14:57
  • Depends on what you mean by iteration. I'm not sure how PHP works internally, but it certainly creates the index used in $x["a"]["b"]["c"]["d"] one by one when evaluating the left member of the assignment. No magic can create all of them at the same time. $x["a"]["b"]["c"]["d"] probably results in (((($x)["a"])["b"])["c"])["d"]) which is recursive, like most expressions parsing. Commented May 19, 2015 at 14:59
  • @Virus721 By "iteration" I mean for($arr as $key) and then create each node in the array. Commented May 19, 2015 at 15:03

3 Answers 3

1

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?

Sign up to request clarification or add additional context in comments.

4 Comments

You are actually doing iterations in your code. The function implode is doing such iterations =) Otherwise, your code is quite amazing.
Good ! No try to code a physics engine without using the if keyword !
I think I'll go with the getter/setter solution, even I was hoping I could avoid doing more iterations than required.
I'm pretty sure that internally this: $x["a"]["b"]["c"]["d"] = 1; is iterating as well.
1

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
                )
            )
        )
    )
)

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.