簡體   English   中英

計算二維數組中的“真實”值

[英]Count "truthy" values in a 2d array

給定以下數組$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

請注意,我沒有使用任何回調,因此我可以排除錯誤、null 和空。

來自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); 
  } 
?>

應該管用

$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值。

如果您在 5.3 之前的版本中使用 PHP,我會使用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']);
}

更新

關於下面的評論:

Thx @kc 我實際上希望該方法刪除 false、0、空等

當這真的只是你想要的,解決方案也很簡單。 但是現在不知道怎么解釋

我的預期結果是 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
    )

)

更好的選擇

一種一直對我有用的實現是這個:

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

我注意到有人創建了一個類似的 function,但在我看來,這個有幾個優點:

  1. 您將數組作為參考(而不是其副本)傳遞,因此該算法是內存友好的
  2. 沒有對 array_filter 的額外調用,這實際上涉及:
    • 堆棧的使用,即。 附加 memory
    • 其他一些操作,即。 CPU 周期

基准

  1. 64MB 陣列
    • 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 !!!
    • 上面由 Francois Deschenes推薦的 array_filter_recursive function 沒有機會; 1s 后 PHP 致命錯誤:允許的 memory 大小為 134217728 字節用盡
  2. 36MB 陣列
    • 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這次在 0.6s 內成功,memory 之前/之后完全一樣

我希望它有所幫助。

這個 function 通過提供的回調有效地應用 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);

這可能會幫助某人

我需要一個數組過濾器遞歸 function 將遍歷所有節點(包括 arrays,以便我們有可能丟棄整個數組),所以我想出了這個:


    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

這應該適用於回調和模式支持以及對深度的可選支持。

function array_filter_recursive(array $array, callable $callback = null, int $mode = 0, int $depth = -1): array
{
    foreach ($array as & $value) {
        if ($depth != 0 && is_array($value)) {
            $value = array_filter_recursive($value, $callback, $mode, $depth - 1);
        }
    }

    if ($callback) {
        return array_filter($array, $callback, $mode);
    }

    return array_filter($array);
}

為嵌套的 arrays 調用$depth = 0array_filter ,將產生與 array_filter 相同的結果。

這讓我覺得是一個XY 問題

  1. 遞歸不是必需的,因為數組具有一致的 2 級深度。
  2. 不需要生成一個過濾后的元素數組,這樣就可以遍歷過濾后的數據進行統計。 只需遍歷一次,並在遇到真值時將 count 變量加 1。

以下代碼段不調用任何函數(僅調用語言構造 -- foreach() ),因此效率很高。

代碼:(演示

$truthyCount = 0;
foreach ($array as $row) {
    foreach ($row as $v) {
        $truthyCount += (bool) $v;
    }
}
var_export($truthyCount);
<?php

$mm = array
(
    147 => array
        (
            "pts_m" => "",
            "pts_mreg" => 1,
            "pts_cg" => 1
        ) ,
    158 => array
        (
            "pts_m" => null ,
            "pts_mreg" => null,
            "pts_cg" => 0
        ),

    159 => array
        (
            "pts_m" => "",
            "pts_mreg" => 1,
            "pts_cg" => 1
        )

);

$count = 0;
foreach ($mm as $m) {
    foreach ($m as $value) {
        if($value !== false && $value !== "" && $value !== null) {
            $count++;
        }
    }
}
echo $count;
?>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM