using array_map to test values? - php

Is it possible to use array_map() to test values of an array? I want to make sure that all elements of an array are numeric.
I've tried both
$arrays = array(
array(0,1,2,3 )
, array ( 0,1, "a", 5 )
);
foreach ( $arrays as $arr ) {
if ( array_map("is_numeric", $arr) === FALSE ) {
echo "FALSE\n";
} else {
echo "TRUE\n";
}
}
and
$arrays = array(
array(0,1,2,3 )
, array ( 0,1, "a", 5 )
);
foreach ( $arrays as $arr ) {
if ( ( array_map("is_numeric", $arr) ) === FALSE ) {
echo "FALSE\n";
} else {
echo "TRUE\n";
}
}
And for both I get
TRUE
TRUE
Can this be done? If so, what am I doing wrong?
Note: I am aware that I can get my desired functionality from a foreach loop.

array_map returns an array. So it will always be considered 'true'. Now, if you array_search for FALSE, you might be able to get the desire effects.
From the PHP.net Page
array_map() returns an array containing all the elements of
arr1 after applying the callback function to each one.
This means that currently you have an array that contains true or false for each element. You would need to use array_search(false,$array) to find out if there are any false values.

I'm usually a big advocate of array_map(), array_filter(), etc., but in this case foreach() is going to be the best choice. The reason is that with array_map() and others it will go through the entire array no matter what. But for your purposes you only need to go through the array until you run into a value for which is_numeric() returns false, and as far as I know there's no way in PHP to break out of those methods.
In other words, if you have 1,000 items in your array and the 5th one isn't numeric, using array_map() will still check the remaining 995 values even though you already know the array doesn't pass your test. But if you use a foreach() instead and have it break on is_numeric() == false, then you'll only need to check those first five elements.

You could use filter, but it ends up with a horrible bit of code
$isAllNumeric = count(array_filter($arr, "is_numeric")) === count($arr)
Using a custom function makes it a bit better, but still not perfect
$isAllNumeric = count(array_filter($arr, function($x){return !is_numeric($x);})) === 0
But if you were using custom functions array_reduce would work, but it still has some failings.
$isAllNumeric = array_reduce($arr,
function($x, $y){ return $x && is_numeric($y); },
true);
The failings are that it won't break when it has found what it wants, so the functional suggestions above are not very efficient. You would need to write a function like this:
function array_find(array $array, $callback){
foreach ($array as $x){ //using iteration as PHP fails at recursion
if ( call_user_func($callback, array($x)) ){
return $x;
}
}
return false;
}
And use it like so
$isAllNumeric = array_find($arr, function($x){return !is_numeric($x);})) !== false;

i have two tiny but extremely useful functions in my "standard library"
function any($ary, $func) {
foreach($ary as $val)
if(call_user_func($func, $val)) return true;
return false;
}
function all($ary, $func) {
foreach($ary as $val)
if(!call_user_func($func, $val)) return false;
return true;
}
in your example
foreach ( $arrays as $arr )
echo all($arr, 'is_numeric') ? "ok" : "not ok";

A more elegant approach IMHO:
foreach ($arrays as $array)
{
if (array_product(array_map('is_numeric', $array)) == true)
{
echo "TRUE\n";
}
else
{
echo "FALSE\n";
}
}
This will return true if all the values are numeric and false if any of the values is not numeric.

Related

How to exit from array iteration functions (array_reduce) in PHP

I'm having an array_reduce function which I am willing to exit when specific criteria is met.
$result = array_reduce($input, function($carrier, $item) {
// do the $carrier stuff
if (/* god was one of us */) {
break; //some break analogue
}
return $carrier;
});
How do I achieve this? Or should I use foreach instead?
array_reduce is used to write functional-style code which always iterates over the full array. You can either rewrite to use a regular foreach loop to implement short circuiting logic, or you can simply return the current $carrier unmodified. This will still iterate over your full array, but it will not alter the result (as you said, this is more alike to continue)
Firstly, let me say that array_reduce is probably one of my favorite functions - I am famous (well, in a very small circle) for taking 40 lines of clearly written code and replacing them with four harder-to-follow 10 line array_reduce calls to do the same thing!
Sadly, PHP array functions seem bound to want to complete their task. This, combined with the inability to make a recursive unnamed function, makes this common situation difficult to deal with. Not wanting to put a lot of ugly for loops in my code, I tend to bury them in another function (see reduce below) as did an earlier poster.
It's worth pointing out that this is in no way as efficient as using array functions, and, in most circumstances, it's better just to let the array reduce function use a "done" flag to spin quickly through the unneeded values. At any rate, this is something reasonably array_reduce like (the evaluation function using a null return to indicate its finished). The goal is to add up the numbers in the array until you get to a 4.
<?php
$init = 0;
$arr = [1,2,3,4,5,6,7,8,9,0];
$func = function($c, $it) {
if ($it == 4) return null;
return $c + $it;
};
function reduce($arr, $f, $init) {
for ($c = $init; count($arr); ) {
$newc = $f($c, array_shift($arr));
if (!isset($newc)) break;
$c = $newc;
}
return $c;
}
echo reduce($arr, $func, $init) . "\n"; // 6
According to a similar answer.
Break array_walk from anonymous function
The best and the worst way to complete this is Exception.
Not recommend this way, but this way is the solution to your question:
try {
$result = array_reduce( $input, function ( $carrier, $item ) {
// do the $carrier stuff
$condition = true;
if ( $condition ) {
throw new Exception;
}
return $carrier;
} );
} catch ( Exception $exception ) {
echo 'Break';
}
The way I would solve the problem
I would create a global function or write PHP extension and add a function
There is a good answer about writing PHP extension:
How to make a PHP extension
array_reduce_2();
But there is a problem with breaking implementation.
Need to detect which condition to out of function.
Below implementation, array_reduce_2 checks if a callback returned.
If so - breaking out of execution.
This way allows checking if execution has broken by checking return type of value.
array_reduce_2 implementation
According to #wordragon notice, implemented the ability to pass an associative array as param too.
function array_reduce_2( $array, $callback, $initial = null ) {
$len = count( $array );
$index = 0;
if ( $len == 0 && count( func_get_args() ) == 1 ) {
return null;
}
$values = array_values( $array );
$result = $initial ?? $values[ $index ++ ];
foreach ( $values as $value ) {
$result = $callback( $result, $value );
if ( ! is_callable( $result ) ) {
continue;
}
// break;
return $result;
}
return $result;
}
I've used the idea from JS implementation and rewrite for PHP accordingly
https://gist.github.com/keeto/229931
Detecting if the break occured
$input = [ 'test', 'array', 'god was one of us' ];
$result = array_reduce_2( $input, function ( $carrier, $item ) {
// do the $carrier stuff
if ( $item === 'god was one of us' ) {
return function () {
return 'god was one of us';
};
}
return $carrier;
} );
$is_break = is_callable( $result );
if ( $is_break ) {
echo $result();
exit;
}
Important to note!
This array_reduce_2 implementation works properly only if you don't need to return the normal value as a callback.
I suggest using foreach loops instead. The reasons to not use array_reduce are:
Sound reasons:
It is not statically type-checked. So code inspections do not show type errors if there are any in the input or callback arguments.
It returns mixed, so inspections do not show errors if you misuse the result, or they may show false positive if you use it properly.
You cannot break.
Opinionated reasons:
It is harder on the eye. Having a $result and adding to it in a loop (or whatever you do) is way easier to read than grasping that something is returned and then passed as a $carry accumulator in the next call.
It makes me lazy to extract functions properly. If I extract one operation to a callback, I then may find the code short enough to not extract the whole array operation to a function which should really be done in the first place.
If you use a condition to break, there is a good chance you may one day need other arguments to that callback function. With the callback signature being fixed, you would have to pass arguments with use keyword which really much harder to read than a non-callback.
breakable_array_reduce()
function breakable_array_reduce(array $array, callable $callback, $initial = null) {
$result = $initial;
foreach ($array as $value) {
$ret = $callback($result, $value);
if (false === $ret) {
return $result;
} else {
$result = $ret;
}
}
return $result;
}
Usage
// array of 10 values
$arr = [
1,
1,
1,
1,
1,
1,
1,
1,
1,
1
];
// callback function which stops once value >= 5
$sum_until_five = function($initial, $value) {
if ($initial >= 5) {
return false;
} else {
return $initial + $value;
}
};
// calculate sum of $arr using $sum_until_five()
$sum = breakable_array_reduce($arr, $sum_until_five);
// output: 5
echo $sum;
Explanation
breakable_array_reduce() will work just like array_reduce() unless/until callback $callback returns bool(false)
Alternate implementation using array keys:
breakable_array_reduce_keyed()
function breakable_array_reduce_keyed(array $array, callable $callback, $initial = null) {
$result = $initial;
foreach ($array as $key => $value) {
$ret = $callback($result, $value, $key);
if (false === $ret) {
return $result;
} else {
$result = $ret;
}
}
return $result;
}
Usage
// array of values
$arr = [
'foo' => 1,
'bar' => 1,
'baz' => 1
];
// callback function which stops when $key === 'baz'
$sum_until_baz = function($initial, $value, $key) {
if ('baz' === $key) {
return false;
} else {
return $initial + $value;
}
};
// calculate sum of $arr using $sum_until_baz()
$sum = breakable_array_reduce($arr, $sum_until_baz);
// output: 2
echo $sum;
P.S. I just wrote and fully tested this locally.

