0

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?

4
  • Is there any specific reason you want to use recursion? Commented Jan 12, 2018 at 15:21
  • My multidimensional array might go deeper than this in the future. Commented Jan 12, 2018 at 15:25
  • Why are you not unsetting the whole array with unset($target)? Commented Jan 12, 2018 at 15:30
  • Then I will get empty $target as result? I only want to unset the elements based on the $candidate array. Commented Jan 12, 2018 at 15:31

3 Answers 3

3
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);
Sign up to request clarification or add additional context in comments.

Comments

3

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);

5 Comments

It is actually, I updated my code with reference and unsetting empty arrays, but here I'm staring at my computer frozen.
Sadly I wasn't able to get to my desk before someone answered this one, but this is what I would have written (using pass-by-reference). One thing that you need to also add is after the clean() line, check to see if $target[$index] is now empty and unset it if so. This addresses the requirement about "'Villa' must also be gone because it's empty after...". Example code to add: if(!count($target[$index])) { unset($target[$index]); }
Have a +1 for the empty array fix then :)
(there was also a bug - my code wasn't checking if the index was in $candidate before deleting!)
...and since we should be exempliying good programming practice, I've added a limit on the recursion
1

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

2 Comments

what does it mean "Using reference"?
I meant instead of copying the variable to the function you can just pass a reference to the variable you want to work with (I should've removed the return part from the function)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.