Get count of substring occurrence in an array - php

I would like to get a count of how many times a substring occurs in an array. This is for a Drupal site so I need to use PHP code
$ar_holding = array('usa-ny-nyc','usa-fl-ftl', 'usa-nj-hb',
'usa-ny-wch', 'usa-ny-li');
I need to be able to call a function like foo($ar_holding, 'usa-ny-'); and have it return 3 from the $ar_holding array. I know about the in_array() function but that returns the index of the first occurrence of a string. I need the function to search for substrings and return a count.

You could use preg_grep():
$count = count( preg_grep( "/^usa-ny-/", $ar_holding ) );
This will count the number of values that begin with "usa-ny-". If you want to include values that contain the string at any position, remove the caret (^).
If you want a function that can be used to search for arbitrary strings, you should also use preg_quote():
function foo ( $array, $string ) {
$string = preg_quote( $string, "/" );
return count( preg_grep( "/^$string/", $array ) );
}

If you need to search from the beginning of the string, the following works:
$ar_holding = array('usa-ny-nyc','usa-fl-ftl', 'usa-nj-hb',
'usa-ny-wch', 'usa-ny-li');
$str = '|'.implode('|', $ar_holding);
echo substr_count($str, '|usa-ny-');
It makes use of the implode function to concat all array values with the | character in between (and before the first element), so you can search for this prefix with your search term. substr_count does the dirty work then.
The | acts as a control character, so it can not be part of the values in the array (which is not the case), just saying in case your data changes.

$count = subtr_count(implode("\x00",$ar_holding),"usa-ny-");
The \x00 is to be almost-certain that you won't end up causing overlaps that match by joining the array together (the only time it can happen is if you're searching for null bytes)

