php - 或运算符上的 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 部分的任何帮助。
解决方案
我认为递归函数会让你到达那里。首先,我将模拟一些与您的数据相似但可能更简单的数据:
$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
推荐阅读
- r - 将第一次出现的值保留在 r
- animation - 如何在gsap中同时制作动画?
- php - 启用 HTTP/2 H2 的服务器上的 HTTP/1.1 响应代码标头
- kubernetes - 容器上的“container_memory_working_set_bytes”和“container_memory_rss”指标有什么区别
- python - 分割已标记的图像以进行对象检测
- heroku - 你如何正确设置DSN?[错误:未找到 DNSKEY 记录;CNAME RRset 未由信任链中的任何密钥签名]
- c - 如何用逗号分割 .csv 中的单词?
- angular - TMDb API - 按流派 id 过滤
- python-3.x - 请求库的问题
- c++ - 运行后从 Google 测试中获取错误消息