Search a string using keys from an array and return the value of the first qualifying key
Asked Answered
E

4

5

I have an array like this:

$array = ['cat' => 0, 'dog' => 1];

I have a string like this:

$string = 'I like cats.';

I want to see if any keys in the array are found in the string; if so, I want to return the value associated with the first matched key.

I tried the following, but obviously it doesn't work.

array_key_exists("I like cats", $array)

Assuming that I can get any random string at a given time, how can I do something like this?

Pseudo code:

array_key_exists("I like cats", *.$array.*)
//The value for cat is "0"

Note that I want to check if "cat" in any form exists. It can be cats, cathy, even random letters like vbncatnm. I am getting the array from a mysql database and I need to know which ID "cat" or "dog" is.

Everglades answered 22/10, 2016 at 18:42 Comment(9)
"I want to see if the string matches any keys in the array" Do you mean if any word from the string matches any key?Palenque
@Palenque yes correct.Everglades
@KeithC. I edited your question, adding a comment you left and the "database" tag as it could be relevant. Also, the RDBMS used is unknown, so that could play a role here. If you feel the db stuff is irrelevant, you can roll it back to an earlier revision.Concavoconvex
But in your array you have just the key "cat" and in your string you have "cats", so it won't match exactly by word because the string is plural. Should the key be "cats" instead?Bernhard
@KodosJohnson that's fine. I just want to know if anything in my array matches anything in the string. Even if the string said "I like catastrophes" I'd like to know "cat" was present.Everglades
aah I see. Can you put that in your question please? It's useful to know.Bernhard
@Fred-ii- I accidentally wrote "car" instead of cat in one of my responses. My mistake.Everglades
@KodosJohnson done, thanks.Everglades
@KeithC. No worries Keith.Concavoconvex
S
7

You can use a regex to check if the key is found in the string. The preg_match function allows to test a regular expression.

$array = ['cat' => 0, 'dog' => 1];
$string = 'I like cats';
foreach ($array as $key => $value) {
    //If the key is found, prints the value and breaks
    if (preg_match("/".preg_quote($key, '/')."/", $string)) {
        echo $value . PHP_EOL;
        break;
    }
}

EDIT:

As said in comment, strpos could be better! So, using the same code, you can just replace preg_match:

$array = ['cat' => 0, 'dog' => 1];
$string = 'I like cats';
foreach ($array as $key => $value) {
    //If the key is found, prints the value and breaks
    if (false !== strpos($string, $key)) {
        echo $value . PHP_EOL;
        break;
    }
}
Straub answered 22/10, 2016 at 19:3 Comment(10)
Thanks Anthony. When I try this I get "Warning: preg_match(): Delimiter must not be alphanumeric or backslash in /var/app/current/reporting/SqsToDbWorker.php on line 56" Not sure why it must NOT be alphanumeric?Everglades
I'm sorry that was my fault, I fix the issue. In preg_match we have to surround pattern ($key, here) by a delimiter. I added it but you can use a sharp if you want why not.Straub
I think regular expression are a little too resource-heavy for this kind of thing. You can just use strposBernhard
Yup that fixed it. Thanks. Note, you have a period before the first "/" which doesn't belong. Fix that for anyone who references this in the future.Everglades
@KodosJohnson that is correct, php documentation suggests the same thing.Everglades
@KodosJohnson you are right, so I added strpos into my answer.Straub
Can you do if (strpos("I like cats", $key) !== false) instead? The reason is that if cats is in the beginning of the string, strpos will return 0 since it returns the position number and that is falsey, so it will evaluate to false even if the string is there.Bernhard
I just did it, thanks. Indeed strpos returns the position you are right. And if strpos does not find the string it returns a falsy value, so check with !== is important as said in documentation.Straub
@AnthonyB One final note .. it wasn't finding everything while running a test so I did strpos("/I like cats/", $key) which seemed to fix it. I'm not sure if it is good for to put regular expression here but it did fix it.Everglades
@KeithC. can you give me an exemple ? I'll try to fix it if there is a bug.Straub
B
3

