tags:

views:

565

answers:

5

Hello, and first of all, thank you for taking the time to read my question. Basically, I have an array of keywords, and a piece of text. I am wondering what would be the best way to find out if any of those keywords are present in the text, bearing in mind performance issues.

I was thinking of just looping over the array and doing a strpos() for each keyword, but with well over ten thousand words in the array, it takes PHP a bit of time to do it, and so I was wondering if there is a more efficient way to do it.

Thank you in advance, Bruno De Barros.

A: 

I really don't know if it is more efficient, but you could try to put them all in a regex like this: (keyword1|keyword2|...) With the preg_quote function you can escape the keywords for the regex. If you set the compiled option, it might be more efficient when using it with multiple strings.

eWolf
10,000 keywords would cause the regular expression parser to barf all over the place.
Byron Whitlock
+2  A: 

Depending on the size of the string You could use a hash to make it faster.

First iterate the text. For each word, assign it to an array:

 foreach (preg_split("/\s/", $text) as $word)
 {
  $string[$word] = 1;
 }

Then iterate the keywords checking the $string:

 foreach ($keywords as $keyword)
 {
  if (isset($string[$keyword]))
  {
   // $keyword exists in string
  }
 }

EDIT If your text is much smaller than your keywords, do it backwards, check the keywords for each word in the text. This would likley be faster than the above if the text is pretty short.

 foreach (preg_split("/\s/", $text) as $word)
 {
 if (isset($keywords[$word]))
 {
  //might be faster if sizeof($text) < sizeof($keywords)
 }
}
Byron Whitlock
I realised a better way to do it from your reply. Explode the text string into separate words, and then for each word, see if it's in the array. Using in_array instead of strpos. I wonder if that would be faster. Thank you, Byron. :)
Bruno De Barros
Heh, I think we both had the AHAH moment at the same time here ;) Good Luck
Byron Whitlock
Except, in_array can be slow if you search per-word. What you really want is a binary search instead.
The Wicked Flea
@Flea hence the hash.
Byron Whitlock
Byron, the hash makes more sense to me now than in_array itself, and is much faster. I have about 10,000 keywords, and the piece of text is somewhere around 250-500 words, so it is much faster to simply do isset($keywords[$word])), as you mentioned in your answer. Thank you for everything :)
Bruno De Barros
A: 

Assuming the formatting and only that you care if any (not which) of the keywords exist, you could try something like:

$keywords = array( "dog", "cat" );

// get a valid regex
$test = "(\b".implode( "\b)|(\b", $keywords )."\b)";

if( preg_match( $test, "there is a dog chasing a cat down the road" ) )
    print "keyword hit";
Kevin Peno
NO. 10,000+ keywords.
Byron Whitlock
You're right. But the question did not pose such a size at the time ;)
Kevin Peno
Yes it was. (check the revision history)
Byron Whitlock
A: 

You could dump the text into an array and do a array_intersect_key on the two arrays. I am not sure of the performance of this though...

Buggabill
A: 

Working off eWolf's idea...

foreach($keywords as &$keyword) {
  $keyword = preg_quote($keyword);
}

$regex = "/(". implode('|', $keywords) .")/";

return preg_match($regex, $str);

You don't have to check for boundaries if you don't want to, but if you do just surround the group (the () characters) with \b then it'll match only a given word. And you'll want to make sure all the array's members are preg_quoted, for safety.

The Wicked Flea
Over 10,000 keywords!!!!!
Byron Whitlock