We have a function in PHP, in_array, which checks if the given value is present in an array. I guess I wanna do a reverse of it. I have a set of strings:
$words = array("hello", "world");
And, I wanna check, if a given string has these words, by giving parameters whether all or any.
$string = "Hello, World!";
$second = "Hello and Welcome!";
in_string($words, $string, "all");
in_string($words, $string, "any");
What I have right now is, using stripos(). I do not wanna use regex.
Current Code:
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
foreach ($words as $value)
$isFound = $isFound && (stripos($string, $value) || false);
else
foreach ($words as $value)
$isFound = $isFound || (stripos($string, $value) || false);
return $isFound;
}
?>
My question is, can this be improved? Any thoughts?
Update #1: Current Code Updated:
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
{
$isFound = true;
foreach ($words as $value)
$isFound = $isFound && (stripos($string, $value) !== false);
return $isFound;
}
else
{
$isFound = true;
foreach ($words as $value)
if (stripos($string, $value) !== false) return true;
return $isFound;
}
}
Now the function is working as expected. But I need a better performance in the foreach() part and all. Anything possible for improvements?
Update #2: Improvements Added
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all")
{
foreach ($words as $value)
if (stripos($string, $value) === false)
return false;
return true;
}
else
{
foreach ($words as $value)
if (stripos($string, $value) !== false)
return true;
return false;
}
}
?>
<?php
/**
* Checks if the given words is found in a string or not.
*
* #param Array $words The array of words to be given.
* #param String $string The string to be checked on.
* #param String $option all - should have all the words in the array. any - should have any of the words in the array
* #return boolean True, if found, False if not found, depending on the $option
*/
function in_string ($words, $string, $option)
{
if ($option == "all") {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && (stripos($string, $value) !== false); // returns boolean false if nothing is found, not 0
if (!$isFound) break; // if a word wasn't found, there is no need to continue
}
} else {
$isFound = false;
foreach ($words as $value) {
$isFound = $isFound || (stripos($string, $value) !== false);
if ($isFound) break; // if a word was found, there is no need to continue
}
}
return $isFound;
}
?>
Just the same code as the person below me, with the break; added. I don't have enough rep to comment apparently
Divide the logic and you'll get simpler code in each function, better performance and each function will solve a concrete task.
<?php
function in_string_all ($string, $words) {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && stripos($string, $value) !== false;
if (!$isFound)
break;
}
return $isFound;
}
function in_string_any ($string, $words) {
foreach ($words as $value)
if (stripos($string, $value) !== false)
return true;
return false;
}
?>
Using strpos does not work because it will return true for partial string matches. examples searching "in" in "insersection" results in a false positive.
Here is an alternative extracting words from the target string and making an intersection between arrays.
/**
* Checks if a array of given words is found in a string.
*
* #param array $words The array of words to be given.
* #param string $string The string to be checked on.
* #param boolean $exactMatch true - should have all the words in the array. false - should have any of the words in the array
* #param boolean $insensitive true to compare insensitive. false to compare sensitive
* #return boolean True, if found, False if not found, depending on the $option
*/
private function arrayInString (array $words, $string, $exactMatch = true, $insensitive = true)
{
$stringArr = explode(" ", preg_replace('/[^a-z0-9]+/i', ' ', $string));
if ($insensitive) {
$words = array_map('strtolower', $words);
$stringArr = array_map('strtolower', $stringArr);
}
$intersectionCount = count(array_intersect($words, $stringArr));
if ($exactMatch && $intersectionCount == count($words)) {
return true;
}
if (!$exactMatch && $intersectionCount > 0) {
return true;
}
return false;
}
Use strpos() instead of substr() or strstr() because you only care about the word being present, you don't need to parse anything out. According to the docs, it is faster.
From the Manual:
If you only want to determine if a particular needle occurs within haystack, use the faster and less memory intensive function strpos() instead.
You need to init $isFound, use stripos() for case-insensitive search, and check that stripos() returns false, not 0. (0 means that $string starts with $value)
function in_string ($words, $string, $option)
{
if ($option == "all") {
$isFound = true;
foreach ($words as $value) {
$isFound = $isFound && (stripos($string, $value) !== false);
}
} else {
$isFound = false;
foreach ($words as $value) {
$isFound = $isFound || (stripos($string, $value) !== false);
}
}
return $isFound;
}
Related
I am trying to loop through a string that contains html from a scraped webpage. First I look to return all links that contain the word "result" and then I would like to organize all the links that contain one of four cases, "base", "second", "third" or "latest" and create a fluid array.
Below is what I have come up with but it returns "Warning: strpos(): needle is not a string or an integer". I cannot seem to get the array cases to work.
Any help would be greatly appreciated. Thank you
$key = "results";
$reportKey = array("base", "second", "third","latest");
$keyArray = array();
foreach($html->find('a') as $element){
if (strpos($element->href, $key) !== false){
if (strpos($element->href, $reportKey) !== false){
$keyArray[] = $element->href;
}
}
}
echo "<pre>" . print_r($keyArray) . "</pre> ";
You can't use an array as a needle in strpos. Change second if to:
if (str_replace($reportKey, "", $element->href) === $element->href) {
$keyArray[] = $element->href;
}
strpos() does not allow more than one needle, you can do this:
$key = "results";
$reportKey = array("base", "second", "third","latest");
$keyArray = array();
foreach($html->find('a') as $element)
{
if (strpos($element->href, $key) !== false){
if (
strpos($element->href, $reportKey[0]) !== false
|| strpos($element->href, $reportKey[1]) !== false
|| strpos($element->href, $reportKey[2]) !== false
|| strpos($element->href, $reportKey[3]) !== false
){
$keyArray[] = $element->href;
}
}
}
echo "<pre>" . print_r($keyArray) . "</pre> ";
You could also do your own function, this is only an example:
function multi_strpos($string, $check, $getResults = false)
{
$result = array();
$check = (array) $check;
foreach ($check as $s)
{
$pos = strpos($string, $s);
if ($pos !== false)
{
if ($getResults)
{
$result[$s] = $pos;
}
else
{
return $pos;
}
}
}
return empty($result) ? false : $result;
}
A solution using array_map() and in_array():
$key = 'results';
$reportKey = ['base', 'second', 'third', 'latest'];
$keyArray = [];
foreach($html->find('a') as $element) {
if (false !== strpos($element->href, $key)) {
// I changed the condition here
$pos = array_map(fn($k) => strpos($element->href, $k) !== false, $reportKey);
if (in_array(true, $pos)){
$keyArray[] = $element->href;
}
}
}
$pos will be an array containing booleans based on matches between $element->href and $reportKey items.
Then we check with in_array() if it matched at least one time.
hi i have an array of about 20/30 items big.
i need to have it loop threw the array and echo out only the items with the text p1 in them.
the array looks like so
"lolly","lollyp1","top","topp1","bum","bump1","gee","geep1"
and so on
i have tried to use something like this
foreach ($arr as $value) {
$needle = htmlspecialchars($_GET["usr"]);
$ret = array_keys(array_filter($arr, function($var) use ($needle){
return strpos($var, $needle) !== false;
}));
but all this gives me is a blank page or 1s
how can i have it echo out the items with p1 in them ?
Try This:
$needle = htmlspecialchars($_GET["usr"]);
$rtnArray = array();
foreach ($arr as $value) {
$rtnArray = strpos($value,$needle);
};
return $rtnArray;
If your trying to write directly to the page the lose the $rtnarray and echo:
$needle = htmlspecialchars($_GET["usr"]);
foreach ($arr as $value) {
echo strpos($value,$needle);
};
To only show ones with 'p1' then filter:
$needle = htmlspecialchars($_GET["usr"]);
foreach ($arr as $value) {
$temp = strpos($value,$needle);
if($temp > 1){
echo $value;
}
};
Using a direct loop with string-comparison would be a simple way to go here:
$needle = $_GET['usr'];
$matches = array();
foreach ($arr as $key => $value) {
if (strpos($value, $needle) !== false) {
$matches[] = $key;
}
}
The use of array_filter() in your post should work, pending the version of PHP you're using. Try updating to use a separate / defined function:
function find_needle($var) {
global $needle;
return strpos($var, $needle) !== false;
}
$ret = array_keys(array_filter($arr, 'find_needle'));
Codepad Example of the second sample
I'm trying to validate a string to an array of numbers. If the string only contains numbers then the function should validate, but in_array isn't working, any suggestions?
$list = array(0,1,2,3,4,5,6,7,8,9);
$word = 'word';
$split = str_split($word);
foreach ($split as $s) {
if (!in_array($s, $list)) {
print 'asdf';
}
}
here is the class:
class Validate_Rule_Whitelist {
public function validate($data, $whitelist) {
if (!Validate_Rule_Type_Character::getInstance()->validate($data)) {
return false;
}
$invalids = array();
$data_array = str_split($data);
foreach ($data_array as $k => $char) {
if (!in_array($char, $whitelist)) {
$invalids[] = 'Invalid character at position '.$k.'.';
}
}
if (!empty($invalids)) {
$message = implode(' ', $invalids);
return $message;
}
return true;
}
}
in_array comparison with loosely typed values is somewhat strange. What would work in your case is:
$list = array('0','1','2','3','4','5','6','7','8','9');
$word = 'word';
$split = str_split($word);
foreach ($split as $s) {
if (!in_array($s, $list, true)) {
print 'asdf';
}
}
This compares strings with strings and results in no surprises.
But, as noted in the comments already, this is quite wrong way to do things and it is much better to use filter_var() or regular expressions** to achieve what you're trying.
it's ugly as sin but it works, no elegant solution here, just a double loop, if you see any problems please let me know
$match = array();
foreach ($data_array as $k => $char) {
foreach ($whitelist as $w) {
if (!isset($match[$k])) {
if ($char === $w) {
$match[$k] = true;
}
}
}
if (!isset($match[$k]) || $match[$k] !== true) {
$invalids[$k] = 'Invalid character at position '.$k.'.';
}
}
Something along the lines of this should work:
<?php
$validate_me = '123xyz';
if(preg_match("/[^0-9]/", $validate_me, $matches))
print "non-digit detected";
?>
update: add the $type = gettype ... settype($char, $type) to allow === to function correctly when checking for integers
foreach ($data_array as $k => $char) {
foreach ($whitelist as $w) {
if (!isset($match[$k])) {
$type = gettype($w);
if (gettype($char) !== $type) {
settype($char, $type);
}
if ($char === $w) {
$match[$k] = true;
}
}
}
...
How would I improve this functions for that is:
Searches an array for fields names which either:
a) Exactly matches
b) beings with "_" check if string begins with
c) ends with "_" check if string ends with
E.g I have a list of column names:
array(
'customer_name',
'customer_lastname',
'customer_streetname',
'customer_dob',
'system_modified'
)
And another array with formatting conditions:
array(
'_dob' => 'date_dob',
'_name' => 'varchar',
'customer_name' => 'html_text',
'system_' => 'required'
)
Results apply the conditions against the column names:
1. customer_name = html_text (exact matches have higher preference)
2. customer_lastname = varchar
3. customer_streetname =
4. customer_dob = dob
5. system_modified = required
Current have this:
protected function matchPatterns($string) {
$return = array();
$parrerns = $this->_normaliseArrayItem($this->getPatterns());
foreach ($parrerns as $match) {
// If exact match
if($string == $match) {
$return[] = $match;
break;
// Else if begins with _ and ends with string.
} elseif($string[0] == "_" && substr_compare($string, $match, -strlen($match), strlen($match)) === 0) {
$return[] = $match;
}
} // end loop
return $return;
}
/**
* Return an array of validation patterns.
*
* #return string[]
*/
public function getPatterns() {
return $this->_patterns;
}
/**
* Returns an item as array rather than single item.
*
* #param string[] $data
* #return string[]
*/
protected function _normaliseArrayItem($data) {
if(!isset($data[0])|| !is_array($data)) {
$tmp[] = $data;
$data = $tmp;
}
return $data;
}
foreach ($parrerns as $match => $format) {
// If exact match
if ($string == $match) {
$return[] = $format;
break;
// Else if begins with _ and string ends with it.
} elseif ($match[0] == "_" && substr_compare($string, $match, -strlen($match), strlen($match)) === 0) {
$return[] = $format;
// Else if ends with _ and string begins with it
} elseif (substr($match, -1) == "_" && substr_compare($string, $match, 0, strlen($match)) == 0) {
$return[] = $format;
}
}
Adapted from Marc's solution.
if(substr($str, 0, 1) === '_' || substr($str, -1) === '_') {
// it starts or ends with an underscore
} else if($str == $match) {
// it's the same
}
I don't entirely understand your question though.
I'm trying to do a scrape with SimpleHTMLDom and seem to be running in to a problem.
My code is as follows :
$table = $html->find('table',0);
$theData = array();
foreach(($table->find('tr')) as $row) {
$rowData = array();
foreach($row->find('td') as $cell) {
$rowData[] = $cell->innertext;
}
$theData[] = $rowData;
}
function array_find($needle, array $haystack)
{
foreach ($haystack as $key => $value) {
if (false !== stripos($needle, $value)) {
return $key;
}
}
return false;
}
$searchString = "hospitalist";
$position = array_find($searchString, $theData);
echo ($position);
Which yields the following error:
Warning: stripos() [function.stripos]: needle is not a string or an integer in C:\xampp\htdocs\main.php on line 85
What am I doing wrong?
You have the order of the actual parameters reversed in your call to stripos. See http://us3.php.net/manual/en/function.stripos.php. Just reverse the order of the arguments and that error should be fixed.
Change:
if (false !== stripos($needle, $value)) {
to
if (false !== stripos($value, $needle)) {
From the docs, you should be passing in the needle second, not first. Try this:
function array_find($needle, array $haystack)
{
foreach ($haystack as $key => $value) {
if (false !== stripos($value, $needle)) {
return $key;
}
}
return false;
}
The message is referring to the function argument of stripos and not your variable named $needle.
int stripos ( string $haystack , string $needle [, int $offset = 0 ] )
It is actually complaining about the needle $value