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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.