Pragmatism in the real world

Recursively deleting elements from an array

I had a need recently to delete items from a nested associative array and also any empty sub-arrays. My initial thought was to use array_walk_recursive, but this doesn’t work as you can’t unset nested elements and you only have access to the leaves. Clearly I needed a recursive function.

I’m sure that this has been done many times before, but this is my solution:

/**
 * Remove any elements where the callback returns true
 *
 * @param  array    $array    the array to walk
 * @param  callable $callback callback takes ($value, $key, $userdata)
 * @param  mixed    $userdata additional data passed to the callback.
 * @return array
 */
function array_walk_recursive_delete(array &$array, callable $callback, $userdata = null)
{
    foreach ($array as $key => &$value) {
        if (is_array($value)) {
            $value = array_walk_recursive_delete($value, $callback, $userdata);
        }
        if ($callback($value, $key, $userdata)) {
            unset($array[$key]);
        }
    }

    return $array;
}

with this test:

class FunctionsTest extends \PHPUnit_Framework_TestCase
{
    public function testArrayWalkRecursiveDelete()
    {
        $array = [
            'a'=> 'a',
            'b'=> null,
            'c' => [
                'a' => null,
                'b' => 'b',
            ],
            'd' => [
                'a' => null
            ]
        ];

        $result = array_walk_recursive_delete($array, function ($value, $key) {
            if (is_array($value)) {
                return empty($value);
            }
            return ($value === null);
        });

        $expected = [
            'a'=> 'a',
            'c' => [
                'b' => 'b',
            ],
        ];

        $this->assertSame($expected, $result);
    }
}

This is very similar to how array_walk_recursive works except that I return the altered array rather than a boolean as it’s a recursive function.

The test shows how I use it:

        $result = array_walk_recursive_delete($array, function ($value, $key) {
            if (is_array($value)) {
                return empty($value);
            }
            return ($value === null);
        });

If the callback returns true, then the element is deleted from the array, so for my case, I return true if the value is an empty array or null.

3 thoughts on “Recursively deleting elements from an array

Comments are closed.