Highlighting strings with different orders in PHP - php

I am trying to highlight the strings. Whether they are in the sequence or not.
Like this -
$str = "star 5 hotel";
$keywords = "5 star hotels";
There is my function. It only highlights the last matching string.Here $str contains the search string. $keyword contains that string which i have stored into database
How can i highlight each matching string.
function highlight($str, $keyword) {
$str = "star hotel 5";
$keyword = "5 star hotel";
foreach($look as $find){
if(strpos($keyword, $find) !== false) {
if(!isset($highlight)){
$highlight[] = $find;
} else {
if(!in_array($find,$highlight)){
$highlight[] = $find;
}
}
}
}
if(isset($highlight)){
foreach($highlight as $replace){
$str = str_ireplace($replace,'<b>'.$replace.'</b>',$keyword);
$stra[] = str_ireplace($replace,'<b>'.$replace.'</b>',$keyword);
echo "<pre>";
print_r ($stra);
echo "</pre>";
}
}
echo $str."<br>";
die();
return $str;
}
But when i put this into an array and when i print this array $stra[]. It given me this
Array
(
[0] => 5 star hotel
[1] => 5 star hotel
[2] => 5 star hotel
)
I can not find way to combine these.
Output : -If have that keyword which is searching . then this must be Highlighted..
5 star hotel

This is what I assumed:-
I assume that you want to search each word of the search sting in the given string and if and only if all the words found then make string in bold (complete string).
Then do like below (Explanation given in comments):-
<?php
$search = "star 5 hotel"; //search string
$string = "5 star hotels"; // string in which you want to search
function highlight($search, $string) {
$new_search = array_unique(array_filter(explode(" " ,$search)));//explode search string
$found_count = 0; //create a counter
foreach($new_search as $find){ // iterate over search words array
if(strpos($string, $find) !== false) { // if word found in the string
$found_count +=1; // increase the counter
}
}
if(count($new_search) == $found_count){ //check that all words found in the string
$string = "<b>". $string ."</b>"; // if yes then make each word of the string bold
}
return $string; //return the newly modified string
}
echo highlight($search, $string); // echo newly modified string
Output:- https://eval.in/838325

You can use an array, and strtr() function. :)
$str = "the quick brown fox";
$keywords = "quick fox brown";
$matches = explode(" ",$keywords);
foreach ($matches as $v) {
$arr_matches[$v] = "<b>".$v."</b>";
}
$str = strtr($str, $arr_matches);

I know it's late but here is one way to make a double loop that will keep the bloat tags to a minimum and handle extra words in the sentence (in what I think is correct way).
It checks if the word is in match list, if yes loop til there is a not matching word.
Add the tags around those two words and go back to main loop.
$str = "star 5 hotel";
$strarr =explode(" ", $str);
$keywords = "a 5 star uinique hotel";
$arr = explode(" ", $keywords);
For($i=0; $i < count($arr) ; $i++){
If(in_array($arr[$i], $strarr)){
$j=$i;
While(in_array($arr[$j], $strarr) && $j < count($arr)){
$j++;
}
$j--;
$arr[$i] = "<b>" . $arr[$i];
$arr[$j] = $arr[$j] . "</b>";
$i=$j;
}
}
Echo implode(" ", $arr);
Output of above example:
a <b>5 star</b> uinique <b>hotel</b>
https://3v4l.org/pKE4X

You can use an array to achieve:
$str = 'This is some 5 start hotel in US';
$keyWords = ['5', 'star', 'hotel'];
$strToCheck = explode(' ', $str);
foreach ($keyWords as $k => $v)
{
if (in_array($v, $strToCheck)) {
//do something
}
}
this creates the string as an array, this means we can use in_array to check, then inside the if statement do whatever :)
references:
http://php.net/manual/en/function.in-array.php
http://php.net/manual/en/function.explode.php

