简体   繁体   中英

How to replace values in a nested array with array_walk?

How to replace values in a nested array with array_walk?

This is my array,

$item = array(
    "id" => "2",
    "title" => "parent 2",
    "children" => array (
           "id" => "4",
           "title" => "children 1"
        )
);

//replacement array:
$replace = [
  '2' => 'foo',
  '4' => 'bar',
  '7' => 'baz'
];

My working function,

function myfunction(&$value,$key,$replace)
{   

    if(isset($replace[$key]))
    {
       $value = $replace[$key];
    }

    if(is_array($key))
    {
        array_walk($value,"myfunction",$replace);
    }
}

array_walk($item,"myfunction",$replace);

print_r($item);

result,

Array
(
    [id] => 2
    [title] => parent 2
    [children] => Array
        (
            [id] => 4
            [title] => children 1
        )

)

The result I'm after,

Array
(
    [id] => 2
    [title] => foo
    [children] => Array
        (
            [id] => 4
            [title] => bar
        )

)

This recursive function may help you

function replace($item,$replace)
{
    foreach ($replace as $key => $value) 
    {
        if($item["id"]==$key)
            $item["title"] = $value;
        if(isset($item['children']))
            $item['children'] = replace($item['children'],$replace);
    }
    return $item;
}

it doesn't modify $item, it returns it modified, so you need to call it like this

$item = replace($item,$replace);

In order to make your function modify the argument $item, just pass it by reference like this :

function replace(&$item,$replace)
{
    foreach ($replace as $key => $value) 
    {
        if($item["id"]==$key)
            $item["title"] = $value;
        if(isset($item['children']))
            replace($item['children'],$replace);
    }
}

In this case you just need to call the function like this :

replace($item,$replace);
print_r($item);

This may or may not feel like a little unorthodox. What my method does is remove the trouble of dealing with a multi-dimensional array by flattening it into a very predictable string using json_encode() , then replaces the correct values using preg_replace() , before using json_decode() to generate the desired associative array. I am sure you'll find it very simple to follow the procedure.

Inputs:

$item = array(
    "id" => "2",
    "title" => "parent 2",
    "children" => array ( "id" => "4", "title" => "children 1" )
);
$replace=['2'=>'foo','4'=>'bar','7'=>'baz'];

Code ( Demo ):

foreach($replace as $k=>$v){
    $pattern[]="/(?<=\"id\":\"$k\",\"title\":\")[^\"]*/";
}
$item=json_encode($item);
$item=preg_replace($pattern,$replace,$item);
var_export(json_decode($item,true));

Output:

array (
  'id' => '2',
  'title' => 'foo',
  'children' => 
  array (
    'id' => '4',
    'title' => 'bar',
  ),
)

Whenever someone uses this technique to modify an array's keys or values, it is absolutely crucial that the substring that is targeted for replacement is NOT POSSIBLY confused for another substring. Fortunately for your input array, the structure is very clean and predictable.

That said, the pattern that I have written matches the json syntax from id key to id value to title key before matching between the double quotes of the title value. This is going to be a very trustworthy pattern for your case.

If you are curious about what the regex patterns look like in the $pattern array:

array (
  0 => '/(?<="id":"2","title":")[^"]*/',  # \
  1 => '/(?<="id":"4","title":")[^"]*/',  # --- Mostly positive lookbehind, then match
  2 => '/(?<="id":"7","title":")[^"]*/',  # /
)

Now, if you are brave enough to stack my functions together to use fewer lines of code and avoid extra declaring of $item , this is the same method which produces the same output in just two-steps:

Code ( Demo ):

foreach($replace as $k=>$v){
    $pattern[]="/(?<=\"id\":\"$k\",\"title\":\")([^\"]*)/";
}
var_export(json_decode(preg_replace($pattern,$replace,json_encode($item)),true));

Try this function :

    function replaceKey($subject, $newKey, $oldKey) {

    if (!is_array($subject)) return $subject;

    $newArray = array(); // empty array to hold copy of subject
    foreach ($subject as $key => $value) {

        // replace the key with the new key only if it is the old key
        $key = ($key === $oldKey) ? $newKey : $key;

        // add the value with the recursive call
        $newArray[$key] = $this->replaceKey($value, $newKey, $oldKey);
    }
    return $newArray;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM