array_filter with element modification [duplicate] - php

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

Related

How to search $_POST for dynamic added input values?

I have a form where i add inputs on the fly.
The names of the inputs start the same but has a number added on the end:
<input type="number" name="number_of_floors_house_'+i+'">
Now, when i post my form i would like to loop threw this inputs.
So i need something like array_search($_POST['number_of_floors_house_%']
Or find all $POST['keys'] that starts with 'number_of_floors_house' and loop them threw to get it's values :)
Can you help please?
EDIT
I've tried:
$houses_and_floors = array_search('number_of_floors_house_%', $_POST);
var_dump($houses_and_floors);
Change your input to something like this:
<input type="number" name="number_of_floors['+i+']">
The square brackets make the input submit as an array, so you could then loop through all the values easier:
foreach($_POST['number_of_floors'] as $house_number => $value) {
// $house_number will be whatever number was added to the form element.
// $value is the actual input boxes value
}
You could use an array_filter() to pick out the pieces of $_POST you actually want like this
//fake up a post array
$_POST = ['aa'=>1, 'bb'=>2, 'number_of_floors_house_1'=>2, 'number_of_floors_house_2'=>2,'number_of_floors_house_3'=>4];
function picker($v, $k)
{
return strpos($k, 'number_of_floors_house') !== FALSE;
}
$res = array_filter($_POST, 'picker', ARRAY_FILTER_USE_BOTH);
print_r($res);
RESULT
Array
(
[number_of_floors_house_1] => 2
[number_of_floors_house_2] => 2
[number_of_floors_house_3] => 4
)
You can filter for array keys matching a given prefix with array_filter() and strncmp():
$prefix = 'number_of_floors_';
$length = strlen($prefix);
$nof = array_filter($_POST, function($key) use ($prefix, $length) {
return strncmp($prefix, $key, $length) === 0;
}, ARRAY_FILTER_USE_KEY);
This will filter your post data for all array members with the defined prefix. Note that we're checking the prefix length outside the function to avoid a redundant repeated evaluation inside the function (which is executed for once each array member). You could, of course, also just hard-code this for a single use-case and skip the leading variables above:
$nof = array_filter($_POST, function($key) {
return strncmp('number_of_floors_', $key, 17) === 0;
}, ARRAY_FILTER_USE_KEY);
...which would compare the first 17 characters of each key with the defined string. Again, you could also do this with substr() in place of strncmp(), with return substr($key, 0, $length) === $prefix as the filter condition. I chose the former for this example, since it's a function explicitly for the "binary safe string comparison of the first n characters".

How to determine if an array contains anything but a specific value?

Given the below array of values, what is the best way to determine if the array contains anything other than the value 4?
$values = [1,2,3,4,5,6];
For clarity, I don't want to check that 4 simply doesn't exist, as 4 is still allowed to be in the array. I want to check the existence of any other number.
The only way I can think to do it is with a function, something like the below:
function checkOtherValuesExist(array $values, $search) {
foreach ($values as $value) {
if ($value !== $search)
return TRUE;
}
}
}
Simply do array_diff to get array without 4's
$diff = array_diff($values, [4]);
if (!empty($diff)) {
echo "Array contains illegal values! "
. "Legal values: 4; Illegal values: " . implode(', ', $diff);
} else {
echo "All good!";
}
TBH - I think your current version is the most optimal. It will potentially only involve 1 test, find a difference and then return.
The other solutions (so far) will always process the entire array and then check if the result is empty. So they will always process every element.
You should add a return FALSE though to make the function correct.

selecting an array key based on partial string