Why don't you just iterate over your keywords and replace them:
$str = "This is a star 5 hotel located in New York";
$keywords = array("5","star","hotels");
foreach (array_expression as $key => $value){
$str = str_replace($value, "<b>".$value."</b>", $str);
}
Simple and failsafe if you don't have double keywords.
Or if your keywords need to be a something-separated string:
$str = "This is a star 5 hotel located in New York";
$keywords = "5 star hotel";
$arraykeywords = explode(" ",$keywords);
foreach (array_expression as $key => $value){
$str = str_replace($value, "<b>".$value."</b>", $str);
}
To filter double keywords use array_unique:
$str = "This is a star 5 hotel located in New York";
$keywords = "5 star hotel star";
$arraykeywords = array_unique(explode(" ",$keywords));
foreach (array_expression as $key => $value){
$str = str_replace($value, "<b>".$value."</b>", $str);
}
After a lot of discussion about regex (see the comments), this is the approach using #Andreas's regex:
$str = "This is a star 5 hotel located in New York";
$keywords = "5 star hotel star";
$pregsearch = "/".implode("|",array_unique(explode(" ",$keywords)))."/g"; //remove dups and join as regex
$str = preg_replace($pregsearch, "<b>$0</b>", $str);
Only recomended if your searching a large string.

Related

Add +1 to each index found in an array

I have a form that takes in a users input and puts it in an array, the user then chooses a word they want to find in said array. Thereafter the array is checked to see what each index of the word is as well as each occurrence of the word in the text.
So if you write the string "What is what" and want to find the word "what" it will say that the position of the word is "0 and 2" whereas I'd like it to say "1 and 3". How do I go about this?
Here's the code:
<form action="sida3.php" method="post">
Text: <textarea name="textarea"></textarea>
<br> Search word: <input type="text" name="search">
<br>
<input type="submit" name="submit" value="Submit">
</form>
<?php
if(isset($_POST['submit'])){
$parts = explode(" ", $_POST['textarea']);
$strName = $_POST['search'];
print_r ($parts);
echo '<br>';
foreach($parts as $item) {
if ($item == $strName) {
$counter++;
}
}
echo "The word $strName can be found at: ";
echo implode(' ', array_keys($parts, $strName));
echo "<br>";
echo "The word $strName was found $counter times";
}
I'm not sure what you're doing with the foreach or if that's where you want to create the positions, but:
foreach($parts as $pos => $item) {
if ($item == $strName) {
$result[] = $pos + 1;
}
}
Then just:
echo implode(' ', $result);
You can map +1 over the array keys before imploding. I also suggested another way to find the search term and count the occurrences, if you're interested in that, but map will work with the way you're currently doing it as well.
$found = array_intersect($parts, [$strName]);
echo "The word $strName can be found at: ";
echo implode(' ', array_map(function($x) { return $x + 1; }, array_keys($found)));
echo "<br>";
echo "The word $strName was found ". count($found) ." times";
You can use array_combine to combine a range from 1 to count of the array with the values.
$str = "This is a string.";
$arr = explode(" ", $str);
$range = range(1,count($arr)); // creates a range from 1 -> count of $arr
$new = array_combine($range, $arr); // sets the range as the key and $arr as the value
var_dump($new);
https://3v4l.org/SUhVM
Now that I look at the question again I see that there is a big flaw we all have missed.
If the word you search for is next to a dot, comma or other symbol it won't be counted.
Here I use the code I had in the answer above but added preg_replace to remove all characters that is not a-Z 0-9 (meaning english alphabet, change to suit your needs).
I then use substr_count to find the number of words without looping.
I use array_intersect to reduce the array to only the items matching $find, and grab the keys with array_keys. The key is the positions of the words.
All done without looping.
$str = "This is a car. Automobile (car) is another word for it. You can also add a hashtag, #car";
$find = "car";
$count = substr_count($str, $find);
$str = preg_replace("/[^a-zA-Z 0-9]/", "", $str);
$arr = explode(" ", $str);
$range = range(1,count($arr));
$new = array_combine($range, $arr);
$positions = array_keys(array_intersect($new, [$find]));
echo $find . " was found " . $count . " times. At positions: " . implode(", ", $positions);
Just looping and matching with == will only find one car in this string.
https://3v4l.org/sHAd5

PHP check if two keywords occur in String

