数组值的数组过滤器
array filter for array values
我有这段代码可以搜索选项名称,我如何使用它从数组中搜索选项值。
$productspp ='[{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}, {
"id": 4674394325124,
"title": "2nd march",
"options": [{
"id": 6046844190852,
"product_id": 4674394325124,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}],
}, {
"id": 4679851704452,
"title": "3rd marchhh",
"options": [{
"id": 6053112545412,
"product_id": 4679851704452,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}]
}]';
$array = json_decode($productspp,1);
$filter_name555 ='options';
$dummytstt ='values';
$filter_value= blue;
$expected = array_filter($array, function($el) use ($filter_name555, $dummytstt, $filter_value) {
return ( stripos($el[$filter_name555][0][$dummytstt], $filter_value) !== false );
}
});
如果用户搜索 option_value 并且它匹配,那么它应该列出该产品,所以在这种情况下,如果用户搜索 silk 那么它应该列出该产品,否则不
对于选项名称,它适用于选项值,它不起作用,因为 stripos 期望它是字符串,但在数据中它是数组。
我们也尝试 in_array 进行过滤,但同样无效
当我们搜索 12 或 24 或 36 或蓝色或绿色时,它应该列出 json 的这一部分。我的意思是这个产品和我上面给出的代码做的一样,但选项名称。你可以看到选项值是数组。它可以有多个值,所以我的代码失败了。
{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}
你的基本问题是你有一个多维数组并且你没有在所有级别上迭代。为了完成这项工作,我不得不重新设计整个逻辑,但我希望这将是一个很好的学习练习。
array_filter
函数仅适用于给定级别。你可以保留它,但我会提出一个只使用嵌套循环的解决方案:
/**
* Selects products where options match the searched one by removing the ones that don't match.
*/
function selectProductsWithMatchingOptions(array $products, string $filterName, string $filterValue): array
{
foreach ($products as $key => $product) {
if (!hasMatchingOption($product['options'], $filterName, $filterValue)) {
unset($products[$key]);
}
}
return $products;
}
/**
* Checks whether the searched filter is within any of the options for the product.
*/
function hasMatchingOption(array $options, string $filterName, string $filterValue): bool
{
foreach ($options as $option) {
// this part takes care of "values", which is an array
if (is_array($option[$filterName]) && valueIsListed($option[$filterName], $filterValue)) {
return true;
// this part takes care of "name", which is a string
} elseif (is_string($option[$filterName]) && stringMatches($filterValue, $option[$filterName])) {
return true;
}
}
return false;
}
/**
* Checks if a value is in the list of values.
*/
function valueIsListed(array $values, string $filterValue): bool
{
// we have to iterate and check with stripos because in_array can't handle case
foreach ($values as $value) {
if (stringMatches($filterValue, $value)) {
return true;
}
}
return false;
}
function stringMatches(string $needle, string $haystack): bool
{
return stripos($needle, $haystack) !== false;
}
现在,如果你用你的阵列测试这个:
$filtered = selectProductsWithMatchingOptions($array, 'name', 'mAterial');
或
$filtered = selectProductsWithMatchingOptions($array, 'values', 'BLUE');
转储 $filtered
应该会显示所需的结果。
我制作了几个函数,因为我更喜欢提取更小的逻辑片段。它使代码更清晰。另一种方法是将布尔值存储在变量中,以了解我们是否找到了匹配项,但这往往会降低可读性。另外,通过这种方式,我们可以更优雅地放弃循环,只要找到匹配项就返回 true
。这比必须 break
更好,可能需要通过多个级别。
请注意,我没有将 'options'
键作为参数传递,因为它是唯一可以迭代的键 - 此函数不适用于 'id'
或 'title'
.它也可以修改以处理这些问题,但我会把它留给你。
另请注意,即使选项数量发生变化(您在评论中提到最大值为 3),此功能仍然有效,因为它与元素数量无关。只要不需要太多努力,就始终以更通用的解决方案为目标。以后你会感谢自己的。
您需要区分数组值或常规值,因为它们需要以不同方式匹配。
您可以做的一件事是为一个值是否为数组编写逻辑,然后将任何其他类型的值强制放入只有一个元素的数组中。
$key = 'options';
$attr = 'values';
$search = 'blue';
$expected = array_filter($array, function($el) use ($key, $attr, $search) {
$values = $el[$key];
if (is_array($value)) {
$values = array_column($value, $attr);
} else {
$value = array($value);
}
foreach ($value as $body) {
foreach ((array)$body as $contents) {
if (stripos($contents, $search) !== false) {
return true;
}
}
}
return false;
}
我有这段代码可以搜索选项名称,我如何使用它从数组中搜索选项值。
$productspp ='[{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}, {
"id": 4674394325124,
"title": "2nd march",
"options": [{
"id": 6046844190852,
"product_id": 4674394325124,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}],
}, {
"id": 4679851704452,
"title": "3rd marchhh",
"options": [{
"id": 6053112545412,
"product_id": 4679851704452,
"name": "Title",
"position": 1,
"values": ["Default Title"]
}]
}]';
$array = json_decode($productspp,1);
$filter_name555 ='options';
$dummytstt ='values';
$filter_value= blue;
$expected = array_filter($array, function($el) use ($filter_name555, $dummytstt, $filter_value) {
return ( stripos($el[$filter_name555][0][$dummytstt], $filter_value) !== false );
}
});
如果用户搜索 option_value 并且它匹配,那么它应该列出该产品,所以在这种情况下,如果用户搜索 silk 那么它应该列出该产品,否则不
对于选项名称,它适用于选项值,它不起作用,因为 stripos 期望它是字符串,但在数据中它是数组。
我们也尝试 in_array 进行过滤,但同样无效
当我们搜索 12 或 24 或 36 或蓝色或绿色时,它应该列出 json 的这一部分。我的意思是这个产品和我上面给出的代码做的一样,但选项名称。你可以看到选项值是数组。它可以有多个值,所以我的代码失败了。
{
"id": 4674388066436,
"title": "1st march",
"options": [{
"id": 6046836162692,
"product_id": 4674388066436,
"name": "Size",
"position": 1,
"values": ["12", "24", "36"]
}, {
"id": 6067871875204,
"product_id": 4674388066436,
"name": "z",
"position": 2,
"values": ["blue", "green"]
}, {
"id": 6067871907972,
"product_id": 4674388066436,
"name": "Material",
"position": 3,
"values": ["silk", "cotton"]
}],
}
你的基本问题是你有一个多维数组并且你没有在所有级别上迭代。为了完成这项工作,我不得不重新设计整个逻辑,但我希望这将是一个很好的学习练习。
array_filter
函数仅适用于给定级别。你可以保留它,但我会提出一个只使用嵌套循环的解决方案:
/**
* Selects products where options match the searched one by removing the ones that don't match.
*/
function selectProductsWithMatchingOptions(array $products, string $filterName, string $filterValue): array
{
foreach ($products as $key => $product) {
if (!hasMatchingOption($product['options'], $filterName, $filterValue)) {
unset($products[$key]);
}
}
return $products;
}
/**
* Checks whether the searched filter is within any of the options for the product.
*/
function hasMatchingOption(array $options, string $filterName, string $filterValue): bool
{
foreach ($options as $option) {
// this part takes care of "values", which is an array
if (is_array($option[$filterName]) && valueIsListed($option[$filterName], $filterValue)) {
return true;
// this part takes care of "name", which is a string
} elseif (is_string($option[$filterName]) && stringMatches($filterValue, $option[$filterName])) {
return true;
}
}
return false;
}
/**
* Checks if a value is in the list of values.
*/
function valueIsListed(array $values, string $filterValue): bool
{
// we have to iterate and check with stripos because in_array can't handle case
foreach ($values as $value) {
if (stringMatches($filterValue, $value)) {
return true;
}
}
return false;
}
function stringMatches(string $needle, string $haystack): bool
{
return stripos($needle, $haystack) !== false;
}
现在,如果你用你的阵列测试这个:
$filtered = selectProductsWithMatchingOptions($array, 'name', 'mAterial');
或
$filtered = selectProductsWithMatchingOptions($array, 'values', 'BLUE');
转储 $filtered
应该会显示所需的结果。
我制作了几个函数,因为我更喜欢提取更小的逻辑片段。它使代码更清晰。另一种方法是将布尔值存储在变量中,以了解我们是否找到了匹配项,但这往往会降低可读性。另外,通过这种方式,我们可以更优雅地放弃循环,只要找到匹配项就返回 true
。这比必须 break
更好,可能需要通过多个级别。
请注意,我没有将 'options'
键作为参数传递,因为它是唯一可以迭代的键 - 此函数不适用于 'id'
或 'title'
.它也可以修改以处理这些问题,但我会把它留给你。
另请注意,即使选项数量发生变化(您在评论中提到最大值为 3),此功能仍然有效,因为它与元素数量无关。只要不需要太多努力,就始终以更通用的解决方案为目标。以后你会感谢自己的。
您需要区分数组值或常规值,因为它们需要以不同方式匹配。
您可以做的一件事是为一个值是否为数组编写逻辑,然后将任何其他类型的值强制放入只有一个元素的数组中。
$key = 'options';
$attr = 'values';
$search = 'blue';
$expected = array_filter($array, function($el) use ($key, $attr, $search) {
$values = $el[$key];
if (is_array($value)) {
$values = array_column($value, $attr);
} else {
$value = array($value);
}
foreach ($value as $body) {
foreach ((array)$body as $contents) {
if (stripos($contents, $search) !== false) {
return true;
}
}
}
return false;
}