views:

64

answers:

3

I'm trying to build an associative array in PHP dynamically, and not quite getting my strategy right. Basically, I want to insert a value at a certain depth in the array structure, for instance:

$array['first']['second']['third'] = $val;

Now, the thing is, I'm not sure if that depth is available, and if it isn't, I want to create the keys (and arrays) for each level, and finally insert the value at the correct level.

Since I'm doing this quite a lot in my code, I grew tired of doing a whole bunch of "array_key_exists", so I wanted to do a function that builds the array for me, given a list of the level keys. Any help on a good strategy for this is appreciated. I'm sure there is a pretty simple way, I'm just not getting it...

+2  A: 

php doesn't blame you if you do it just so

$array['first']['second']['third'] = $val;
print_r($array);

if you don't want your keys to be hard coded, here's a flexible solution

/// locate or create element by $path and set its value to $value
/// $path is either an array of keys, or a delimited string
function array_set(&$a, $path, $value) {
    if(!is_array($path))
        $path = explode($path[0], substr($path, 1));
    $key = array_pop($path);
    foreach($path as $k) {
        if(!isset($a[$k]))
            $a[$k] = array();
        $a = &$a[$k];
    }
    $a[$key ? $key : count($a)] = $value;
}

// example:
$x = array();

array_set($x, "/foo/bar/baz", 123);
array_set($x, "/foo/bar/quux", 456);
array_set($x, array('foo', 'bah'), 789);
stereofrog
No, it doesn't, but I consider using unintialized variables (like inner arrays) bad coding style. PHP will generate a strict NOTICE, I think.
soulmerge
It does, if you have `error_reporting` set to `E_STRICT`.
fireeyedboy
err no it doesn't
stereofrog
Doesn't work if you add another `array_set($x, "/foo/bar/quux/baz", 42);``
Felix Kling
@stereofrog: I consider a notice something to take care of, but granted; 'blame' is a bit strong perhaps.
fireeyedboy
@stereofrog - thanks a million - this did the trick. I had started something similar, but couldn't get hang of the reference part. Now I can spend the rest of the afternoon trying to grok that instead of tearing out any more hair. :-)
Emil
If you -know- that the array keys are good (just maybe not existent), and the value you are setting is good, you can suppress the notice for the line with the Error Control Operator (@).
J. Kenzal Hunter Sr.
A: 

That's tricky, you'd need to work with references (or with recursion, but I chose references here):

# Provide as many arguments as you like:
# createNestedArray($array, 'key1', 'key2', etc.)
function createNestedArray(&$array) {
    $arrayCopy = &$array;
    $args = func_get_args();
    array_shift($args);
    while (($key = array_shift($args)) !== false) {
        $arrayCopy[$key] = array();
        $arrayCopy = &$arrayCopy[$key];
    }
}
soulmerge
A: 

Create a function like:

function insert_into(&$array, array $keys, $value) {
     $last = array_pop($keys);       

     foreach($keys as $key) {
          if(!array_key_exists($key, $array) || 
              array_key_exists($key, $array) && !is_array($array[$key])) {
                  $array[$key] = array();

          }
          $array = &$array[$key];
     }
     $array[$last] = $value;
}

Usage:

$a = array();
insert_into($a, array('a', 'b', 'c'), 1);
print_r($a);

Ouput:

Array
(
    [a] => Array
        (
            [b] => Array
                (
                    [c] => 1
                )

        )

)
Felix Kling