Search for part of string in multidimentional array returned by Drupal

I'm trying to find a part of a string in a multidimentional array.
foreach ($invitees as $invitee) {
if (in_array($invitee, $result)){
echo 'YES';
} else {
echo 'NO';
}
}
the $invitees array has 2 elements:
and $result is what I get from my Drupal database using db_select()
What I'm trying to do is, if the first part from one of the emails in $invitees is in $result it should echo "YES". (the part before the "#" charather)
For example:
"test.email" is in $result, so => YES
"user.one" is not in $result, so => NO
How do i do this? How can I search for a part of a string in a multidimentional array?
Sidenote: I noticed that the array I get from Drupal ($result) has 2 "Objects" which contain a "String", and not arrays like I would expect.
For example:
$test = array('red', 'green', array('apple', 'banana'));
Difference between $result and $test:
Does this have any effect on how I should search for my string?
Because $result is an array of objects, you'll need to use a method to access the value and compare it. So, for instance you could do:
//1: create new array $results from array of objects in $result
foreach ($result as $r) {
$results[] = get_object_vars($r);
}
//2: expanded, recursive in_array function for use with multidimensional arrays
function in_array_r($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
//3: check each element of the $invitees array
foreach ($invitees as $invitee) {
echo in_array_r($invitee, $results) ? "Yes" : "No";
}
Also, for some illumination, check out this answer.
You can search through the array using preg_grep, and use a wildcard for anything before and after it. If it returns a value (or values), use key to get the index of the first one. Then do a check if its greater than or equal to 0, which means it found a match :)
<?php
$array = array('test1#gdfgfdg.com', 'test2#dgdgfdg.com', 'test3#dfgfdgdfg');
$invitee = 'test2';
$result = key(preg_grep('/^.*'.$invitee.'.*/', $array));
if ($result >= 0) {
echo 'YES';
} else {
echo 'NO';
}
?>
Accepted larsAnders's answer since he pointed me in to direction of recursive functions.
This is what I ended up using (bases on his answer):
function Array_search($array, $string) {
foreach ($array as $key => $value) {
if (is_array($value)) {
Array_search($array[$key], $string);
} else {
if ($value->data == $string) {
return TRUE;
}
}
}
return FALSE;
}

PHP if in_array for multiple values

I have an array that is generated with anywhere between 3 and 12 values, it generates the array from account information;
$result = $ad->user()->groups($user['username']);
I want to check this array for multiple values (around 4 or 5) and if any of them are in it do what's inside the if, I can do it for one value pretty easily via:
if (in_array("abc",$result)) { $this->login_session($user); }
Is there a simple way to check this one array for multiple values in it other than consecutive ORs:
if (in_array("abc",$result) || in_array("123",$result) || in_array("def",$result) || in_array("456",$result) ) {
$this->login_session($user);
}
Try and see if this is helpful:
if(array_intersect($result, array('abc', '123', 'def'))) {
$this->login_session($user);
}
This should be what you are after:
$a = array(1,2,3,4,5);
$b = array(6,8);
function is_in_array($needle, $haystack) {
foreach ($needle as $stack) {
if (in_array($stack, $haystack)) {
return true;
}
}
return false;
}
var_dump(is_in_array($b, $a));
Basically loops through needle and runs an in array of it on the haystack. returns true once something is found, else it returns false.

PHP Search Multidimensional Array - Not Associative

