How to run array_filter recursively in a PHP array?

This function effectively applies filter_recursive with a provided callback

class Arr {

    public static function filter_recursive($array, $callback = NULL)
    {
        foreach ($array as $index => $value)
        {
            if (is_array($value))
            {
                $array[$index] = Arr::filter_recursive($value, $callback);
            }
            else
            {
                $array[$index] = call_user_func($callback, $value);
            }

            if ( ! $array[$index])
            {
                unset($array[$index]);
            }
        }

        return $array;
    }

}

And you'd use it this way:

Arr::filter_recursive($my_array, $my_callback);

This might help someone


From the PHP array_filter documentation:

//This function filters an array and remove all null values recursively. 

<?php 
  function array_filter_recursive($input) 
  { 
    foreach ($input as &$value) 
    { 
      if (is_array($value)) 
      { 
        $value = array_filter_recursive($value); 
      } 
    } 

    return array_filter($input); 
  } 
?> 

//Or with callback parameter (not tested) : 

<?php 
  function array_filter_recursive($input, $callback = null) 
  { 
    foreach ($input as &$value) 
    { 
      if (is_array($value)) 
      { 
        $value = array_filter_recursive($value, $callback); 
      } 
    } 

    return array_filter($input, $callback); 
  } 
?>

Should work

$count = array_sum(array_map(function ($item) {
  return ((int) !is_null($item['pts_m'])
       + ((int) !is_null($item['pts_mreg'])
       + ((int) !is_null($item['pts_cg']);
}, $array);

or maybe

$count = array_sum(array_map(function ($item) {
  return array_sum(array_map('is_int', $item));
}, $array);

There are definitely many more possible solutions. If you want to use array_filter() (without callback) remember, that it treats 0 as false too and therefore it will remove any 0-value from the array.

If you are using PHP in a pre-5.3 version, I would use a foreach-loop

$count = 0;
foreach ($array as $item) {
  $count += ((int) !is_null($item['pts_m'])
          + ((int) !is_null($item['pts_mreg'])
          + ((int) !is_null($item['pts_cg']);
}

Update

Regarding the comment below:

Thx @kc I actually want the method to remove false, 0, empty etc

When this is really only, what you want, the solution is very simple too. But now I don't know, how to interpret

My expected result here would be 5.

Anyway, its short now :)

$result = array_map('array_filter', $array);
$count = array_map('count', $result);
$countSum = array_sum($count);

The resulting array looks like

Array
(
[147] => Array
    (
        [pts_mreg] => 1
        [pts_cg] => 1
    )    
[158] => Array
    (
    )

[159] => Array
    (
        [pts_mreg] => 1
        [pts_cg] => 1
    )

)

A better alternative

One implementation that always worked for me is this one:

function filter_me(&$array) {
    foreach ( $array as $key => $item ) {
        is_array ( $item ) && $array [$key] = filter_me ( $item );
        if (empty ( $array [$key] ))
            unset ( $array [$key] );
    }
    return $array;
}

I notice that someone had created a similar function except that this one presents, in my opinion, few advantages:

  1. you pass an array as reference (not its copy) and thus the algorithm is memory-friendly
  2. no additional calls to array_filter which in reality involves:
    • the use of stack, ie. additional memory
    • some other operations, ie. CPU cycles

Benchmarks

  1. A 64MB array
    • filter_me function finished in 0.8s AND the PHP allocated memory before starting the function was 65MB, when function returned it was 39.35MB !!!
    • array_filter_recursive function recommended above by Francois Deschenes had no chance; after 1s PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
  2. A 36MB array
    • filter_me function finished in 0.4s AND the PHP allocated memory before starting the function was 36.8MB, when function returned it was 15MB !!!
    • array_filter_recursive function succeeded this time in 0.6s and memory before/after was quite the same

I hope it helps.