Set value of an array using xpath notation

This is a continuation to my previous article on how to get values of multi-dimensional arrays using xpath notation.

This time I will show you how to set a value of an array using the same xpath notation (root/branch/leaf).

As always, let's begin with the code itself:

  1. <?php
  2.  
  3. /**
  4.  * Class adding the functionality to set array values of a path
  5.  */
  6. class ArrayObjectExt extends ArrayObject
  7. {
  8.  
  9.     // specify the delimiter
  10.     protected $_pathDelimiter = '/';
  11.  
  12.  
  13.     /**
  14.      * Set value of an array by using "root/branch/leaf" notation
  15.      *
  16.      * @param string $path Path to set
  17.      * @param mixed $value Value to set the target cell to
  18.      * @return ArrayObjectExt
  19.      */
  20.     public function setPathValue($path, $value)
  21.     {
  22.         // fail if the path is empty
  23.         if (empty($path)) {
  24.             throw new Exception('Path cannot be empty');
  25.         }
  26.  
  27.         // fail if path is not a string
  28.         if (!is_string($path)) {
  29.             throw new Exception('Path must be a string');
  30.         }
  31.  
  32.         // remove all leading and trailing slashes
  33.         $path = trim($path, $this->_pathDelimiter);
  34.  
  35.         // split the path in into separate parts
  36.         $parts = explode($this->_pathDelimiter, $path);
  37.  
  38.         // initially point to the root of the array
  39.         $pointer =& $this;
  40.  
  41.         // loop through each part and ensure that the cell is there
  42.         foreach ($parts as $part) {
  43.             // fail if the part is empty
  44.             if (empty($part)) {
  45.                 throw new Exception('Invalid path specified: ' . $path);
  46.             }
  47.  
  48.             // create the cell if it doesn't exist
  49.             if (!isset($pointer[$part])) {
  50.                 $pointer[$part] = array();
  51.             }
  52.  
  53.             // redirect the pointer to the new cell
  54.             $pointer =& $pointer[$part];
  55.         }
  56.  
  57.         // set value of the target cell
  58.         $pointer = $value;
  59.  
  60.         return $this;
  61.     }
  62.  
  63.  
  64.     /**
  65.      * Set path delimiter
  66.      *
  67.      * @param string $delimiter Delimiter used to split the path
  68.      * @return ArrayObjectExt
  69.      */
  70.     public function setPathDelimiter($delimiter)
  71.     {
  72.         $this->_pathDelimiter = (string) $delimiter;
  73.         return $this;
  74.     }
  75.  
  76. }

Note: you can combine code found in the previous article with this one to get the full functionality of setting and getting path values!

For those who don't want (or like) the OOP approach, here is a procedural solution to the same problem:

  1. <?php
  2.  
  3. /**
  4.  * Set value of an array by using "root/branch/leaf" notation
  5.  *
  6.  * @param array $array Array to affect
  7.  * @param string $path Path to set
  8.  * @param mixed $value Value to set the target cell to
  9.  * @return void
  10.  */
  11. function set_array_path_value(array &$array, $path, $value)
  12. {
  13.     // fail if the path is empty
  14.     if (empty($path)) {
  15.         throw new Exception('Path cannot be empty');
  16.     }
  17.  
  18.     // fail if path is not a string
  19.     if (!is_string($path)) {
  20.         throw new Exception('Path must be a string');
  21.     }
  22.  
  23.     // specify the delimiter
  24.     $delimiter = '/';
  25.  
  26.     // remove all leading and trailing slashes
  27.     $path = trim($path, $delimiter);
  28.  
  29.     // split the path in into separate parts
  30.     $parts = explode($delimiter, $path);
  31.  
  32.     // initially point to the root of the array
  33.     $pointer =& $array;
  34.  
  35.     // loop through each part and ensure that the cell is there
  36.     foreach ($parts as $part) {
  37.         // fail if the part is empty
  38.         if (empty($part)) {
  39.             throw new Exception('Invalid path specified: ' . $path);
  40.         }
  41.  
  42.         // create the cell if it doesn't exist
  43.         if (!isset($pointer[$part])) {
  44.             $pointer[$part] = array();
  45.         }
  46.  
  47.         // redirect the pointer to the new cell
  48.         $pointer =& $pointer[$part];
  49.     }
  50.  
  51.     // set value of the target cell
  52.     $pointer = $value;
  53. }

There isn't much to explain in this case. You should be able to understand everything by just reading the comments.

The main feature of this function/method is references (read more about them here). Initially we reference the array itself as we don't know anything about the path yet, however with every iteration we reset the pointer to the next level of our array.

As you can see, we loop through every part of the path and make sure that the specified indice exists on the current level. If it doesn't we set to an empty array (as most probably there will be another sublevel), or just don't do anything if it does.

Here's an example of the function usage:

  1. $array = array(
  2.     'a' => array(
  3.         'b' => array(
  4.             'd' => 123,
  5.         ),
  6.     ),
  7. );
  8.  
  9. set_array_path_value($array, '/a/b/c', 456);

The resulting array will look like this:

array(1) {
["a"]=>
array(1) {
["b"]=>
array(2) {
["d"]=>
int(123)
["c"]=>
int(456)
}
}
}

Usage of the OOP approach is similar (using $array defined previously):

  1. $a = new ArrayObjectExt($array);
  2. $a->setPathValue('/a/b/c', 456);

The output of

  1. var_dump((array) $a);

is exactly the same as in the procedural approach.

Hope this helps and put it to good use!

Comments
1
this is what I'm waiting for,
thanks Andris, you're the best! :)
bukak, October 18th 2011, 7:09
2
My pleasure :)
Andris, October 18th 2011, 13:07
3
Hey Andris,
I like this site very much and I just wanted to say a big "köszönöm" for all the useful code snippets I found here. Keep up the good work :)
balazsp, December 7th 2011, 12:04
4
Örömömre, balazsp! :)
Andris, December 9th 2011, 15:39
Name
Email (required)
will not be published
Website
Recaptcha
you will only be required to fill it in once in this session

You can use [code][/code] tags in your comments