I have an array and in that array I have an array key that looks like, show_me_160 this array key may change a little, so sometimes the page may load and the array key maybe show_me_120, I want to now is possible to just string match the array key up until the last _ so that I can check what the value is after the last underscore?
one solution i can think of:
foreach($myarray as $key=>$value){
if("show_me_" == substr($key,0,8)){
$number = substr($key,strrpos($key,'_'));
// do whatever you need to with $number...
}
}
I ran into a similar problem recently. This is what I came up with:
$value = $my_array[current(preg_grep('/^show_me_/', array_keys($my_array)))];
you would have to iterate over your array to check each key separately, since you don't have the possibility to query the array directly (I'm assuming the array also holds totally unrelated keys, but you can skip the if part if that's not the case):
foreach($array as $k => $v)
{
if (strpos($k, 'show_me_') !== false)
{
$number = substr($k, strrpos($k, '_'));
}
}
However, this sounds like a very strange way of storing data, and if I were you, I'd check if there's not an other way (more efficient) of passing data around in your application ;)
to search for certain string in array keys you can use array_filter(); see docs
// the array you'll search in
$array = ["search_1"=>"value1","search_2"=>"value2","not_search"=>"value3"];
// filter the array and assign the returned array to variable
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($key){
return(strpos($key,'search_') !== false);
},
// flag to let the array_filter(); know that you deal with array keys
ARRAY_FILTER_USE_KEY
);
// print out the returned array
print_r($foo);
if you search in the array values you can use the flag 0 or leave the flag empty
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value){
return(strpos($value,'value') !== false);
},
// flag to let the array_filter(); know that you deal with array value
0
);
or
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value){
return(strpos($value,'value') !== false);
}
);
if you search in the array values and array keys you can use the flag ARRAY_FILTER_USE_BOTH
$foo = array_filter(
// the array you wanna search in
$array,
// callback function to search for certain sting
function ($value, $key){
return(strpos($key,'search_') !== false or strpos($value,'value') !== false);
},
ARRAY_FILTER_USE_BOTH
);
in case you'll search for both you have to pass 2 arguments to the callback function
You can also use a preg_match based solution:
foreach($array as $str) {
if(preg_match('/^show_me_(\d+)$/',$str,$m)) {
echo "Array element ",$str," matched and number = ",$m[1],"\n";
}
}
filter_array($array,function ($var){return(strpos($var,'searched_word')!==FALSE);},);
return array 'searched_key' => 'value assigned to the key'
foreach($myarray as $key=>$value)
if(count(explode('show_me_',$event_key)) > 1){
//if array key contains show_me_
}
More information (example):
if array key contain 'show_me_'
$example = explode('show_me_','show_me_120');
print_r($example)
Array ( [0] => [1] => 120 )
print_r(count($example))
2
print_r($example[1])
120

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.

How to find a string in an array in PHP?

I have an array:
$array = array("apple", "banana", "cap", "dog", etc..) up to 80 values.
and a string variable:
$str = "abc";
If I want to check whether this string ($str) exists in the array or not, I use the preg_match function, which is like this:
$isExists = preg_match("/$str/", $array);
if ($isExists) {
echo "It exists";
} else {
echo "It does not exist";
}
Is it the correct way? If the array grows bigger, will it be very slow? Is there any other method? I am trying to scaling down my database traffic.
And if I have two or more strings to compare, how can I do that?
bool in_array ( mixed $needle , array $haystack [, bool $strict ] )
http://php.net/manual/en/function.in-array.php
If you just need an exact match, use in_array($str, $array) - it will be faster.
Another approach would be to use an associative array with your strings as the key, which should be logarithmically faster. Doubt you'll see a huge difference between that and the linear search approach with just 80 elements though.
If you do need a pattern match, then you'll need to loop over the array elements to use preg_match.
You edited the question to ask "what if you want to check for several strings?" - you'll need to loop over those strings, but you can stop as soon as you don't get a match...
$find=array("foo", "bar");
$found=count($find)>0; //ensure found is initialised as false when no terms
foreach($find as $term)
{
if(!in_array($term, $array))
{
$found=false;
break;
}
}
preg_match expects a string input not an array. If you use the method you described you will receive:
Warning: preg_match() expects parameter 2 to be string, array given in LOCATION on line X
You want in_array:
if ( in_array ( $str , $array ) ) {
echo 'It exists';
} else {
echo 'Does not exist';
}
Why not use the built-in function in_array? (http://www.php.net/in_array)
preg_match will only work when looking for a substring in another string. (source)
If you have more than one value you could either test every value separatly:
if (in_array($str1, $array) && in_array($str2, $array) && in_array($str3, $array) /* … */) {
// every string is element of the array
// replace AND operator (`&&`) by OR operator (`||`) to check
// if at least one of the strings is element of the array
}
Or you could do an intersection of both the strings and the array:
$strings = array($str1, $str2, $str3, /* … */);
if (count(array_intersect($strings, $array)) == count($strings)) {
// every string is element of the array
// remove "== count($strings)" to check if at least one of the strings is element
// of the array
}
The function in_array() only detects complete entries if an array element. If you wish to detect a partial string within an array, each element must be inspected.
foreach ($array AS $this_string) {
if (preg_match("/(!)/", $this_string)) {
echo "It exists";
}
}

Categories