PHP: How do you remove empty elements from an array?

array_filter($array) is the answer you find everywhere. It is short, it works — and it removes more than most people expect. The number zero, among other things.

The short answer

<?php
  $clean = array_values(array_filter($array, fn($v) => $v !== ''));
?>

A little longer than the usual array_filter($array), but it does exactly what it says. Why the differences matter I will show now.

What array_filter() without a callback really does

Without a callback, array_filter() keeps every element that is truthy. That is something other than “not empty”:

<?php
  $raw = ['a', '', 'b', null, 'c', 0, '0', false, [], 'd', '   '];
  $filtered = array_filter($raw);
?>
before : ["a","","b",null,"c",0,"0",false,[],"d","   "]
after  : {"0":"a","2":"b","4":"c","9":"d","10":"   "}

The complete list:

0        removed      '0.0'    kept
'0'      removed      ' '      kept
0.0      removed      'a'      kept
''       removed
null     removed
false    removed
[]       removed

The two lines worth remembering sit right next to each other: a string of three spaces is kept, the string '0' is thrown out.

With form data that is almost always exactly the wrong way round. Values from $_POST are strings without exception, and '0' is a perfectly normal entry — a quantity, a discount, a house number. It gets lost here. The user entered something and your code pretends they left the field blank.

What to write instead

State explicitly what “empty” means to you:

<?php
  // only genuine empty strings
  $a = array_filter($values, fn($v) => $v !== '');

  // only null
  $b = array_filter($values, fn($v) => $v !== null);

  // both
  $c = array_filter($values, fn($v) => $v !== '' && $v !== null);
?>

If whitespace should go too, which is usually the case with input fields:

without trim : ["a","   ","\t","\n","b"]
with trim    : ["a","b"]
<?php
  $d = array_filter($values, fn($v) => trim($v) !== '');
?>

You also find array_filter($array, 'strlen') in the answers. It works, but since PHP 8.1 it reports a deprecation as soon as there is a null in the array:

Deprecated: strlen(): Passing null to parameter #1 ($string)
of type string is deprecated

The result is correct, the notice still ends up in your log. And in PHP 9 it becomes a TypeError. Your own little callback is worth it.

The gaps in the keys are not a cosmetic issue

array_filter() preserves the keys. Afterwards your array is no longer a list:

keys after array_filter : [0,2]

That looks harmless until you emit the array as JSON:

json_encode           : {"0":"a","2":"b"}   <- object, not array!
after array_values()  : ["a","b"]

A JSON array turns into a JSON object. Every client expecting an array — JavaScript, an app, another API — breaks on it. And the bug only appears when something was actually filtered out, so typically not in your tests.

Hence: wrap it in array_values() whenever the result is meant to be a list. Not with an associative array, of course — there it destroys your keys.

array_filter() is not recursive

With nested arrays only the topmost level is touched:

plain     : {"a":1,"b":{"c":null,"d":2}}
recursive : {"a":1,"b":{"d":2}}

The null on the inner level survives. A recursive variant is quickly written:

<?php
function clean(array $a): array {
  foreach ($a as $k => $x) {
    if (is_array($x)) {
      $a[$k] = clean($x);
    } elseif ($x === null) {
      unset($a[$k]);
    }
  }
  return $a;
}
?>

Filtering by key

The third parameter is rarely mentioned but useful:

<?php
  // sort out internal fields
  $public = array_filter($data,
      fn($k) => !str_starts_with($k, '_'), ARRAY_FILTER_USE_KEY);

  // value and key at once
  $both = array_filter($data,
      fn($v, $k) => $v !== '' && !str_starts_with($k, '_'),
      ARRAY_FILTER_USE_BOTH);
?>
USE_KEY  : {"name":"x","ort":""}
USE_BOTH : {"name":"x"}

Watch the order with USE_BOTH: the value first, then the key. That is exactly the reverse of what you are used to from foreach.

Where the empty element usually comes from

Finally the actual cause. In the vast majority of cases the empty element arises from an explode():

explode(',', '')      : [""]   <- one element, not null
count of that         : 1
explode(',', 'a,,b')  : ["a","","b"]
then array_filter     : ["a","b"]

The first line is the classic. explode() on an empty string does not return an empty array, but an array containing one empty string. count() of that is 1. So anyone checking whether the user entered anything by counting the elements always gets at least one.

If you check the input beforehand, you often do not need the filtering at all:

<?php
  $parts = $input === '' ? [] : explode(',', $input);
?>

Summary

  • array_filter() without a callback removes 0, '0', false and [] too.
  • With form data that is almost always wrong, because '0' is a valid entry.
  • Write the callback out: fn($v) => $v !== ''.
  • To include whitespace: trim($v) !== ''.
  • Wrap in array_values(), otherwise the JSON array becomes a JSON object.
  • array_filter() is not recursive.
  • explode(',', '') returns [''] and not [] — usually the actual cause.

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).