This should help you achieve what you're trying to do:

$array = array('cat' => 10, 'dog' => 1);

$findThis = 'I like cats';

$filteredArray = array_filter($array, function($key) use ($findThis) {
    return strpos($findThis, $key) !== false;
}, ARRAY_FILTER_USE_KEY);

I find that using the array_filter() function with a closure/anonymous function is a much more elegant solution than a foreach loop as it maintains one level of indentation.

Boric answered 22/10, 2016 at 19:47 Comment(0)
P
1

You could do using a preg_match with the value not in array but in search criteria

 if(preg_match('~(cat|dog)~', "I like cats")) {
    echo 'ok';
}

or

$criteria = '~(cat|dog)~';

 if (preg_match($criteria, "I like cats")) {
    echo 'ok';
}

Otherwise you could use a foreach on your array

 foreach($array as $key => $value ) {
     $pos = strpos("I like cats", $key);
     if ($pos > 0) {
      echo $key .  ' '. $value;
     }

 }
Perique answered 22/10, 2016 at 18:48 Comment(2)
Understood. However, I am getting the array from a database and I need to know which ID car or dog is.Everglades
if ($pos > 0) { is not safe because it will fail to find the match at the very beginning of the haystack string.Kovrov
K
1

For the following demonstrations, assume the following input data:

$haystack = "I like cats.";
$needles = ['cat' => 0, 'dog' => 1];

For all implements, please note that because the task described in the question expects the "value" of the qualifying "key" to be returned, the script needs to distinguish between positive outcomes (which may return 0, false, an array, null, or really any data type) and negative outcomes.

For this reason, if the solution is built into a function, when the search fails to find a match, clarity can be enjoyed by throwing an exception so that it can be caught by the calling script. Of course, you can opt to not contain the process in a custom function as well.


  • Using functional iteration is probably not a wise choice in terms of efficiency. By default, functional iterators (like array_filter()) will continue iterating even after they have found what they are looking for. This means wasted cycles. Demo

    function inefficient(string $haystack, array $needles) {
        $qualifiers = array_filter(
            $needles,
            fn($k) => strpos($haystack, $k) !== false,
            ARRAY_FILTER_USE_KEY
        );
        if (!$qualifiers) {
            throw new Exception('No needles were found in the haystack');
        }
        return current($qualifiers);
    }
    try {
        var_export(inefficient($haystack, $needles));    
    } catch (Exception $e) {
        echo $e->getMessage();
    }
    

  • Using regex takes a little bit of preparation and depending on how much data is involve may not be super-performant, but it is not a ridiculous approach. I don't endorse making iterated regex calls; doing so with literal string matching is effectively the same as calling strpos() but is more taxing on resources (it doesn't make sense). It will be less work for PHP to build a pipe-delimited pattern then only search the string once.

    Please note that it is safe to call preg_quote without nominating the pattern delimiter as the second parameter because the pattern being used is delimited by # -- this character is escaped by default by preg_quote(). Demo

    function regex(string $haystack, array $needles) {
        $regex = implode('|', array_map('preg_quote', array_keys($needles)));
        if (!preg_match("#$regex#", $haystack, $m)) {
            throw new Exception('No needles were found in the haystack');
        }
        return $needles[$m[0]];
    }
    try {
        var_export(regex($haystack, $needles));    
    } catch (Exception $e) {
        echo $e->getMessage();
    }
    

  • Using a classic foreach() with a conditional/early return or break may not be deemed the most sexy approach to some developers, but it will likely boast the best performance. Demo

    function earlyReturn(string $haystack, array $needles) {
        foreach ($needles as $needle => $value) {
            if (strpos($haystack, $needle) !== false) {
                return $value;
            }
        }
        throw new Exception('No needles were found in the haystack');
    }
    try {
        var_export(earlyReturn($haystack, $needles));    
    } catch (Exception $e) {
        echo $e->getMessage();
    }
    
Kovrov answered 12/3 at 21:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.