26

I can't seem to find a simple, straight-forward solution to the age-old problem of removing empty elements from arrays in PHP.

My input array may look like this:

Array ( [0] => Array ( [Name] => [EmailAddress] => ) ) 

(And so on, if there's more data, although there may not be...)

If it looks like the above, I want it to be completely empty after I've processed it.

So print_r($array); would output:

Array ( )

If I run $arrayX = array_filter($arrayX); I still get the same print_r output. Everywhere I've looked suggests this is the simplest way of removing empty array elements in PHP5, however.

I also tried $arrayX = array_filter($arrayX,'empty_array'); but I got the following error:

Warning: array_filter() [function.array-filter]: The second argument, 'empty_array', should be a valid callback

What am I doing wrong?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Chuck Le Butt
  • 47,570
  • 62
  • 203
  • 289
  • array_filter is set for 1D arrays. – Jirka Kopřiva Mar 27 '12 at 18:14
  • Can you give sample input, with expected output please? There are a few different ways to interpret your question, *"If it looks like the above, I want it to be completely empty after I've processed it."* is throwing me off, do you want it to be totally gone or to be an empty array? – Wesley Murch Mar 27 '12 at 18:19
  • This is a multi-dimensional array. array_filter only works in one dimension. You need to iterate through your array and apply array_filter to each iteration first. – Nilpo Mar 27 '12 at 18:20
  • @Madmartigan I don't want the entire array unset, I just want it to be completely empty. – Chuck Le Butt Mar 27 '12 at 18:24
  • 2
    Your new error is because `empty_array` is not a defined function. I also wonder what your real use case is here... – Wesley Murch Mar 27 '12 at 18:39
  • This is not disruptive trolling. I have spent a fair amount of time analyzing the answers and writing my own methods, so that future readers don't have to waste time experimenting with the different methods. See this demo for comparing the other volunteers' answers: http://sandbox.onlinephpfunctions.com/code/be9de9f471997331f248e29282dbd2bc5310777d – mickmackusa Oct 17 '17 at 02:40
  • 1
    @mickmackusa The correct answer was in the comments below the accepted question. Not sure why Wesley didn't pull it into his answer, but I've added it now. – Chuck Le Butt Oct 19 '17 at 14:25
  • @mickmackusa Here's your code with the accepted answer: http://sandbox.onlinephpfunctions.com/code/038c0f684908d595029df410739599e4b0b2cc51 – Chuck Le Butt Oct 19 '17 at 14:29
  • 1
    Would you be offended if the title was changed to: `Remove empty elements then empty subarrays from a 2d array`? This clarification, IMO, is important because duplicate page closures can be better targeted this way. – mickmackusa Oct 20 '17 at 22:11

8 Answers8

54

Try using array_map() to apply the filter to every array in $array:

$array = array_map('array_filter', $array);
$array = array_filter($array);

Demo: http://codepad.org/xfXEeApj

Chuck Le Butt
  • 47,570
  • 62
  • 203
  • 289
Wesley Murch
  • 101,186
  • 37
  • 194
  • 228
  • 1
    Wait wait, did you want to remove the entire array only if *all* items are empty? That's not what this does. Can you give sample input, with expected output please? – Wesley Murch Mar 27 '12 at 18:17
  • According to your question and the clarification in the comments, yes it should: http://codepad.org/FdfY5aqj When all the empty items get removed, you end up with an empty array. If this isn't what you want, please give some sample input with expected output. Are you concerned with the side effect of removing empties from the other arrays? – Wesley Murch Mar 27 '12 at 18:28
  • It sort of works. It still keeps empty keys. So if `[0][EmailAddress]` is empty, `[0]` still exists. That said, it seems to work with what I need it to, so thanks! – Chuck Le Butt Mar 27 '12 at 18:54
  • Well that's not an empty array, it's an array with keys in it. I don't know how else to describe an empty array! Like an array that was declared as: `$array = new array();` – Chuck Le Butt Mar 28 '12 at 20:14
  • You've only asked for input and output examples, which I've given you (see the original question). Your most recent code sample is exactly what I was after, though. Thanks very much! – Chuck Le Butt Mar 30 '12 at 10:45
  • Worked great for me! Thank you! – Sam Feb 16 '18 at 16:57
  • 1
    this doesnt work if $array contains a simple value like $array['number'] = '234'; you will get a warning in the array_map and a wrong result – Pablo Pazos Apr 16 '19 at 17:19
  • @PabloPazos is correct, the first line will set all non-array values to null as it generates the warning `array_filter() expects parameter 1 to be array, string given in...`, and then the second line will remove them, so this is not ideal if you have an array like this: `['element1', 'element2', ['subelement1', 'subelement2'] ]`, which then will turn into this: `[ ['subelement1', 'subelement2'] ]` – Smithee Feb 07 '22 at 14:22
  • @Smithee good explanation, that is why I downvoted this answer. – Pablo Pazos Feb 09 '22 at 18:27
6

There are numerous examples of how to do this. You can try the docs, for one (see the first comment).

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

    return $array;
}

Granted this example doesn't actually use array_filter but you get the point.

jeremyharris
  • 7,884
  • 22
  • 31
  • This code was not able to unset the deepest array of 0 length, and failed to follow up and clear the triple-nested structure it was in – CodedMonkey Jan 23 '14 at 20:27