I am trying to write a piece of code that searches one column of 2-D array values and returns the key when it finds it. Right now I have two functions, one to find a value and return a boolean true or false and another (not working) to return the key. I would like to merge the two in the sense of preserving the recursive nature of the finding function but returning a key. I cannot think how to do both in one function, but working key finder would be much appreciated.
Thanks
function in_array_r($needle, $haystack, $strict = true) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
function loopAndFind($array, $index, $search){
$returnArray = array();
foreach($array as $k=>$v){
if($v[$index] == $search){
$returnArray[] = $k;
}
}
return $returnArray;
}`
Sorry, I meant to add an example. For instance:
Array [0]{
[0]=hello
[1]=6
}
[1]
{
[0]=world
[1]=4
}
and I want to search the array by the [x][0] index to check each string of words for the search term. If found, it should give back the index/key in the main array like "world" returns 1
This works:
$array = array(array('hello', 6), array('world', 4));
$searchTerm = 'world';
foreach ($array as $childKey => $childArray) {
if ($childArray['0'] == $searchTerm) {
echo $childKey; //Your Result
}
}
You already have all you need in your first function. PHP does the rest:
$findings = array_map('in_array_r', $haystack);
$findings = array_filter($findings); # remove all not found
var_dump(array_keys($findings)); # the keys you look for

Is there a method to check if all array items are '0'?

I have an array
$data = array( 'a'=>'0', 'b'=>'0', 'c'=>'0', 'd'=>'0' );
I want to check if all array values are zero.
if( all array values are '0' ) {
echo "Got it";
} else {
echo "No";
}
Thanks
I suppose you could use array_filter() to get an array of all items that are non-zero ; and use empty() on that resulting array, to determine if it's empty or not.
For example, with your example array :
$data = array(
'a'=>'0',
'b'=>'0',
'c'=>'0',
'd'=>'0' );
Using the following portion of code :
$tmp = array_filter($data);
var_dump($tmp);
Would show you an empty array, containing no non-zero element :
array(0) {
}
And using something like this :
if (empty($tmp)) {
echo "All zeros!";
}
Would get you the following output :
All zeros!
On the other hand, with the following array :
$data = array(
'a'=>'0',
'b'=>'1',
'c'=>'0',
'd'=>'0' );
The $tmp array would contain :
array(1) {
["b"]=>
string(1) "1"
}
And, as such, would not be empty.
Note that not passing a callback as second parameter to array_filter() will work because (quoting) :
If no callback is supplied, all entries of input equal to FALSE (see
converting to boolean) will be removed.
How about:
// ditch the last argument to array_keys if you don't need strict equality
$allZeroes = count( $data ) == count( array_keys( $data, '0', true ) );
Use this:
$all_zero = true;
foreach($data as $value)
if($value != '0')
{
$all_zero = false;
break;
}
if($all_zero)
echo "Got it";
else
echo "No";
This is much faster (run time) than using array_filter as suggested in other answer.
you can loop the array and exit on the first non-zero value (loops until non-zero, so pretty fast, when a non-zero value is at the beginning of the array):
function allZeroes($arr) {
foreach($arr as $v) { if($v != 0) return false; }
return true;
}
or, use array_sum (loops complete array once):
function allZeroes($arr) {
return array_sum($arr) == 0;
}
#fireeyedboy had a very good point about summing: if negative values are involved, the result may very well be zero, even though the array consists of non-zero values
Another way:
if(array_fill(0,count($data),'0') === array_values($data)){
echo "All zeros";
}
Another quick solution might be:
if (intval(emplode('',$array))) {
// at least one non zero array item found
} else {
// all zeros
}
if (!array_filter($data)) {
// empty (all values are 0, NULL or FALSE)
}
else {
// not empty
}
I'm a bit late to the party, but how about this:
$testdata = array_flip($data);
if(count($testdata) == 1 and !empty($testdata[0])){
// must be all zeros
}
A similar trick uses array_unique().
You can use this function
function all_zeros($array){//true if all elements are zeros
$flag = true;
foreach($array as $a){
if($a != 0)
$flag = false;
}
return $flag;
}
You can use this one-liner: (Demo)
var_export(!(int)implode($array));
$array = [0, 0, 0, 0]; returns true
$array = [0, 0, 1, 0]; returns false
This is likely to perform very well because there is only one function call.
My solution uses no glue when imploding, then explicitly casts the generated string as an integer, then uses negation to evaluate 0 as true and non-zero as false. (Ordinarily, 0 evaluates as false and all other values evaluate to true.)
...but if I was doing this for work, I'd probably just use !array_filter($array)

Categories