24

I need to remove empty entries on multilevel arrays. For now I can remove entries with empty sub-arrays, but not empty arrays... confused, so do I... I think the code will help to explain better.

/**
 * 
 * This function remove empty entries on arrays
 * @param array $array
 */
function removeEmptysFromArray($array) {
    
    $filtered = array_filter($array, 'removeEmptyItems');
    return $filtered;
}

/**
 * 
 * This is a Callback function to use in array_filter()
 * @param array $item
 */
function removeEmptyItems($item) {
    
    if (is_array($item)) {
        return array_filter($item, 'removeEmptyItems');
    }
 
    if (!empty($item)) {
        return true;  
    }
}


$raw = array(
    'firstname' => 'Foo',
    'lastname'  => 'Bar',
    'nickname' => '',
    'birthdate' => array( 
        'day'   => '',
        'month' => '',
        'year'  => '',
    ),
    'likes' => array(
        'cars'  => array('Subaru Impreza WRX STi', 'Mitsubishi Evo', 'Nissan GTR'),
        'bikes' => array(),
    ),
);

print_r(removeEmptysFromArray($raw));

This code will remove nickname, birthdate but is not removing bikes that have an empty array.

My question is... How to remove the bikes entry?

1
  • 1
    You could clear up some ambiguity in your requirements by more explicitly defining what "empty" means. Is false empty? Is 0 empty? Is '0' empty? Your sample input only contains empty strings, so fringe case expectations are not well defined. Commented Apr 25 at 20:44

6 Answers 6

46

Try this code:

<?php
function array_remove_empty($haystack)
{
    foreach ($haystack as $key => $value) {
        if (is_array($value)) {
            $haystack[$key] = array_remove_empty($haystack[$key]);
        }

        if (empty($haystack[$key])) {
            unset($haystack[$key]);
        }
    }

    return $haystack;
}

$raw = array(
    'firstname' => 'Foo',
    'lastname'  => 'Bar',
    'nickname' => '',
    'birthdate' => array(
        'day'   => '',
        'month' => '',
        'year'  => '',
    ),
    'likes' => array(
        'cars'  => array('Subaru Impreza WRX STi', 'Mitsubishi Evo', 'Nissan GTR'),
        'bikes' => array(),
    ),
);

print_r(array_remove_empty($raw));
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, just used it. One addition: if you want to remove "whitespaced" strings in array values also, then add a one little "elseif" clause: if (is_array($value)) { $haystack[$key] = array_remove_empty($haystack[$key]); } elseif(is_string($haystack[$key])) { $haystack[$key] = trim($value); } that way array(array(" ", ' ')) will be returned as empty also.
Always use __FUNCTION__ in recursive calls to make your function portable.
@ThanhTrung , he's calling array_remove_empty from inside array_remove_empty. If he were to rename the function, there would no longer be an array_remove_empty function to call. If the internal call was done by assigning $fn = __FUNCTION__ and then calling $fn, it would work whatever the function was named. An alternative of course is be careful when you rename functions and ensure you replaced all the proper function name's occurrences. Especially if you rename a function to avoid name clashes, when the function will still exist, but as a completely different function.
NOTE: be careful with empty($haystack[$key]) since it not only removes empty strings or null values, but also: 0 (integer) "0" (zero string) false (boolean) true (boolean). w3schools.com/php/func_var_empty.asp
@Vitor it is an untruth to say that empty() removes ANYTHING. empty() certainly evaluates false and true differently.
3

Here is my solution, it will remove exactly specified list of empty values recursively:

/**
 * Remove elements from array by exact values list recursively
 *
 * @param array $haystack
 * @param array $values
 *
 * @return array
 */
function array_remove_by_values(array $haystack, array $values)
{
    foreach ($haystack as $key => $value) {
        if (is_array($value)) {
            $haystack[$key] = array_remove_by_values($haystack[$key], $values);
        }

        if (in_array($haystack[$key], $values, true)) {
            unset($haystack[$key]);
        }
    }

    return $haystack;
}

You can use it like this:

$clean = array_remove_by_values($raw, ['', null, []]);

Note, it removes empty sub arrays if you pass [] as one of values.

Comments

1
array_filter(explode('/', '/home/teste sdsd/   /'), 'trim');
//Result
[
     1 => "home",
     2 => "teste sdsd",
]

//-----------
array_filter(explode('/', '/home/teste sdsd/   /'), 'strlen')
//Result
  [
     1 => "home",
     2 => "teste sdsd",
     3 => "   ",
   ]

1 Comment

This answer doesn't even attempt to answer the asked question.
1

array_filter() and empty() may be safe to leverage for the asker's sample input, but they are not reliable for public general use. These native functions will treat all "falsey" values as filterable/empty. See the documentation for empty() for details on its greedy determination of emptiness.

I recommend explicitly checking (data type specifically) for empty string strings if that is the intent. This way you don't inadvertently destroy meaningful data. I am choosing to declare $value as a reference to avoid iterating a copy of the array -- this allows me to mutate/overwrite array data with $value instead of the more verbose $array[$key] (when not using unset()).

Code: (Demo with an assortment of falsey values)

function recursivelyRemoveEmpties(array $array): array
{
    foreach ($array as $key => &$value) {  // modify by reference to affect original array
        if (is_array($value)) {
            $value = recursivelyRemoveEmpties($value);
            if (!$value) {  // if the array is empty
                unset($array[$key]);
            }
            continue;
        }
        if ($value === '') { // add additional qualifying conditions here only
            unset($array[$key]);
        }
    }

    return $array;
}

var_export(recursivelyRemoveEmpties($raw));

*Note that this script is not designed to accommodate objects in the data payload.

2 Comments

empty is not: if ($value === '')... You have function in php empty(), its incluides null, false and empty string
I already said that in my answer. I know exactly how empty() works (and rarely use it because of how it works). In this context removing zeros and falses is probably harmful when trying to remove "empty/missing" values. See my demo.
0

I think this should solve your problem.

$retArray =array_filter($array, arrayFilter);

function arrayFilter($array) {
     if(!empty($array)) {
         return array_filter($array);
     }
}

Comments

0

Recursively clear multidimensional array of empty'ish items:

final class ArrayCleaner
{
    public static function clean(array $value): array
    {
        foreach ($value as $k => $v) {
            if (\is_array($v)) {
                $value[$k] = self::clean($v);

                if (0 == \count($value[$k])) {
                    unset($value[$k]);
                }
            } elseif (empty($v)) {
                unset($value[$k]);
            }
        }

        return $value;
    }
}

Unit test:

final class ArrayCleanerTest
{
    public function testItCleans(): void
    {
        $input = [
            'empty_string_to_remove' => '',
            'empty_int_to_remove' => 0,
            'empty_string_number_to_remove' => '0',
            'value_to_keep' => 5,
            'empty_array_to_remove' => [],
            'empty_array_of_empty_arrays_to_remove' => [
                'one' => [],
                'two' => [],
                'three' => [false, null, '0'],
            ],
            'array_to_keep_1' => [
                'value' => 1,
            ],
            'array_to_keep_2' => [
                'empty_to_remove' => [],
                'array_to_keep' => ['yes'],
            ],
        ];

        $expected = [
            'value_to_keep' => 5,
            'array_to_keep_1' => [
                'value' => 1,
            ],
            'array_to_keep_2' => [
                'array_to_keep' => ['yes'],
            ],
        ];

        $this->assertEquals($expected, ArrayCleaner::clean($input));
    }
}

Working proof of concept at 3v4l.org

1 Comment

empty() should not be used on a variable which is guaranteed to be declared.

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.