5

Following up jeremyharris' suggestion, this is how I needed to change it to make it work:

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

   return $array;
}
Manish Kumar
  • 397
  • 3
  • 11
CodedMonkey
  • 458
  • 1
  • 7
  • 17
5

The accepted answer does not do exactly what the OP asked. If you want to recursively remove ALL values that evaluate to false including empty arrays then use the following function:

function array_trim($input) {
    return is_array($input) ? array_filter($input, 
        function (& $value) { return $value = array_trim($value); }
    ) : $input;
}

Or you could change the return condition according to your needs, for example:

{ return !is_array($value) or $value = array_trim($value); }

If you only want to remove empty arrays. Or you can change the condition to only test for "" or false or null, etc...

Alain
  • 51
  • 1
  • 1
  • Please note that from PHP7, the ability to pass by reference in array_filter has been removed (annoyingly), and now generates a warning from PHP8. – Smithee Feb 07 '22 at 15:10
5

Try with:

$array = array_filter(array_map('array_filter', $array));

Example:

$array[0] = array(
   'Name'=>'',
   'EmailAddress'=>'',
);
print_r($array);

$array = array_filter(array_map('array_filter', $array));

print_r($array);

Output:

Array
(
    [0] => Array
        (
            [Name] => 
            [EmailAddress] => 
        )
)

Array
(
)
joseantgv
  • 1,943
  • 1
  • 26
  • 34
4

array_filter() is not type-sensitive by default. This means that any zero-ish, false-y, null, empty values will be removed. My links to follow will demonstrate this point.

The OP's sample input array is 2-dimensional. If the data structure is static then recursion is not necessary. For anyone who would like to filter the zero-length values from a multi-dimensional array, I'll provide a static 2-dim method and a recursive method.

Static 2-dim Array: This code performs a "zero-safe" filter on the 2nd level elements and then removes empty subarrays: (See this demo to see this method work with different (trickier) array data)

$array=[
    ['Name'=>'','EmailAddress'=>'']
];   

var_export(
    array_filter(  // remove the 2nd level in the event that all subarray elements are removed
        array_map(  // access/iterate 2nd level values
            function($v){
                return array_filter($v,'strlen');  // filter out subarray elements with zero-length values
            },$array  // the input array
        )
    )
);

Here is the same code as a one-liner:

var_export(array_filter(array_map(function($v){return array_filter($v,'strlen');},$array)));

Output (as originally specified by the OP):

array (
)

*if you don't want to remove the empty subarrays, simply remove the outer array_filter() call.


Recursive method for multi-dimensional arrays of unknown depth: When the number of levels in an array are unknown, recursion is a logical technique. The following code will process each subarray, removing zero-length values and any empty subarrays as it goes. Here is a demo of this code with a few sample inputs.

$array=[
    ['Name'=>'','Array'=>['Keep'=>'Keep','Drop'=>['Drop2'=>'']],'EmailAddress'=>'','Pets'=>0,'Children'=>null],
    ['Name'=>'','EmailAddress'=>'','FavoriteNumber'=>'0']
];

function removeEmptyValuesAndSubarrays($array){
   foreach($array as $k=>&$v){
        if(is_array($v)){
            $v=removeEmptyValuesAndSubarrays($v);  // filter subarray and update array
            if(!sizeof($v)){ // check array count
                unset($array[$k]);
            }
        }elseif(!strlen($v)){  // this will handle (int) type values correctly
            unset($array[$k]);
        }
   }
   return $array;
}

var_export(removeEmptyValuesAndSubarrays($array));

Output:

array (
  0 => 
  array (
    'Array' => 
    array (
      'Keep' => 'Keep',
    ),
    'Pets' => 0,
  ),
  1 => 
  array (
    'FavoriteNumber' => '0',
  ),
)

If anyone discovers an input array that breaks my recursive method, please post it (in its simplest form) as a comment and I'll update my answer.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • 1
    Thanks for recursive function without removing zero value from multidimensional array. – Rahul Apr 26 '20 at 17:21
2

When this question was asked, the latest version of PHP was 5.3.10. As of today, it is now 8.1.1, and a lot has changed since! Some of the earlier answers will provide unexpected results, due to changes in the core functionality. Therefore, I feel an up-to-date answer is required. The below will iterate through an array, remove any elements that are either an empty string, empty array, or null (so false and 0 will remain), and if this results in any more empty arrays, it will remove them too.

function removeEmptyArrayElements( $value ) {
    if( is_array($value) ) {
        $value = array_map('removeEmptyArrayElements', $value);
        $value = array_filter( $value, function($v) {
            // Change the below to determine which values get removed
            return !( $v === "" || $v === null || (is_array($v) && empty($v)) );
        } );
    }
    return $value;
}

To use it, you simply call removeEmptyArrayElements( $array );

Smithee
  • 710
  • 7
  • 14
1

If used inside class as helper method:

private function arrayFilterRecursive(array $array): array 
{
    foreach ($array as $key => &$value) {
        if (empty($value)) {
            unset($array[$key]);
        } else if (is_array($value)) {
            $value = self::arrayFilterRecursive($value);
        }
    }

    return $array;
}
RomkaLTU
  • 3,683
  • 8
  • 40
  • 63