I don't see any reason to overcomplicate this task.
Iterate the array and add 1 to the count everytime a value starts with the search string.
Code: (Demo: https://3v4l.org/5Lq3Y )
function foo($ar_holding, $starts_with) {
$count = 0;
foreach ($ar_holding as $v) {
if (strpos($v, $starts_with)===0) {
++$count;
}
}
return $count;
}
$ar_holding = array('usa-ny-nyc','usa-fl-ftl', 'usa-nj-hb',
'usa-ny-wch', 'usa-ny-li');
echo foo($ar_holding, "usa-ny-"); // 3
Or if you don't wish to declare any temporary variables:
function foo($ar_holding, $starts_with) {
return sizeof(
array_filter($ar_holding, function($v)use($starts_with){
return strpos($v, $starts_with)===0;
})
);
}

Related

array_filter with element modification [duplicate]

This question already has answers here:
Explode string on commas and trim potential spaces from each value
(11 answers)
Closed 6 months ago.
I'm trying to make a clean array from a string that my users will define.
The string can contain non-valid IDs, spaces, etc. I'm checking the elements using a value object in a callback function for array_filter.
$definedIds = "123,1234,1243, 12434 , asdf"; //from users panel
$validIds = array_map(
'trim',
array_filter(
explode(",", $definedIds),
function ($i) {
try {
new Id(trim($i));
return true;
} catch (\Exception $e) {
return false;
}
}
)
);
This works fine, but I'm applying trim twice. Is there a better way to do this or a different PHP function in which I can modify the element before keeping it in the returned array?
NOTE: I also could call array_map in the first parameter of array_filter, but I would be looping through the array twice anyway.
It depends on whether you care about performance. If you do, don't use map+filter, but use a plain for loop and manipulate your array in place:
$arr = explode(',', $input);
for($i=count($arr)-1; $i>=0; $i--) {
// make this return trimmed string, or false,
// and have it trim the input instead of doing
// that upfront before passing it into the function.
$v = $arr[$i] = Id.makeValid($arr[$i]);
// weed out invalid ids
if ($v === false) {
array_splice($arr, $i, 1);
}
}
// at this point, $arr only contains valid, cleaned ids
Of course, if this is inconsequential code, then trimming twice is really not going to make a performance difference, but you can still clean things up:
$arr = explode(',', $input);
$arr = array_filter(
array_map('Id.isValidId', $arr),
function ($i) {
return $i !== false;
}
);
In this example we first map using that function, so we get an array of ids and false values, and then we filter that so that everything that's false gets thrown away, rather than first filtering, and then mapping.
(In both cases the code that's responsible for checking validity is in the Id class, and it either returns a cleaned id, or false)
Actually you can do it by different way but If I were you then I'll do it this way. Here I just used only one trim
<?php
$definedIds = "123,1234,1243, 12434 , asdf"; //from users panel
function my_filter($b){
if(is_numeric($b)){
return true;
}
}
print '<pre>';
$trimmed = array_map('trim',explode(',',$definedIds));
print_r(array_filter($trimmed,my_filter));
print '</pre>';
?>
Program Output:
Array
(
[0] => 123
[1] => 1234
[2] => 1243
[3] => 12434
)
DEMO: https://eval.in/997812

Count number of values in array with variables

I'm trying to count the number of times a certain value turns up in an array. Except this value will always increment by 1, and there is an unknown number of these values in the array.
Example:
$first = array( 'my-value-1','my-value-2','my-value-3' );
$second = array( 'my-value-1','my-value-2','my-value-3', 'my-value-4', 'my-value-5' );
My goal is to be able to retrieve a count of 3 for $first and a count of 5 for $second in the example above.
There may be other values in the array, but the only values I'm interested in counting are the ones that start with my-value-.
I won't know the number of the values in the array, but they will always start with my-value- with a number added to the end.
Is there a way to count the number of times my-value- shows up in the array with some sort of wildcard?
Use a regex to filter the array and count the values that match. You could also use ^ to force it to be at the beginning ^my-value-\d+:
$count = count(preg_grep('/my-value-\d+/', $first));
You could also do it this way. Again you could use === 0 instead to make it match at the beginning:
$count = count(array_filter($first, function($v) {
return strpos($v, 'my-value-') !== false;
}));
A quick function to count values by providing a partial string and the target array.
Note: search is case-insensitive.
function countValues($prefix, $array) {
return count(array_filter($array, function($item) use ($prefix) {
return stripos($item, $prefix) !== false;
}));
}
Usage:
$count = countValues('my-value', $first);
According to your question, "they will always start with my-value- with a number added to the end." So you don't need a regex, just a count of the number of items in your array, using PHP's built-in count() function. Try:
<?php
$first = array( 'my-value-1','my-value-2','my-value-3' );
$second = array( 'my-value-1','my-value-2','my-value-3', 'my-value-4', 'my-value-5' );
$size_of_first = count($first);
$size_of_first = count($first);
echo $size_of_first; //Will echo 3
echo $size_of_first; //Will echo 5
?>

Compare All strings in a array to all strings in another array, PHP

What i am trying to do is really but i am going into a lot of detail to make sure it is easily understandable.
I have a array that has a few strings in it. I then have another that has few other short strings in it usually one or two words.
I need it so that if my app finds one of the string words in the second array, in one of the first arrays string it will proceed to the next action.
So for example if one of the strings in the first array is "This is PHP Code" and then one of the strings in the second is "PHP" Then it finds a match it proceeds to the next action. I can do this using this code:
for ( $i = 0; $i < count($Array); $i++) {
$Arrays = strpos($Array[$i],$SecondArray[$i]);
if ($Arrays === false) {
echo 'Not Found Array String';
}
else {
echo 'Found Array String';
However this only compares the First Array object at the current index in the loop with the Second Array objects current index in the loop.
I need it to compare all the values in the array, so that it searches every value in the first array for the First Value in the second array, then every value in the First array for the Second value in the second array and so on.
I think i have to do two loops? I tried this but had problems with the array only returning the first value.
If anyone could help it would be appreciated!
Ill mark the correct answer and + 1 any helpful comments!
Thanks!
Maybe the following is a solution:
// loop through array1
foreach($array1 as $line) {
// check if the word is found
$word_found = false;
// explode on every word
$words = explode(" ", $line);
// loop through every word
foreach($words as $word) {
if(in_array($word, $array2)) {
$word_found = true;
break;
}
}
// if the word is found do something
if($word_found) {
echo "There is a match found.";
} else {
echo "No match found."
}
}
Should give you the result you want. I'm absolute sure there is a more efficient way to do this.. but thats for you 2 find out i quess.. good luck
You can first normalize your data and then use PHP's build in array functions to get the intersection between two arrays.
First of all convert each array with those multiple string with multiple words in there into an array only containing all words.
A helpful function to get all words from a string can be str_word_count.
Then compare those two "all words" arrays with each other using array_intersect.
Something like this:
$words1 = array_unique(str_word_count(implode(' ', $Array), 1));
$words2 = array_unique(str_word_count(implode(' ', $SecondArray), 1));
$intersection = array_intersect($words1, $words2);
if(count($intersection))
{
# there is a match!
}
function findUnit($packaging_units, $packaging)
{
foreach ($packaging_units as $packaging_unit) {
if (str_contains(strtoupper($packaging[3]), $packaging_unit)) {
return $packaging_unit;
}
}
}
Here First parameter is array and second one is variable to find

PHP: How do I check the contents of an array for my string?

I a string that is coming from my database table say $needle.
If te needle is not in my array, then I want to add it to my array.
If it IS in my array then so long as it is in only twice, then I still
want to add it to my array (so three times will be the maximum)
In order to check to see is if $needle is in my $haystack array, do I
need to loop through the array with strpos() or is there a quicker method ?
There are many needles in the table so I start by looping through
the select result.
This is the schematic of what I am trying to do...
$haystack = array();
while( $row = mysql_fetch_assoc($result)) {
$needle = $row['data'];
$num = no. of times $needle is in $haystack // $haystack is an array
if ($num < 3 ) {
$$haystack[] = $needle; // hopfully this adds the needle
}
} // end while. Get next needle.
Does anyone know how do I do this bit:
$num = no. of times $needle is in $haystack
thanks
You can use array_count_values() to first generate a map containing the frequency for each value, and then only increment the value if the value count in the map was < 3, for instance:
$original_values_count = array_count_values($values);
foreach ($values as $value)
if ($original_values_count[$value] < 3)
$values[] = $value;
As looping cannot be completely avoided, I'd say it's a good idea to opt for using a native PHP function in terms of speed, compared to looping all values manually.
Did you mean array_count_values() to return the occurrences of all the unique values?
<?php
$a=array("Cat","Dog","Horse","Dog");
print_r(array_count_values($a));
?>
The output of the code above will be:
Array (
[Cat] => 1,
[Dog] => 2,
[Horse] => 1
)
There is also array_map() function, which applies given function to every element of array.
Maybe something like the following? Just changing Miek's code a little.
$haystack_count = array_count_values($haystack);
if ($haystack_count[$needle] < 3)
$haystack[] = $needle;

Searching an array of different strings inside a single string in PHP

I have an array of strings that I want to try and match to the end of a normal string. I'm not sure the best way to do this in PHP.
This is sorta what I am trying to do:
Example:
Input: abcde
Search array: er, wr, de
Match: de
My first thought was to write a loop that goes through the array and crafts a regular expression by adding "\b" on the end of each string and then check if it is found in the input string. While this would work it seems sorta inefficient to loop through the entire array. I've been told regular expressions are slow in PHP and don't want to implement something that will take me down the wrong path.
Is there a better way to see if one of the strings in my array occurs at the end of the input string?
The preg_filter() function looks like it might do the job but is for PHP 5.3+ and I am still sticking with 5.2.11 stable.
For something this simple, you don't need a regex. You can either loop over the array, and use strpos to see if the index is length(input) - length(test). If each entry in the search array is always of a constant length, you can also speed things up by chopping the end off the input, then comparing that to each item in the array.
You can't avoid going through the whole array, as in the worst general case, the item that matches will be at the end of the array. However, unless the array is huge, I wouldn't worry too much about performance - it will be much faster than you think.
Though compiling the regular expression takes some time I wouldn't dismiss using pcre so easily. Unless you find a compare function that takes several needles you need a loop for the needles and executing the loop + calling the compare function for each single needle takes time, too.
Let's take a test script that fetches all the function names from php.net and looks for certain endings. This was only an adhoc script but I suppose no matter which strcmp-ish function + loop you use it will be slower than the simple pcre pattern (in this case).
count($hs)=5549
pcre: 4.377925157547 s
substr_compare: 7.951938867569 s
identical results: bool(true)
This was the result when search for nine different patterns. If there were only two ('yadda', 'ge') both methods took the same time.
Feel free to criticize the test script (aren't there always errors in synthetic tests that are obvious for everyone but oneself? ;-) )
<?php
/* get the test data
All the function names from php.net
*/
$doc = new DOMDocument;
$doc->loadhtmlfile('http://docs.php.net/quickref.php');
$xpath = new DOMXPath($doc);
$hs = array();
foreach( $xpath->query('//a') as $a ) {
$hs[] = $a->textContent;
}
echo 'count($hs)=', count($hs), "\n";
// should find:
// ge, e.g. imagick_adaptiveblurimage
// ing, e.g. m_setblocking
// name, e.g. basename
// ions, e.g. assert_options
$ns = array('yadda', 'ge', 'foo', 'ing', 'bar', 'name', 'abcd', 'ions', 'baz');
sleep(1);
/* test 1: pcre */
$start = microtime(true);
for($run=0; $run<100; $run++) {
$matchesA = array();
$pattern = '/(?:' . join('|', $ns) . ')$/';
foreach($hs as $haystack) {
if ( preg_match($pattern, $haystack, $m) ) {
#$matchesA[$m[0]]+= 1;
}
}
}
echo "pcre: ", microtime(true)-$start, " s\n";
flush();
sleep(1);
/* test 2: loop + substr_compare */
$start = microtime(true);
for($run=0; $run<100; $run++) {
$matchesB = array();
foreach( $hs as $haystack ) {
$hlen = strlen($haystack);
foreach( $ns as $needle ) {
$nlen = strlen($needle);
if ( $hlen >= $nlen && 0===substr_compare($haystack, $needle, -$nlen) ) {
#$matchesB[$needle]+= 1;
}
}
}
}
echo "substr_compare: ", microtime(true)-$start, " s\n";
echo 'identical results: '; var_dump($matchesA===$matchesB);
I might approach this backwards;
if your string-ending list is fixed or varies rarely,
I would start by preprocessing it to make it easy to match against,
then grab the end of your string and see if it matches!
Sample code:
<?php
// Test whether string ends in predetermined list of suffixes
// Input: string to test
// Output: if matching suffix found, returns suffix as string, else boolean false
function findMatch($str) {
$matchTo = array(
2 => array( 'ge' => true, 'de' => true ),
3 => array( 'foo' => true, 'bar' => true, 'baz' => true ),
4 => array( 'abcd' => true, 'efgh' => true )
);
foreach($matchTo as $length => $list) {
$end = substr($str, -$length);
if (isset($list[$end]))
return $end;
}
return $false;
}
?>
This might be an overkill but you can try the following.
Create a hash for each entry of your search array and store them as keys in the array (that will be your lookup array).
Then go from the end of your input string one character at time (e, de,cde and etc) and compute a hash on a substring at each iteration. If a hash is in your lookup array, you have much.

Categories