1

I want to be able to remove elements from the multidimensional array by key

Example of such an array can be:

Array
(
    [data] => Array
        (
            [todo] => code review
            [schedule] => Array
                (
                    [endDate] => 2019-05-10T00:00:00+01:00
                    [startDate] => 2019-05-09T00:00:00+01:00
                )

            [codeDetails] => Array
                (
                    [language] => PHP
                    [type] => class
                    [abstract] => true
                    [methodCount] => Array
                        (
                            [public] => 3
                            [protected] =>
                            [private] => 1
                        )

                    [LOC] => 123
                    [cyclomaticComplexity] => 4
                    [author] => Array (
                            [name] => Lukasz
                            [email] => [email protected]
                        )
                )

        )
)

I have two methods, recursiveArrayDelete removes any elements where the callback returns true:

private function recursiveArrayDelete(array &$array, callable $callback): array
{
    foreach ($array as $key => &$value) {
        if (is_array($value)) {
            $value = $this->recursiveArrayDelete($value, $callback);
        }
        if ($callback($value, $key)) {
            unset($array[$key]);
        }
    }

    return $array;
}

Second, removes properties all included in restrictedProperties from the array:

private function sanitizedArray(array &$array, array &$restrictedProperties): array
{
    foreach ($restrictedProperties as $restrictedProperty) {
        $this->recursiveArrayDelete(
            $array,
             static function () use ($array, $restrictedProperty): bool {
                array_walk_recursive(
                    $array,
                    static function ($value, $key) use (&$bool, $restrictedProperty) {
                         // here $bool is as expected from condition
                         $bool = $key === $restrictedProperty;
                     });
                  // here is always false
                 return $bool; 
             });
    }

    return $array;
}

Example of usage:

$this->sanitizedResponse($data, ['methodCount', `endDate`]);

Should remove these elements from array. But as I mentioned in commenrt sanitizedArray where return $bool; always result in false.

4
  • 2
    It seems $bool will only ever compare the last key in the array with the restricted property? Commented May 10, 2019 at 12:27
  • @Nick You are right, do you have an idea how can I implement it correctly? Commented May 10, 2019 at 12:33
  • 1
    I think your callback is too complicated, it's attempting to recurse where you are already doing that in the recursiveArrayDelete function. It probably just needs to compare the key with values in the restrictedProperty array. Unfortunately I don't have time to look at it in more detail right now. Commented May 10, 2019 at 12:38
  • Why do you need the $value in the callback function? Commented May 10, 2019 at 12:52

2 Answers 2

2

@dWinder's answer is almost perfect. But when I played around with it a bit more I noticed that it would not delete an array under a given "restricted property".

Let us assume that all "D"-elements were to be removed:

dWinder's solution would result in:

Array
(
    [A] => aa
    [B] => Array
        (
            [A] => aaa
            [B] => bb
        )

    [C] => cc
    [D] => Array
        (
            [E] => ee
        )

)

The array under "D" is still there. But when you switch the if condition and action with that of the else if you get the (assumed!) desired result:

Array
(
    [A] => aa
    [B] => Array
        (
            [A] => aaa
            [B] => bb
        )

    [C] => cc
)

So, the improved function code should look something like this (I am quoting parts of @dWinder's solution here):

$arr = array("A" => "aa", "B" => ["A" => "aaa", "B" => "bb"], "C" => "cc", "D" => ["E" =>"ee"]);

function array_filter_recursive(array &$array, callable $callback) {
    foreach ($array as $key => &$value) {
        if ($callback($key, $value))   unset($array[$key]);
        else if (is_array($value))     array_filter_recursive($value, $callback);
    }
}

function sanitizedArray(&$arr, $restrictedProperties) {
    foreach($restrictedProperties as $prop) {
        array_filter_recursive($arr, function($k, $v) use ($prop) {return $prop == $k;});
    }
}
sanitizedArray($arr, ["D"]);
print_r($arr);

See here for a demo: https://3v4l.org/05tc7

Sign up to request clarification or add additional context in comments.

1 Comment

Nice catch - you may take my answer and add it to yours so this question will have 1 full post for solution
2

You done very well. Notice you have 2 issues: first you doing the recursion in both function and you reassign to $bool - so only the last compare is the one that count (this is why you getting false all the time).

I would make your code more simpler this way:

function array_filter_recursive(array &$array, callable $callback) {
    foreach ($array as $key => &$value) {
        if ($callback($key, $value)) unset($array[$key]);
        else if (is_array($value)) array_filter_recursive($value, $callback);
    }
}

function sanitizedArray(&$arr, $restricted, $keys = false) {
    foreach($restricted as $val) {
        array_filter_recursive($arr, function($k, $v) use ($val, $keys) {return $keys ? ($val === $k) : ($val === $v);});
    }
}

Now you can simply use it as your example: $this->sanitizedResponse($data, ['methodCount', 'endDate'], true);

Here is live example with fake data: 3v4l

3 Comments

@ŁukaszD.Tulikowski - there was a bug in my post - cars10m found it. Updated my answer
@dWinter @cars10m I think this nicer here array_filter_recursive($arr, function($k, $v) use ($val, $keys) {return $keys ? ($val === $k) : ($val === $v);}); Could we edit/merge both solutions?
@ŁukaszD.Tulikowski update my answer (include the live example). I will leave car10m to update his

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.