Creating a complex nested array key

I want to create a nested array key with a single variable. I know how to do it if I split it up into separate keys, but not with specifying the nested key as a single string or something like it.

$array = array();

$key = "['houses']['ocean_view']['state']";

$array[$key] = 'Oregon';


var_dump($array);
var_dump($array[$key]); 

Outcome:
array (size=1)
‘[‘houses’][‘ocean_view’][‘state’]’ => string ‘Oregon’ (length=6)

string 'Oregon' (length=6)

I want it to create a nested array, not a very long key in the root.

Before giving you a solution, I am bound to ask about the prerequisites of this question.

Where this string with all the keys is coming from and what is the context of this task? Why do you need to assign a value this way?

Some more context.

I have the array keys as strings already. I have a function that must place an incoming string into the right place in an array, but it only accepts two parameters - the key and the value. (There is a corresponding function that gets the appropriate array values instead of placing them.) I can edit the function, but I already have a massive amount of keys-as-strings. I know I could explode / regex the string into separate variables as needed in the function, but this would be liable to error as the strings themselves could be escaped, contain strange characters, and so on. I’m trying very hard to avoid an explode / regex solution.

Edit: If you can tell me how to do it, even if you think it doesn’t suit this use case, I very much still need to know for other use cases.

I am not aware of anything builtin to php that will allow you to do this. But there a Symfony component called PropertyAccessor with this sort of functionality.

The code would look like:

use Symfony\Component\PropertyAccess\PropertyAccess;

$accessor = PropertyAccess::createPropertyAccessor();

$accessor->setValue($array,'[houses][ocean_view][state]';

Not exactly what you asked for but close.

1 Like

I am not aware of a native function to do that, but you could write your own. :-) E.g. this function would take two arrays, the array to access and an array of keys, and recursively walk down the array (if nested) to eventually return a reference (if desired) to the match, like

/**
 * Return the reference to an element/subarray
 * of an array, specified by a key array
 * 
 * @param  array &$array Array to access by reference
 * @param  mixed $keys   Array of the keys
 * @return mixed         Reference to the match
 */
function &array_access(&$array, $keys) {

    // If there are keys left to walk down
    // a nested array
    if ($keys) {
        $key = array_shift($keys);

        // Get and return the reference to the
        // subarray with the current key
        $sub = &array_access(
            $array[$key],
            $keys
        );

        return $sub;
    } else {

        // Return the match
        return $array;
    }
}

// Usage
$array = [];
$keys  =['houses', 'ocean_view', 'state'];

// Here we're accessing the array match
// by reference
$reference = &array_access($array, $keys);
$reference = 'Oregon';

// But we might also just return the value
$value = array_access($array, $keys);
echo $value;

$value = 'Ibiza';
print_r($array);

If the format of $keys is fixed as a string like above, you might parse it to an array like e.g.

$keys = "['houses']['ocean_view']['state']";
$keys = preg_replace('/^\[|\]$/', '', $keys);
$keys = explode('][', $keys);

You might also incorporate that functionality right within the access function, like

/**
 * Return the reference to an element/subarray
 * of an array, specified by a key array or string
 * 
 * @param  array &$array Array to access by reference
 * @param  mixed $keys   Array of the keys, or a string
 *                       representation of how you'd normally
 *                       access $array
 * @return mixed         Reference to the match
 */
function &array_access(&$array, $keys) {

    // Check if $keys is a string
    if (is_string($keys)) {

        // Parse it to an array
        $keys  = explode(
            '][', 
            preg_replace(
                '/^\[|\]$/', 
                '', 
                $keys
            )
        );
    }

    // etc...
}
1 Like

Thanks ahundiak - I did not know that symfony component existed. It is a little heavy duty for what I need, but it looks like I could make it fit. The example you provided helped.

Thanks m3g4p0p - you went above and beyond with that example code (even with doc blocks!). I learned a lot. I will probably implement something very close to what you’ve done, even though I was trying to avoid regex and exploding. :slight_smile: If I can’t make it work the way I want, I will fall back to the symfony component.

In your code, you use & in front of the function name ( &array_access ) when declaring the function, and also when calling it recursively within the function. I am not familiar with this usage - is this passing the function itself by reference or is it only a naming convention you use to indicate the function takes a value by reference?

Glad I could help! :-)

Both are necessary here: the & in front of the function name in the declaration means that the function will return by reference, and the one in front of the call is for binding this reference to a variable – thus allowing us to return the reference up the recursion.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.