String contains any items in an array (case insensitive)
Asked Answered
B

15

54

How can i check if a $string contains any of the items expressed in an array?

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(contains($string,$array))
{
// do something to say it contains
}

Any ideas?

Barghest answered 23/1, 2010 at 20:14 Comment(3)
There is no indication of if partial word matching is desired. Do you need word boundaries?Kellikellia
Hi @mickmackusa, what about partial string matching?Perseid
My question to the asker is asking if My username is Tom. should result in a match for name. This is a common fork in business requirements for developers. Sometimes, making a partial match is absolutely fine; other times, it is critical that only full-words are matched.Kellikellia
F
26

is that what you wanted? i hope that code is compiling :)

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
{
  //do sth
}
Fairchild answered 23/1, 2010 at 20:21 Comment(3)
This would fail on the strings: "Tom, what do you think?" "His 'name' is Tom." among many others.Mcreynolds
Why call strlower() n times after exploding? It makes better sense to call strtolower() before exploding. The 0 < is unnecessary -- the return from count() will be a truthy value when greater than zero. This answer is missing its educational explanation.Kellikellia
This assumes the array only contains single words and not phrases.Electorate
C
112

I don't think there is a built-in function that will handle what you want. You could easily write a contains() function however:

function contains($str, array $arr)
{
    foreach($arr as $a) {
        if (stripos($str,$a) !== false) return true;
    }
    return false;
}
Crony answered 23/1, 2010 at 20:22 Comment(4)
Why not regexp? I think its not to diffucult to interpret preg_match('/'.implode('|', $arr).'/i', $str)Consignment
@Consignment While a valid approach, that is not functionally equivalent. The array would need to be written in consideration of being matched via regex. Otherwise if any strings in the array include a / or | or any other modifiers it will provide unexpected results.Vincentia
Tip for developers using Laravel: this method exists in the framework from 5.7 onwards as Str::contains(string $haystack, array|string $needles) and as str_contains(string $haystack, array|string $needles) for version 5.6. laravel.com/docs/8.x/helpers#method-str-containsFounder
@Founder Would be cool with a Str:: helper function that would either return the index: number or index(s): number[] from the Str::contains instead of a booleanMateri
F
26

is that what you wanted? i hope that code is compiling :)

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
{
  //do sth
}
Fairchild answered 23/1, 2010 at 20:21 Comment(3)
This would fail on the strings: "Tom, what do you think?" "His 'name' is Tom." among many others.Mcreynolds
Why call strlower() n times after exploding? It makes better sense to call strtolower() before exploding. The 0 < is unnecessary -- the return from count() will be a truthy value when greater than zero. This answer is missing its educational explanation.Kellikellia
This assumes the array only contains single words and not phrases.Electorate
S
18

Using the accepted answer:

$string = 'My nAmE is Tom.';
$array = array("name","tom");
if(0 < count(array_intersect(array_map('strtolower', explode(' ', $string)), $array)))
{
  //do sth
}

Just a side note that the if statement could be changed to:

if(0 < count(array_intersect(explode(' ', strtolower($string)), $array)))

since it's not really necessary to use array_map to apply strtolower to each element. instead apply it to the initial string.

Snap answered 5/9, 2013 at 8:23 Comment(2)
This (along with the accepted answer) matches whole words rather than substrings so if you need to match more than whole words you should probably use @Crony 's answer.Anisometric
Note that this answer will not match tom to tom. because the explosion on spaces does not trim the punctuation character. You might enjoy str_word_count() here with a format parameter of 1.Kellikellia
E
10

One more workaround for contains function

function contains($string, $array, $caseSensitive = true)
{
    $strippedString = $caseSensitive ? str_replace($array, '', $string) : str_ireplace($array, '', $string);
    return $strippedString !== $string;
}

PS. as for me, I'm just using it without function...