my challange explained on the following example: The keyword combination "gaming notebook" is given.
I want to check whether the two keywords occur in a string. The challange is that the string could look like this:
"Nice Gaming Notebook"
"Notebook for Gaming"
"Notebook for extreme Gaming"
I want my function to return true for all of the three strings. There is a tolerance of 3-4 words that can be between the word combination and as the examples show, I want it to work if the keywords are switched.
So my approach was the following, but it does not seem to work:
$keyword = strtolower("gaming notebook");
$parts = explode(" ", $keyword);
$string = strtolower("Which notebook for good gaming performance");
//point to end of the array
end($parts);
//fetch key of the last element of the array.
$lastElementKey = key($parts);
//iterate the array
$searchExpression = "";
foreach($parts as $k => $v) {
if($k != $lastElementKey) {
$searchExpression .= $v . "|";
} else {
$searchExpression .= $v;
}
}
if(preg_match_all('#\b('. $searchExpression .')\b#', $string, $matches) > 0) {
echo "Jep, keyword combination is in string";
} else {
echo "No, keyword combination is not in string";
}
You want to use something like CMU Sphinx or a natural language index in your database. (See http://dev.mysql.com/doc/refman/5.7/en/fulltext-natural-language.html) Doing a quick search of php libraries turned up "nlp-tools/nlp-tools," however, I have never used a pure php solution to accomplish what you are trying to do.
The solution using preg_match_all and array_intersect functions:
$keywordStr = "gaming notebook";
$string = "Which notebook for good gaming performance,it's my notebook";
$keywords = explode(" ", $keywordStr);
$parts = implode("|", $keywords);
preg_match_all("/\b$parts\b/i", $string, $matches);
// matched items should contain all needed keywords
if (count($keywords) == count(array_intersect($keywords, $matches[0]))) {
echo "Jep, keyword combination is in string";
} else {
echo "No, keyword combination is not in string";
}
<?php
$keyword = strtolower("gaming notebook");
$string = strtolower("Which notebooks for good gaming performance");
function check($keyword,$string){
$parts = explode(' ',$keyword);
$result = false;
$pattern = implode('|',$parts);
preg_match_all("(\b{$pattern}\b)",$string,$matches);
if(isset($matches[0])){
return true;
}
return false;
}
var_dump(check($keyword, $string));
$reg = "/(?:\b$kw1(?:\s+\w+){0,4}\s+$kw2\b)|(?:\b$kw2(?:\s+\w+){0,4}\s+$kw1\b)/";
if (preg_match($reg, $string)) {
echo "OK\n";
} else {
echo "KO\n";
}
This will echo OK when the 2 keywords occur in the string, in any order and separated by at most 4 words.
Explanation:
/
(?: : non capture group
\b$kw1 : keyword 1
(?:\s+\w+){0,4} : followed by 0 to 4 other word
\s+ : space(s)
$kw2\b : keyword 2
)
|
(?: : non capture group
\b$kw2 : keyword 2
(?:\s+\w+){0,4} : followed by 0 to 4 other word
\s+ : space(s)
$kw1\b : keyword 1
)
/

Replacing a string while choosing the order in which the part of strings get replaced first

I'm trying to replace a string with certain words, but I also want to replace it in order of the position in the array. For example, I want to replace "b c", before I try to replace "a b", without changing the position in the original string. By the way, the letters are suppose to represent actual words, and they are not supposed be part of another word. For example, the word "sun" is part of "sunflower", and the word "sunflower" cannot be replaced just because the word "sun" is in it.
$text = "a b c";
$replacement = array("a b" => "ab","b c" => "bc");
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
echo $text = preg_replace_callback("/\b(" . implode("|", $search) . ")\b/", function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
First Result
ab c
Second Result
I switched the position in the array around, thinking that it would affect the order of which the strings get replaced. Sadly, it doesn't work like that, and I got the same result.
$replacement = array("b c" => "bc","a b" => "ab");
ab c
At this point, I realize that it wasn't the position of the array that affects which part of the string that get replaced first, but the order of the part of strings that shows up on the original string that determines the order in which it is replaced by.
So, my question is, is there a way to somehow make it so that it can replace the string in order according to the order in which it is in the array, or in a different way? For example, I want to replace
"b c"
before I try to replace "a b" without changing the position of the original string. Is that doable? Thanks.
[EDIT]
The idea consists to cast the original text to an array (with one element at the beginning, the text). Array items at even index are splitted for each patterns. Since PREG_SPLIT_DELIM_CAPTURE option is used, delimiters have always an odd index and stay untouched once they are matched.
$text = 'a b c';
$rep = ['b c'=>'bc', 'a b'=>'ab', ];
$pats = array_map(function($i) {
return '~\b(' . preg_quote($i, '~') . ')\b~';
}, array_keys($rep));
$parts = (array)$text; // or $parts = [ $text ]; // it's the same
foreach ($pats as $pat) {
$temp = [];
foreach ($parts as $k=>$part) {
if ($k & 1)
$temp[] = $part;
else
$temp = array_merge(
$temp,
preg_split($pat, $part, -1, PREG_SPLIT_DELIM_CAPTURE)
);
}
$parts = $temp;
}
$result = '';
foreach ($parts as $k=>$part) {
$result .= ($k & 1) ? $rep[$part] : $part;
}
echo $result;
I changed your code to represent what (I think) you wanted:
$text = "a b c a b";
$replacement = array("b c" => "bc", "a b" => "ab");
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
for($i = 0; $i < count($replacement); $i++) {
$regex = "/\b(" . $search[$i] . ")\b/";
echo $text = preg_replace_callback($regex, function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
echo "<br>";
}
Basically, instead of relying on the regex to do that work I do a for loop to go through each replacement and create the regex. That way the order of the array matters. I also changed the initial $text to test if it worked

PHP get words with length longer than 3 from string

Is there a function that can cut words from a string that are small length e.g. "the, and, you, me, or" all these short words that are common in all sentences. i want to use this function to fill a fulltext search with criteria
before the method:
$fulltext = "there is ongoing work on creating a formal PHP specification.";
outcome:
$fulltext_method_solution = "there ongoing work creating formal specification."
$fulltext = "there is ongoing work on creating a formal PHP specification.";
$words = array_filter(explode(' ', $fulltext), function($val){
return strlen($val) > 3; // filter words having length > 3 in array
});
$fulltext_method_solution = implode(' ', $words); // join words into sentence
try this:
$fulltext = "there is ongoing work on creating a formal PHP specification.";
$result=array();
$array=explode(" ",$fulltext);
foreach($array as $key=>$val){
if(strlen($val) >3)
$result[]=$val;
}
$res=implode(" ",$result);
You can simply use implode, explode along with array_filter
echo implode(' ',array_filter(explode(' ',$fulltext),function($v){ return strlen($v) > 3;}));
or simply use preg_replace as
echo preg_replace('/\b[a-z]{1,3}\b/i','',$fulltext);
try this:
$stringArray = explode(" ", $fulltext);
foreach ($stringArray as $value)
{
if(strlen($value) < 3)
$fulltext= str_replace(" ".$value." " ," ",$fulltext);
}
Here is a working DEMO
Simply explode the string and check for the strlen()
$fulltext = "there is ongoing work on creating a formal PHP specification.";
$ex = explode(' ',$fulltext);
$res = '';
foreach ($ex as $txt) {
if (strlen($txt) > 3) {
$res .= $txt . ' ';
}
}
echo $res;
By using preg_replace
echo $string = preg_replace(array('/\b\w{1,3}\b/','/\s+/'),array('',' '),$fulltext);
This will also produce the desired results:
<?php
$fulltext = "there is ongoing work on creating a formal PHP specification.";
$fulltext = preg_replace('/(\b.{1,3}\s)/',' ',$fulltext);
echo $fulltext;
?>

Count spaces within exploded quotations

In simplest terms im trying to change the data string if more than 4 spaces are found within quotations. I'm able to do this on a simple string but not within exploded quotes as it becomes an array which count functions wont accept. Is there a regex to do what im looking for in this case or something?
$data = 'Hello World "This is a test string! Jack and Jill went up the hill."';
$halt = 'String had more than 4 spaces.';
$arr = explode('"', $data);
if (substr_count($arr, ' ') >= 4) {
$data = implode('"', $arr);
$data = $halt;
As far as I understand your request, this will do the job
$data = 'Hello World "This is a test string! Jack and Jill went up the hill."';
$halt = 'String had more than 4 spaces.';
// split $data on " and captures them
$arr = preg_split('/(")/', $data, -1, PREG_SPLIT_DELIM_CAPTURE);
// must we count spaces ?
$countspace = 0;
foreach ($arr as $str) {
// swap $countspace when " is encountered
if ($str == '"') $countspace = !$countspace;
// we have to count spaces
if ($countspace) {
// more than 4 spaces
if (substr_count($str, ' ') >= 4) {
// change data
$data = $halt;
break;
}
}
}
echo $data,"\n";
output:
String had more than 4 spaces.
If you define:
function count_spaces($str) {return substr_count($str, ' '); }
you can then use array_sum(array_map("count_spaces", $arr)) to count all of the spaces in all of the strings in $arr.

Categories