首页 > 解决方案 > 或运算符上的 PHP array_filter

问题描述

我正在使用array_filter将一组过滤器应用于一组数据。函数调用看起来像
$data = array_filter($tmpData, function($row) use $filterData { ... });.
如果我在每个过滤器的逻辑为 AND 的地方过滤,这工作得很好。但是,如果我想使用 OR 在两个值之间进行过滤,那么我对如何处理这个问题会有些犹豫,因为每个过滤器还可以有一组嵌套的更多过滤器,这些过滤器会递归地调用过滤函数。过滤器的格式如下:

$filter = array(
  'logic' => 'AND',
  'filters' => array(
    array('field' => $myFieldName1, 'operator' => $filterOperator1, 'value' => $myValue1),
    array(
      'logic' => 'OR',
      'filters' => array(
        array('field' => $myFieldName2, 'operator' => $filterOperator2, 'value' => $myValue2),
        array('field' => $myFieldName3, 'operator' => $filterOperator3, 'value' => $myValue3)
      )
    )
  )
);

你可以在这里看到我的函数的一个稍微精简的版本。除了“等于”之外,它只实现了一个过滤运算符。https://www.tehplayground.com/FiPHxwDNJWdKq4hM

感谢您对这项工作的 OR 部分的任何帮助。

标签: phpfilteringarray-filter

解决方案


我认为递归函数会让你到达那里。首先,我将模拟一些与您的数据相似但可能更简单的数据:

$obj = new stdClass();
$obj->a = 5;
$obj->b = 2;
$obj->c = 9;

$filter = array(
    'logic' => 'AND',
    'filters' => array(
        array('field' => 'a', 'operator' => '===', 'value' => 5),
        array(
            'logic' => 'OR',
            'filters' => array(
                array('field' => 'b', 'operator' => '===', 'value' => 6),
                array('field' => 'c', 'operator' => '<', 'value' => 10),
            ),
        ),
    ),
);

然后,我们编写一个递归函数。我使用的是 PHP 8 match,这使得这非常简洁,但您可能需要根据您的 PHP 版本进行更改。有关详细信息,请参阅代码注释。这不是最有效的,但它应该可以完成工作。

function process(string $logic, array $filters, object $obj): bool
{
    // For an AND, all things must be true, so if we're given 5 things, all 5 must be true.
    // For an OR, only one needs to be true.
    $targetCount = 'AND' === $logic ? count($filters) : 1;

    // How many items were true
    $currentCount = 0;
    foreach ($filters as $filter) {

        // The sub-array is a grouping, grab parts, process, and if true, increment our local counter
        if (array_key_exists('logic', $filter)) {
            if (process($filter['logic'], $filter['filters'], $obj)) {
                $currentCount++;
            }

            // For a sub-array, don't process anything else
            continue;
        }

        // Grab array items as variables just for ease of use
        $field = $filter['field'];
        $operator = $filter['operator'];
        $value = $filter['value'];

        // Match on the operator. For lower versions of PHP a switch(true) or just an if could be used, too.
        $result = match ($operator) {
            '===' => $obj->$field === $value,
            '==' => $obj->$field == $value,
            '>=' => $obj->$field >= $value,
            '>' => $obj->$field > $value,
            '<=' => $obj->$field <= $value,
            '<' => $obj->$field < $value,
        };

        // If success, increment our counter
        if ($result) {
            $currentCount++;

            // If we've reach our target, return success
            // This could also just be "if process = OR"
            if ($currentCount === $targetCount) {
                return true;
            }
        }
    }

    // See if we met the final condition outside of the loop
    return $currentCount === $targetCount;
}

最后,一个调用它的例子。使用递归函数,您通常必须确定是否要让整个函数尝试并确定它是否是第一次迭代并设置好东西,或者是否要传入设置以便每次迭代运行相同,我我做了后者。

echo process($filter['logic'], $filter['filters'], $obj) ? 'yes' : 'no';

在线示例在这里:https ://3v4l.org/Hu0CY#v8.0.8


推荐阅读