if (str_replace($array, '', $string) !== $string) {
    // do it
}
Esquiline answered 25/11, 2015 at 9:45 Comment(1)
The cleaner/leaner version of this answer is here because it doesn't bother making two unnecessary strlen() calls.Kellikellia
G
9

We can check if any element of array is exists in a given string.

$string = 'My nAmE is Tom.';
$array = array("name","tom");

if(str_replace($array, '', strtolower($string)) !== strtolower($string)) {
   // If String contains an element from array      
   // Do Something
}
Gonzalez answered 7/4, 2021 at 12:0 Comment(3)
Nice solution. I would prefer mb_strtolower for utf8 characters.Confirmand
By calling strtolower() (or mb_strtolower()) and assigning to $lower before the if condition, you can simplify the condition to if (str_replace($needles, '', $lower) !== $lower) {.Kellikellia
From an academic point of view, this answer cannot enjoy an early return if it findd a match with the first needle in the array ...it would keep re-scanning and replacing strings until the entire array of needles was iterated.Kellikellia
S
7

Something like this would work:

$string = 'My nAmE is Tom.';
$array = array("name", "tom");
foreach ($array as $token) {
    if (stristr($string, $token) !== FALSE) {
        print "String contains: $token\n";
    }
}
Saccular answered 23/1, 2010 at 20:22 Comment(0)
H
1

Will this do the job?

$words = explode(" ", $string);
$wordsInArray = array();
foreach($words as $word) {
    if(in_array($word, $array)) {
        $wordsInArray[] = $word;
    }
}
Hydroplane answered 23/1, 2010 at 20:20 Comment(3)
This only matches whole words.Anisometric
Hi @apokryfos, How to match partial-string also?Perseid
For the asker's sample data, this answer will not match tom to tom. because the explosion on spaces does not trim the punctuation character. You might enjoy str_word_count() here with a format parameter of 1.Kellikellia
B
1
<?php

$input = preg_quote('blu', '~'); // don't forget to quote input string!
$data = array('orange', 'blue', 'green', 'red', 'pink', 'brown', 'black');

$result = preg_grep('~' . $input . '~', $data);
print_r($result);

?>
Bedroom answered 22/6, 2016 at 6:8 Comment(1)
This unexplained answer appears to have completely ignored the details and sample data in the posted question.Kellikellia
K
1

This is an ideal task to familiarize yourself with regular expressions so that you have a robust, easily adaptable, and direct script.

It is important to understand your own criteria for matching.

  1. Do you want case-insensitive matching?
  2. Do you want whole word or partial matching?
  3. Do you need to support the possibility of encountering multibyte/unicode characters?

Here is a battery of patterns that demonstrate a few likely combinations. Notice that most of the tooling is done via "pattern modifiers" after the closing pattern delimiter. The \b means a "word boundary"; if you are not familiar with this metacharacter, please invest in more research and find other posts on Stack Overflow that implement them.

Code: (Demo)

$string = 'My nAmE ïs Tom.';

// case-sensitive matching, including partial matching
$array = ['foo', 'nAmE'];
$regex[] = '#' . implode('|', array_map('preg_quote', $array)) . '#';

// case-insensitive matching, including partial matching
$array = ['foo', 'om'];
$regex[] = '#' . implode('|', array_map('preg_quote', $array)) . '#i';

// case-insensitive matching, full word matching only
$array = ['foo', 'tom'];
$regex[] = '#\b(?:' . implode('|', array_map('preg_quote', $array)) . ')\b#i';

// case-insensitive matching, full word matching only, multibyte aware
$array = ['foo', 'ïs'];
$regex[] = '#\b(?:' . implode('|', array_map('preg_quote', $array)) . ')\b#iu';


foreach ($regex as $r) {
    if (preg_match($r, $string, $m)) {
        echo "found '$m[0]' using $r on $string\n";
    } else {
        echo "no match using $r on $string\n";
    }
}

By using a delimiter which is included in preg_quote()'s list of escaped characters (e.g. #), you can simply call preg_quote by its name inside of array_map().

Kellikellia answered 22/10, 2022 at 6:16 Comment(0)
E
1

Here's a reusable helper function that uses the PHP 8+ function str_contains:

function str_contains_any($haystack, $needles, $case_sensitive)
{
    foreach ($needles as $needle)
    {
        if (str_contains($haystack, $needle) || (($case_sensitive === false) && str_contains(strtolower($haystack), strtolower($needle))))
        {
            return true;
        }
    }
    
    return false;
}

Usage example:

$haystack = 'This is a load of shizzle';
$needles = ['fudge', 'shizzle'];
$match_found = str_contains_any($haystack, $needles, true); //true
Edmundedmunda answered 21/12, 2022 at 12:40 Comment(0)
R
0
function contains($str, $arr)
{
  $ptn = '';
  foreach ($arr as $s) {
    if ($ptn != '') $ptn .= '|';
    $ptn .= preg_quote($s, '/');
  }
  return preg_match("/$ptn/i", $str);
}

echo contains('My nAmE is Tom', array('name', 'tom'));
Runkle answered 23/1, 2010 at 20:27 Comment(1)
This answer is missing its educational explanation. For clarity, this will echo an integer value.Kellikellia
A
0

Another way to do with array_intersect() function, Try below code :

function checkString(array $arr, $str) {

  $str = preg_replace( array('/[^ \w]+/', '/\s+/'), ' ', strtolower($str) ); // Remove Special Characters and extra spaces -or- convert to LowerCase

  $matchedString = array_intersect( explode(' ', $str), $arr);

  if ( count($matchedString) > 0 ) {
    return true;
  }
  return false;
}
Albacore answered 17/8, 2017 at 6:42 Comment(1)
Did you mean return array_intersect(str_word_count(strtolower($str), 1), $arr);?Kellikellia
O
0

I have done some testing because I needed to check user inputs against a list of words we didn't allow.

I have found that converting everything to lowercase (because my list is lowercase) and then using array intersect was by far the fastest.

    **First Option I Tested**
    $tempString= explode(' ',strtolower($string));
    $foundWords = array_intersect($tempString,$profanities);
    Time taken: 0.00065207481384277 

    **The second option I tested**
    $tempWords = explode(' ',$words);
    foreach ($tempWords as $word)
    {
        foreach ($profanities as $profanity)
        {
            if (stripos($word,$profanity) !== false) return true;
        }
    }
    Time Taken: 0.024131059646606
Oversize answered 14/7, 2021 at 15:40 Comment(1)
Exploding on spaces then comparing whole elements is not reliable then there may be ANY kind of punctuation in the input string.Kellikellia
F
0

there is easier Method

   $string = 'My nAmE is Tom.';
   $convert=explode(" ",$string,5);
   if(in_array("My", $convert)){

      echo "Ja";
   }else{

      echo "Nein";
   }
Festa answered 4/1, 2022 at 19:37 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Inosculate
This answer is ignoring the asker's requirement to search the string with multiple needles.Kellikellia
A
0
/**
 * ! Only assumes that $needles strings does not contain the character '|'
 */
function contains(string $haystack, array $needles)
{
    $regex = '/' . str_replace('\|', '|', preg_quote(implode('|', $needles))) . '/i';

    return preg_match($regex, $haystack);
}

Code demo: https://3v4l.org/lY6qo#v8.1.4

Regex demo: https://www.phpliveregex.com/p/E4s

Awry answered 19/3, 2022 at 17:0 Comment(1)
This is not a solid approach. If one of the needles needs to match a literal pipe, then your script will make that literal pipe into an OR metacharacter in the regex. Another reason that I do not endorse this answer is because / is not escaped by default via preg_quote(). If a needle contains a forward slash, then the pattern will break. You need to explicitly nominate / as the delimiter parameter.Kellikellia

© 2022 - 2024 — McMap. All rights reserved.