Как рекурсивно запустить array_filter в массиве PHP?

Учитывая следующий массив $mm

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

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

)

Когда я запускаю count(array_filter($mm)), я получаю 3 в результате, так как он не рекурсивный.

count(array_filter($mm), COUNT_RECURSIVE) тоже не годится, потому что мне на самом деле нужно запустить array_filter рекурсивно, а потом подсчитать его результат.

Итак, мой вопрос: как мне рекурсивно запустить array_filter($mm) в этом случае? Мой ожидаемый результат здесь будет 4.

Обратите внимание, что я не использую обратный вызов, поэтому я могу исключить false, null и empty.


person pepe    schedule 22.07.2011    source источник
comment
Вы просто пытаетесь подсчитать элементы массива, которые имеют значение?   -  person Jrod    schedule 23.07.2011
comment
@jrod это правильно, но не 0   -  person pepe    schedule 23.07.2011


Ответы (6)


Должно сработать

$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);

или, может быть

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

Определенно есть еще много возможных решений. Если вы хотите использовать array_filter() (без обратного вызова), помните, что он также обрабатывает 0 как false и поэтому удалит любое 0-значение из массива.

Если вы используете PHP в версии до 5.3, я бы использовал цикл foreach.

$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']);
}

Обновлять

По поводу комментария ниже:

Спасибо @kc, я действительно хочу, чтобы этот метод удалял false, 0, empty и т. д.

Когда это действительно только то, что вы хотите, решение тоже очень простое. Но теперь я не знаю, как интерпретировать

Мой ожидаемый результат здесь будет 5.

В любом случае, короче :)

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

Результирующий массив выглядит как

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

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

)
person KingCrunch    schedule 22.07.2011
comment
Спасибо @kc, я действительно хочу, чтобы этот метод удалял false, 0, empty и т. д. - person pepe; 23.07.2011
comment
Ваше утверждение Моим ожидаемым результатом здесь будет 5. тогда это вводит в заблуждение, и после удаления любого false-вычисленного значения count($array) все равно не вернет 0. - person KingCrunch; 23.07.2011
comment
... все еще не возвращает 5. Он считает элементы массива, а массив является элементом и, следовательно, будет считаться как 1 (я просто не хотел, чтобы этот комментарий был глупым, чтобы исправить мою опечатку..) - person KingCrunch; 23.07.2011
comment
большое спасибо @kingcrunch - ваше предложение прекрасно работает, очень признателен! - person pepe; 23.07.2011

Из документации по PHP array_filter:

//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); 
  } 
?>
person Francois Deschenes    schedule 22.07.2011
comment
это также удалит пустое и 0? - person pepe; 23.07.2011
comment
@torr - это будет работать так же, как обычно array_filter. Если вы посмотрите на функцию, она вызывает array_filter, если только текущее значение не является массивом, и в этот момент она вызывает функцию array_filter_recursive. - person Francois Deschenes; 23.07.2011
comment
@FrancoisDeschenes: array_filter_recursive с обратным вызовом не работает должным образом, он дает ошибки преобразования массива в строку во время рекурсии .. - person Syed Qarib; 08.03.2015
comment
Не работает с обратным вызовом. Смотрите мой ответ для рабочей функции - person helpse; 14.04.2015
comment
Если я не передаю обратный вызов (обратный вызов равен нулю), результат также будет нулевым:/ Не могу понять, почему - person Karmalakas; 29.06.2015

Лучшая альтернатива

Одна реализация, которая всегда работала для меня, такова:

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;
}

Я заметил, что кто-то создал подобную функцию, за исключением того, что у этой, на мой взгляд, мало преимуществ:

  1. вы передаете массив в качестве ссылки (а не его копию), и поэтому алгоритм удобен для памяти
  2. no additional calls to array_filter which in reality involves:
    • the use of stack, ie. additional memory
    • некоторые другие операции, т.е. Циклы процессора

Эталонные показатели

  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, рекомендованная выше Франсуа Дешеном, не имела шансов; через 1 с PHP Неустранимая ошибка: разрешенный размер памяти 134217728 байт исчерпан
  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 завершилась успешно за 0,6 с, а память до/после была почти такой же.

Я надеюсь, что это помогает.

person Eugen Mihailescu    schedule 13.04.2015
comment
Но это хорошее решение для конкретного вопроса;) Проголосовал - person helpse; 21.04.2015

Эта функция эффективно применяет filter_recursive с предоставленным обратным вызовом.

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;
    }

}

И вы бы использовали его следующим образом:

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

Это может помочь кому-то

person helpse    schedule 14.04.2015
comment
Это может помочь, поэтому полезно знать. В любом случае, вопрос был связан с тем, как отфильтровать эти «пустые» записи, и мой ответ был сосредоточен на предоставлении лучшей альтернативы в отношении этой проблемы. Но я согласен, что ваша версия рекурсивного фильтра имеет более широкое применение. - person Eugen Mihailescu; 14.04.2015
comment
Разве array_filter не должен сохранять исходное значение, если обратный вызов возвращает true? В этом случае вы захлопываете исходное значение до true - person Jonathan; 06.03.2018
comment
уничтожает входной массив - person Mulan; 10.08.2019

Мне нужна была рекурсивная функция фильтра массива, которая проходила бы по всем узлам (включая массивы, чтобы у нас была возможность отбрасывать целые массивы), и поэтому я придумал это:


    public static function filterRecursive(array $array, callable $callback): array
    {
        foreach ($array as $k => $v) {
            $res = call_user_func($callback, $v);
            if (false === $res) {
                unset($array[$k]);
            } else {
                if (is_array($v)) {
                    $array[$k] = self::filterRecursive($v, $callback);
                }
            }
        }

        return $array;
    }

Дополнительные примеры см. здесь: https://github.com/lingtalfi/Bat/blob/master/ArrayTool.md#filterrecursive

person ling    schedule 09.08.2019
comment
это изменяет входной массив - person Mulan; 10.08.2019

person    schedule
comment
Хорошая идея - в этом случае вместо условных выражений и $count++ можно было бы использовать array_filter, не так ли? - person pepe; 23.07.2011