23

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...

<?php

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

?>

Ok, 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?

Best Regards,

Sorry for my english...

André
  • 24,706
  • 43
  • 121
  • 178

9 Answers9

43

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));
Alessandro Desantis
  • 14,098
  • 1
  • 26
  • 32
  • 2
    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. – arunas_t Dec 17 '13 at 15:54
  • Always use \_\_FUNCTION__ in recursive calls to make your function portable. – Alex Skrypnyk Sep 08 '15 at 10:10
  • 2
    @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**. – LSerni Oct 28 '15 at 13:02
2

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.

Vedmant
  • 2,265
  • 1
  • 27
  • 36
2
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 => "   ",
   ]
DEV Tiago França
  • 1,271
  • 9
  • 9
0

I think this should solve your problem.

$retArray =array_filter($array, arrayFilter);

function arrayFilter($array) {
     if(!empty($array)) {
         return array_filter($array);
     }
}
Thyagi
  • 190
  • 1
  • 6
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

Mike Doe
  • 16,349
  • 11
  • 65
  • 88
0

Simple and elegant solution based on recursive function. Dont liked any solution from the prev. answers. Removes empty sub arrays/sub values:

/**
 * Clean array from empty values
 * @param $array
 * @return array|mixed
 */
function array_filter_recursive($array){
    if (!is_array($array)) return $array;
    foreach ( $array as $key=>$item){
        if (is_array($item))
            $array[$key] = array_filter_recursive($item);
    }
    return array_filter($array);
}
CETb
  • 344
  • 1
  • 3
  • 11
0

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) {
        if (is_array($value)) {
            $value = recursivelyRemoveEmpties($value);
            if (!$value) {
                unset($array[$key]);
            }
            continue;
        }
        if ($value === '') {
            unset($array[$key]);
        }
    }

    return $array;
}

var_export(recursivelyRemoveEmpties($raw));

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

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
-1

My function:

function removeEmptyItems($item)
{
    if (is_array($item)) {
        $item = array_filter($item, 'removeEmptyItems');
    }
    return !empty($item);
}

$nonEmpty = array_filter($raw, 'removeEmptyItems');
JustAC0der
  • 2,871
  • 3
  • 32
  • 35
-2

If you want array_filter to work recursively, you'll need to make sure that the subsequent calls may edit the deeper nested items of the array. Short: You'll need to pass it by reference:

function removeEmptyItems(&$item) {
    if (is_array($item) && $item) {
        $item = array_filter(&$item, 'removeEmptyItems');
    }

    return !!$item;
}
rodneyrehm
  • 13,442
  • 1
  • 40
  • 56