如何创建一个递归函数来取消PHP中的多维数组?

问题描述:

Below is my target array which I would like to unset its elements by key based on the candidate array element.

$target = [
    60 => "Home"
    "Villa" => [
        "30" => "Vi",
    ],
    70 => "A",
    40 => "B",
    50 => "C",
    "Land" => [
        1 => "La",
        35 => "Lb",
        37 => "Lc",
        39 => "Ld",
    ],
];

$candidate = [30, 50, 35, 37];

Below is the result that I want after unsetting.

$target = [
    60 => "Home"
    70 => "A",
    40 => "B",
    "Land" => [
        1 => "La",
        39 => "Ld",
    ],
];

'Villa' must also be gone because it's empty after it's element "30" => "Vi" has been unset.

Below my solution in for-loop.

foreach ($target as $id => $option) {
    if (isset($candidate[$id])) {
      unset($target[$id]);
    }
    elseif (is_array($option)) {
      foreach ($option as $sub_id => $opt) {
        if (isset($candidate[$sub_id])) {
          unset($target[$id][$sub_id]);
        }
      }
    }

    if (!count($target[$id])) {
      unset($target[$id]);
    }
}

How can I replace this for-loop in a recursive solution?

function del($target, $candidate) {
    foreach ($target as $key => $value) {
        if (in_array($key, $candidate)) {
            unset($target[$key]);
        } elseif (is_array($value)) {
            $target[$key] = del($value, $candidate);
            if (!count($target[$key])) {
                unset($target[$key]);
            }
        }
    }
    return $target;
}

$new = del($target, $candidate);
var_dump($new);

EDIT:

<?php
$target = [
    60 => "Home",
    "Villa" => [
        "30" => "Vi",
    ],
    70 => "A",
    40 => "B",
    50 => "C",
    "Land" => [
        1 => "La",
        35 => "Lb",
        37 => "Lc",
        39 => "Ld",
    ],
];

$candidate = [30, 50, 35, 37];

function clean(&$target, $candidate){
    // Loop through target
    foreach($target as $index => $value){
        // If the value is an array
        if(is_array($value)){
            // Clean it first
            $result = clean($value, $candidate);
            $target[$index] = $result;
        } else {
            // Check if the key is in the candidate array
            if(in_array($index, $candidate)){
                $target[$index] = NULL;
            }
        }
        // If the value is empty
        if(empty($target[$index])){
            // Unset it
            unset($target[$index]);
        }
    }
}

$target = clean($target, $candidate);

var_dump($target);

Result:

/var/www/test.php:47:
array (size=4)
  60 => string 'Home' (length=4)
  70 => string 'A' (length=1)
  40 => string 'B' (length=1)
  'Land' => 
    array (size=2)
      1 => string 'La' (length=2)
      39 => string 'Ld' (length=2)

As symcbean mentioned using reference here would be better

This started as a comment on Mehdi's answer, but I ran out of space:

While I believe arrays are passed by reference, and there should be minimal overhead assigning the reference to the variable it came from, it strikes me that explicitly passing by reference might be more robust and transparent.

i.e.

function clean(&$target, $candidate, $depth=0){
   if (++$depth>10) {
      // erk!
      trigger_error("Too deep!");
   }
   // Loop through candidates
   foreach($candidate as $index){
       // If the value is an array
       if(isset($target[$index]) && is_array($target[$index])){
           clean($target[$index], $candidate, $depth);
           if (!count($target[$index])) unset($target[$index]); // thanks jhilgeman
       } else {
           isset($target[$index]) && unset($target[$index]);
       }
   }
}

clean($target, $candidate);
var_dump($target);