9

I have array like that

$arr = [
   'baz' => [
      'foo' => [
         'boo' => 'whatever'
     ]
   ]
];

Is there anyway to unset ['boo'] value using string input?

Something like that

    $str = 'baz->foo->boo';
    function array_unset($str, $arr) {

    // magic here

    unset($arr['baz']['foo']['boo']);
    return $arr;
    }

This answer was awesome, and it's made first part of my script run Using a string path to set nested array data . But It's can't reverse. P.S. eval() is not an option :(

Community
  • 1
  • 1
remtsoy
  • 465
  • 1
  • 3
  • 15

3 Answers3

12

Since you can't call unset on a referenced element, you need to use another trick:

function array_unset($str, &$arr)
{
    $nodes = split("->", $str);
    $prevEl = NULL;
    $el = &$arr;
    foreach ($nodes as &$node)
    {
        $prevEl = &$el;
        $el = &$el[$node];
    }
    if ($prevEl !== NULL)
        unset($prevEl[$node]);
    return $arr;
}

$str = "baz->foo->boo";
array_unset($str, $arr);

In essence, you traverse the array tree but keep a reference to the last array (penultimate node), from which you want to delete the node. Then you call unset on the last array, passing the last node as a key.

Check this codepad

huysentruitw
  • 27,376
  • 9
  • 90
  • 133
  • Damn... it's smart. BTW have 2 questions. 1) why you using split? simple explode work totally fine. 2) why you put $arr argument by reference? – remtsoy May 22 '13 at 18:33
  • 1
    1) using Qt/C# a lot last weeks, where they tend to use 'split', explode will work just fine idd. 2) you need to keep a reference to the original array at all times, otherwise unset will only remove the node from the copy instead of the original array. – huysentruitw May 22 '13 at 18:39
  • You can use $GLOBALS instead if you have the name of the array variable passed in the parameter. ( from the doc: _To unset() a global variable inside of a function, then use the $GLOBALS array to do_ ) – antoox May 22 '13 at 18:40
  • @antoox: using $GLOBALS expects your array to be defined at global scope, which I tend to avoid. The code in my answer is more portable and can easily be adopted to work in an OO approach. – huysentruitw May 22 '13 at 18:47
  • My $arr argument was taken from object. It can't changed by that function anyway... that's why I was confused about putting it by reference. In my opinion setting and unsetting global varibles kinda extra work witch not requires here – remtsoy May 22 '13 at 18:48
  • @remtsoy Yes, take the handling which suits you. It's just another method. – antoox May 22 '13 at 18:57
1

This one is similar to Wouter Huysentruit's. Difference is that it returns null if you pass on an invalid path.

function array_unset(&$array, $path)
{
    $pieces = explode('.', $path);
    $i = 0;
    while($i < count($pieces)-1) {
        $piece = $pieces[$i];
        if (!is_array($array) || !array_key_exists($piece, $array)) {
            return null;
        }
        $array = &$array[$piece];
        $i++;
    }
    $piece = end($pieces);
    unset($array[$piece]);
    return $array;
}
brunettdan
  • 997
  • 9
  • 7
0

Something on the fly that might work for you:

$str = 'bar,foo,boo';

function array_unset($str,&$arr) {
  $path = explode(',',$str);
  $buf = $arr;
  for($i=0;$i<count($path)-1;$i++) {
    $buf = &$buf[$path[$i]];
  }

  unset($buf);
}
Nick Andriopoulos
  • 10,313
  • 6
  • 32
  • 56
  • It doesn't work. From the [documentation](http://fr2.php.net/manual/en/function.unset.php): _If a variable that is PASSED BY REFERENCE is unset() inside of a function, only the local variable is destroyed. The variable in the calling environment will retain the same value as before unset() was called._ – antoox May 22 '13 at 17:56
  • Nope... It does not affect $arr itself. – remtsoy May 22 '13 at 17:59