Multiple array_filter and strpos

2020-05-06 13:18发布

问题:

I want to return array that does not contains a list of characters.

Below code works fine for one keyword ('bc').

$array = array("abc", "def", "ghi");
$filterArray = array_filter($array, function ($var) {return(strpos($var, 'bc') === false);});
print_r($filterArray);

However, below code does not work when I try to filter out multiple keywords by using $excludeKeyword_arr and foreach.

$array = array("abc", "def", "ghi");
$excludeKeyword_arr = ("ab", "de");
foreach($excludeKeyword_arr as $exclude){
    $filterArray = array_filter($array, function ($var) {return(strpos($var, $exclude) === false);});
}
print_r($filterArray);

It should be return array instead of boolean type.

回答1:

There are 2 problems with the code. The first is that the scope of $exclude doesn't allow the closure to access it, simply solved by passing it in with use.

The second problem is that you always filter the original array ($array) and so the accumulative effect isn't achieved. So here I copy the array and keep on filtering the copy ($filterArray = array_filter($filterArray, function)...

$array = array("abc", "def", "ghi");
$excludeKeyword_arr = array("ab", "de");
$filterArray = $array;
foreach($excludeKeyword_arr as $exclude){
    $filterArray = array_filter($filterArray, function ($var) use ($exclude) 
                     {return(strpos($var, $exclude) === false);});
}
print_r($filterArray);

which results in

Array
(
    [2] => ghi
)


回答2:

You can use preg_grep which will do the opposite and match the ones that has bc or de then array_diff.

$array = array("abc", "def", "ghi");
$excludeKeyword_arr = array("bc", "de");
$exclude_values = preg_grep("/". implode("|", $excludeKeyword_arr) . "/", $array);
$filterArray = array_diff($array, $values_with_bc);

print_r($filterArray); // [ 2 => "ghi"]

https://3v4l.org/IpNal



回答3:

For best performance, use a foreach() loop with a conditional break -- this way php doesn't need to perform useless iterations.

If the substring is found anywhere in the haystack string, remove it from the array of haystacks using unset().

Code: (Demo)

$array = ["abc", "def", "ghi"];
$excludeKeyword_arr = ["ab", "de"];

foreach ($array as $index => $haystack) {
    foreach ($excludeKeyword_arr as $needle) {
        if (strpos($haystack, $needle) !== false) {
            unset($array[$index]);
            break;
        }
    }
}
var_export(array_values($array));

Output:

array (
  0 => 'ghi',
)

A word of caution: if entertaining the notion of a preg_ call for brevity's sake, understand that for reliability/stability, you must apply preg_quote() to each values in the needles array if there is any chance of characters with special meaning to the regex engine.



回答4:

I would do the same as @Andreas, but reindexing in the end.

$array           = ["ab", "def", "ghi"];
$excludeKeywords = ["ab", "de"];

$pattern = implode("|", $excludeKeywords);
$result = preg_grep("/$pattern/i", $array);

// Positive (Reindex)
print_r(array_values($result));

// Negative (Reindex)
$result = array_diff($array, $result);
print_r(array_values($result));

Demo

Read more:

Pipe ( ab|de ) = https://www.regular-expressions.info/alternation.html