Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access and manipulate multi-dimensional array by key names / path?

Tags:

I've to implement a setter in PHP, that allows me to specify the key, or sub key, of an array (the target), passing the name as a dot-separated-keys value.

Given the following code:

$arr = array('a' => 1,              'b' => array(                  'y' => 2,                  'x' => array('z' => 5, 'w' => 'abc')              ),              'c' => null);  $key = 'b.x.z'; $path = explode('.', $key); 

From the value of $key I want to reach the value 5 of $arr['b']['x']['z'].

Now, given a variable value of $key and a different $arr value (with different deepness).

How can I set the value of the element referred by by $key?

For the getter get() I wrote this code:

public static function get($name, $default = null) {     $setting_path = explode('.', $name);     $val = $this->settings;      foreach ($setting_path as $key) {         if(array_key_exists($key, $val)) {             $val = $val[$key];         } else {             $val = $default;             break;         }     }     return $val; } 

To write a setter is more difficult because I succeed in reaching the right element (from the $key), but I am not able to set the value in the original array and I don't know how to specify the keys all at once.

Should I use some kind of backtracking? Or can I avoid it?

like image 200
Kamafeather Avatar asked Jan 13 '15 19:01

Kamafeather


2 Answers

Assuming $path is already an array via explode (or add to the function), then you can use references. You need to add in some error checking in case of invalid $path etc. (think isset):

$key = 'b.x.z'; $path = explode('.', $key); 

Getter

function get($path, $array) {     //$path = explode('.', $path); //if needed     $temp =& $array;      foreach($path as $key) {         $temp =& $temp[$key];     }     return $temp; }  $value = get($path, $arr); //returns NULL if the path doesn't exist 

Setter / Creator

This combination will set a value in an existing array or create the array if you pass one that has not yet been defined. Make sure to define $array to be passed by reference &$array:

function set($path, &$array=array(), $value=null) {     //$path = explode('.', $path); //if needed     $temp =& $array;      foreach($path as $key) {         $temp =& $temp[$key];     }     $temp = $value; }  set($path, $arr); //or set($path, $arr, 'some value'); 

Unsetter

This will unset the final key in the path:

function unsetter($path, &$array) {     //$path = explode('.', $path); //if needed     $temp =& $array;      foreach($path as $key) {         if(!is_array($temp[$key])) {             unset($temp[$key]);         } else {             $temp =& $temp[$key];         }     } } unsetter($path, $arr); 

*The original answer had some limited functions that I will leave in case they are of use to someone:

Setter

Make sure to define $array to be passed by reference &$array:

function set(&$array, $path, $value) {     //$path = explode('.', $path); //if needed     $temp =& $array;      foreach($path as $key) {         $temp =& $temp[$key];     }     $temp = $value; }  set($arr, $path, 'some value'); 

Or if you want to return the updated array (because I'm bored):

function set($array, $path, $value) {     //$path = explode('.', $path); //if needed     $temp =& $array;      foreach($path as $key) {         $temp =& $temp[$key];     }     $temp = $value;      return $array; }  $arr = set($arr, $path, 'some value'); 

Creator

If you wan't to create the array and optionally set the value:

function create($path, $value=null) {     //$path = explode('.', $path); //if needed     foreach(array_reverse($path) as $key) {         $value = array($key => $value);     }     return $value; }      $arr = create($path);     //or $arr = create($path, 'some value'); 

For Fun

Constructs and evaluates something like $array['b']['x']['z'] given a string b.x.z:

function get($array, $path) {     //$path = explode('.', $path); //if needed     $path = "['" . implode("']['", $path) . "']";     eval("\$result = \$array{$path};");      return $result; } 

Sets something like $array['b']['x']['z'] = 'some value';:

function set(&$array, $path, $value) {     //$path = explode('.', $path); //if needed     $path = "['" . implode("']['", $path) . "']";     eval("\$array{$path} = $value;"); } 

Unsets something like $array['b']['x']['z']:

function unsetter(&$array, $path) {     //$path = explode('.', $path); //if needed     $path = "['" . implode("']['", $path) . "']";     eval("unset(\$array{$path});"); } 
like image 190
AbraCadaver Avatar answered Sep 21 '22 18:09

AbraCadaver


I have solution for you not in the pure PHP, but using ouzo goodies concretely Arrays::getNestedValue method:

$arr = array('a' => 1,     'b' => array(         'y' => 2,         'x' => array('z' => 5, 'w' => 'abc')     ),     'c' => null);  $key = 'b.x.z'; $path = explode('.', $key);  print_r(Arrays::getNestedValue($arr, $path)); 

Similarly if you need to set nested value you can use Arrays::setNestedValue method.

$arr = array('a' => 1,     'b' => array(         'y' => 2,         'x' => array('z' => 5, 'w' => 'abc')     ),     'c' => null);  Arrays::setNestedValue($arr, array('d', 'e', 'f'), 'value'); print_r($arr); 
like image 29
Piotr Olaszewski Avatar answered Sep 21 '22 18:09

Piotr Olaszewski