Reputation: 27723
Are there faster methods to check the existence of a number (not null
) in one
column of a multidimensional array in php (for instance, number9
)?
The if statement
below seems to be working okay.
$arr=Array(
[0] => Array
(
[date] => 2019-01-16
[number1] => 20.4
[number2] => 20.54
[number3] => 19.71
[number4] => 19.73
[number5] => 70849266
[number6] => 70849266
[number7] => -0.65
[number8] => -3.189
[number9] => 20.0902
[string1] => Jan 16
[number10] => 0.047796070100903
)
... more rows ...
[21] => Array
(
[date] => 2019-01-17
[number1 => 19.49
[number2] => 20.51
[number3] => 19.02
[number4] => 20.25
[number5] => 85018421
[number6] => 85018421
[number7] => 0.52
[number8] => 2.636
[number9] => 19.7988
[string1] => Jan 17
[number10] => 0.075411577270313
)
);
function isNumeric($var){
if (is_numeric($var)){
return true;
} else {
return false;
}
}
if ((array_walk(array_column($arr, "number8"), 'isNumeric')) != 1)
Upvotes: 2
Views: 468
Reputation: 47774
From PHP8.4 (or use a polyfill until you upgrade your version), array_all()
is the perfect choice for a short-circuiting functional-style approach that will never make more than a single loop over the array and will return a boolean value. Demo
var_export(
array_all($arr, fn($row) => is_numeric($row['number8']))
);
Upvotes: 0
Reputation: 27723
Thanks a million everyone, for your great answers!
On my PC, I tried your four functions in PHP 5.5.38 with ~5000 iterations and total times are:
"is_numeric_array_with_cast total time is 0.44153618812561"
"with_array_filter total time is 0.21628260612488"
"is_numeric_array_with_func total time is 0.14269280433655"
"is_numeric_matt_fryer total time is 0.155033826828"
$t1=$t2=$t3=$t4=0;
foreach($arrs as $k=>$arr){
$s1=microtime(true);
is_numeric_array_with_cast(array_column($arr, "number8"));
$e1=microtime(true)-$s1;
$t1=$t1+$e1;
$s2=microtime(true);
with_array_filter(array_column($arr, "number8"));
$e2=microtime(true)-$s2;
$t2=$t2+$e2;
$s3=microtime(true);
is_numeric_array_with_func(array_column($arr, "number8"));
$e3=microtime(true)-$s3;
$t3=$t3+$e3;
$s4=microtime(true);
is_numeric_matt_fryer(array_column($arr, "number8"),"number8");
$e4=microtime(true)-$s4;
$t4=$t4+$e4;
}
function is_numeric_array_with_cast($arr) {
foreach ($arr as $b) {
if ($b != (string)(float)$b) {
return false;
}
}
return true;
}
function with_array_filter($arr) {
return $arr == array_filter($arr, 'is_numeric');
}
function is_numeric_array_with_func($arr) {
foreach ($arr as $b) {
if (!is_numeric($b)) {
return false;
}
}
return true;
}
function is_numeric_matt_fryer($arr,$str){
$bool = true;
foreach ($arr as $row)
{
if (!is_numeric($row[$str]))
{
$bool = false;
}
}
return $bool;
}
Upvotes: 0
Reputation:
Use a foreach
loop:
$bool = true;
foreach ($arr as $row)
{
if (!is_numeric($row['number8']))
{
$bool = false;
break;
}
}
Upvotes: 0
Reputation: 33242
Here are my ideas.
First is to just filter the array for numeric only values and compare to the original:
function with_array_filter($arr) {
return $arr == array_filter($arr, 'is_numeric');
}
The second example uses casting to a float and then back to string, keep in mind that this is not going to be accurate for very big numbers, however seems to be the fastest (if you care about that at all):
function is_numeric_array_with_cast($arr) {
foreach ($arr as $b) {
if ($b != (string)(float)$b) {
return false;
}
}
return true;
}
However probably the simplest solution is to just foreach
the array inside a function and return early:
function is_numeric_array_with_func($arr) {
foreach ($arr as $b) {
if (!is_numeric($b)) {
return false;
}
}
return true;
}
Benchmarked with an array of 20 elements over 100000 iterations on PHP 7.2:
$falseArray = [
'1',
2.5,
-10,
32,
11.0,
2.5,
100101221,
345,
-10,
'-10',
'+10',
'10',
12,
PHP_INT_MAX,
PHP_INT_MAX + 1.4e5,
'-10',
null,
'a',
'5',
5
];
Matt Fryer
Time: 4.8473789691925
is_numeric_array_with_func
Time: 4.0416791439056
is_numeric_array_with_cast
Time: 3.2953300476074
with_array_filter
Time: 3.99729180336
Upvotes: 1
Reputation: 21661
AS I said in the comments:
The if statement below seems to be working okay
However, given the code you posed I doubt that: lets look at it.
function isNumeric($var){ ... }
if(array_walk(array_column($arr, "number8"), 'isNumberic'))!=1
The first and most obvious things are these 2
isNumberic
vs isNumeric
, which is a fatal undefined function error (spelling/typo).)!=1
then this is outside of the actual condition, or put another way if(...){ !=1 }
Let's assume those are just typos in the question. Even if your code was free of the 2 "defects" I mentioned above you would still have this problem, array_walk
works by reference and simply returns true (always). Pass by reference updates the "Original" variable without returning a copy of it (in the case of array_walk)
http://php.net/manual/en/function.array-walk.php
Return Values Returns TRUE.
Which of course just makes your condition pass no matter what. So you should always test both the passing and the failing of the condition (As I did by placing some "canned" bad data in there). This way I know for 100% sure exactly how my code behaves.
Others have posted how to correct this, but not what you did wrong. But just for the sake of completeness I will post an answer anyway.
$arr = array (
0 =>
array (
'date' => '2019-01-16',
'number1' => 20.4,
'number2' => 20.54,
'number3' => 19.71,
'number4' => 19.73,
'number5' => 70849266,
'number6' => 70849266,
'number7' => -0.65,
'number8' => -3.189,
'number9' => 20.0902,
'string1' => 'Jan16',
'number10' => 0.047796070100903
),
array (
'date' => '2019-01-16',
'number1' => 20.4,
'number2' => 20.54,
'number3' => 19.71,
'number4' => 19.73,
'number5' => 70849266,
'number6' => 70849266,
'number7' => -0.65,
'number8' => 'foo',#intentially not number
'number9' => 20.0902,
'string1' => 'Jan16',
'number10' => 0.047796070100903
),
);
$col = array_column($arr, "number8");
$col1 = array_filter($col, 'is_numeric');
if($col != $col1){
echo "not the same\n";
}
Output:
not the same
I should mention, there is no "need" to count these, as PHP can compare complex objects for equality. As we are comparing the same "root" array ($col
in this example) with itself after (possibly) removing some elements, if no elements were removed then both arrays should be not only the same length but also "Identical".
Also if you want to do it in one line (inside the condition) you can do this:
if( ($col = array_column($arr, "number8")) && $col != array_filter($col, 'is_numeric')){
echo "not the same\n";
}
It's a bit harder to read, and pay attention to $col = array_column
assignment.
Upvotes: 1