[英]How to filter multidimensional array based on another multidimensional array of exclusions?
我正在尝试过滤包含(> 40,000)个产品的多维数组。 每个产品条目/子数组包含产品 ID 和一些产品属性。
我有一个关联排除数组,其中包含 1 个或多个与特定属性有关的列入黑名单的值。
如果产品具有在我的排除数组中指定的任何键值对,则应过滤掉该产品/子数组。
排除数组:
$exclusions = [
'Discontinue Status' => [
'Discontinued',
'Run Down Stock',
],
'Hazardous' => [
'No',
],
];
示例产品数组:
$products = [
[
'Product ID' => '452',
'Discontinue Status' => 'Discontinued',
'Hazardous' => 'No',
],
[
'Product ID' => '463',
'Discontinue Status' => 'Normal',
'Hazardous' => 'No',
],
[
'Product ID' => '477',
'Discontinue Status' => 'Run Down Stock',
'Hazardous' => 'Yes',
],
[
'Product ID' => '502',
'Discontinue Status' => 'Discontinued',
'Hazardous' => 'No',
],
[
'Product ID' => '520',
'Discontinue Status' => 'Normal',
'Hazardous' => 'Yes',
],
];
预期输出:
[
[
'Product ID' => '520',
'Discontinue Status' => 'Normal',
'Hazardous' => 'Yes',
],
]
我只能返回正确数量的产品/物品,但只能返回与该物品相关的排除项,而不是具有以下代码的物品本身。
$exclusions = $this->exclusions;
$products = [];
foreach ($array as $product) {
$filtered = array_filter($product, function ($val, $key) use ($exclusions) {
return isset($exclusions[$key]) && !in_array($val, $exclusions[$key]);
},
ARRAY_FILTER_USE_BOTH
);
$products[] = $filtered;
}
$result = array_filter(array_map('array_filter', $products));
echo '<pre>' . var_export($result, true) . '</pre>';
echo count($result);
首先,我们将$data
、 $discontinued
和$hazardous
为空白数组,当我们遍历$exclusions
和$products
数组时,我们将用数据填充这些数组。
$data = $discontinued = $hazardous = [];
foreach($exclusions as $exclusion) {
if(isset($exclusion['Discontinue Status'])) $discontinued = $exclusion['Discontinue Status'];
if(isset($exclusion['Hazardous'])) $hazardous = $exclusion['Hazardous'];
}
现在我们要遍历产品并检查每个产品的值是否与排除数组中的任何内容匹配。
foreach($products as $product) {
if(in_array($product['Discountinue Status'], $discontinued)) continue;
if(in_array($product['Hazardous'], $hazardous)) continue;
$data[] = $product;
}
不建议将排除数组分解为单独的变量,因为当您想要修改排除列表时,这会使您的代码更难/更繁琐地维护。
仅迭代您的产品数组一次。 在该循环内,循环每个产品中具有与排除数组中的第一级键匹配的键的属性。 这是array_intersect_key()
最array_intersect_key()
,这可以防止对Product ID
元素执行不必要的比较。 一旦发现要满足不合格条件,就停止内循环以获得最佳效率。
代码#1(演示)*我的建议
$result = [];
foreach ($products as $product) {
foreach (array_intersect_key($product, $exclusions) as $key => $value) {
if (in_array($value, $exclusions[$key])) {
continue 2;
}
}
$result[] = $product;
}
var_export($result);
代码#2:(演示)
foreach ($products as $index => $product) {
foreach (array_intersect_key($product, $exclusions) as $key => $value) {
if (in_array($value, $exclusions[$key])) {
unset($products[$index]);
break;
}
}
}
var_export(array_values($products));
代码#3:(演示)
var_export(
array_values(
array_filter(
$products,
function($product) use ($exclusions) {
return !array_filter(
array_intersect_key($product, $exclusions),
function($value, $key) use ($exclusions) {
return in_array($value, $exclusions[$key]);
},
ARRAY_FILTER_USE_BOTH
);
}
)
)
);
代码#4:(演示)
var_export(
array_values(
array_filter(
$products,
fn($product) => !array_filter(
array_intersect_key($product, $exclusions),
fn($value, $key) => in_array($value, $exclusions[$key]),
ARRAY_FILTER_USE_BOTH
)
)
)
);
代码 #1 使用continue 2;
停止内循环,避免将当前乘积存储在输出数组中,然后返回外循环继续处理下一个乘积。
代码#2 直接修改$products
数组。 通过取消设置产品,数组可能不再是索引数组(键可能在整数之间有间隙)。 如果需要,在循环后调用array_values()
以重新索引输出。
代码#3 使用的是函数式风格。 语言结构(例如foreach()
)的性能优于函数式迭代器是很常见的,所以我假设这个片段(和代码#4)会比前两个稍微慢一些。 此外, array_filter()
不享受 foreach 循环对break
和continue
的早期返回。 换句话说,即使给定产品已经满足不合格条件,代码 #3 和 #4 也会继续检查排除数组。 如果这还不够,我只是觉得语法太复杂了。
代码#4 与代码#3 相同,但使用的是PHP7.4 及更高版本中可用的略短的“箭头函数”语法。 这使得省略了use()
和其他一些字符,但我仍然发现与代码 #1 和 #2 相比,该代码段不那么直观/可读。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.