tags:

views:

70

answers:

5

How do you verify an array contains only values that are integers?

I'd like to be able to check an array and end up with a boolean value of true if the array contains only integers and false if there are any other characters in the array. I know I can loop through the array and check each element individually and return true or false depending on the presence of non-numeric data:

For example:

$only_integers = array(1,2,3,4,5,6,7,8,9,10);
$letters_and_numbers = array('a',1,'b',2,'c',3);

function arrayHasOnlyInts($array)
{
    foreach ($array as $value)
    {
        if (!is_int($value)) // there are several ways to do this
        {
             return false;
        }
    }
    return true;
}

$has_only_ints = arrayHasOnlyInts($only_integers ); // true
$has_only_ints = arrayHasOnlyInts($letters_and_numbers ); // false

But is there a more concise way to do this using native PHP functionality that I haven't thought of?

Note: For my current task I will only need to verify one dimensional arrays. But if there is a solution that works recursively I'd be appreciative to see that to.

+4  A: 
$only_integers === array_filter('is_int', $only_integers);             // true
$letters_and_numbers === array_filter('is_int', $letters_and_numbers); // false

It would help you in the future to define two helper, higher-order, functions:

/**
 * Tell whether all members of $array validate the $predicate.
 *
 * all(array(1, 2, 3),   'is_int'); -> true
 * all(array(1, 2, 'a'), 'is_int'); -> false
 */
function all($array, $predicate) {
    return array_filter($array, $predicate) === $array;
}

/**
 * Tell whether any member of $array validates the $predicate.
 *
 * any(array(1, 'a', 'b'),   'is_int'); -> true
 * any(array('a', 'b', 'c'), 'is_int'); -> false
 */
function any($array, $predicate) {
    return array_filter($array, $predicate) !== array();
}
Ionuț G. Stan
Just want to make sure I'm not crazy here. Shouldn't $predicate, $array be reversed in array_filter()?
John Conde
@John you were right. I've used `array_map` quite a lot these days, hence the mistake.
Ionuț G. Stan
@Ionuț G. Stan, That's what happens when PHP is not consistent in it's parameter order....
John Conde
+1  A: 
function arrayHasOnlyInts($array) {
    return array_reduce(
        $array,
        function($result,$element) {
            return is_null($result) || $result && is_int($element);
        }
    );
}

returns true if array has only integers, false if at least one element is not an integer, and null if array is empty.

stillstanding
+1  A: 

There's always array_reduce():

array_reduce($array, function($a, $b) { return $a && is_int($b); }, true);

But I would favor the fastest solution (which is what you supplied) over the most concise.

konforce
+1: For favoring the fastest solution.
shamittomar
Question is, how do you know which is the fastest without benchmarking?
Ionuț G. Stan
the OP's code short-circuits the iteration by returning false if any of the elements are not integer. the rest of the solutions proposed iterates over the entire array.
stillstanding
this solution returns true even when array passed is empty
stillstanding
my solution doesn't iterates and returns false if its empty 8 ^ ) but i'm unclear on why it won't work with large arrays
mcgrailm
@Ionuț, Theoretically, the OP's code gives the best (on average) n/2 iterations with a single comparison. Of course, the actual fastest solution depends on PHP's implementation and the data being used. So you are correct that a benchmark is needed. The less time spent in PHP user code, the better... which may favor a solution like mine.
konforce
@konforce: `var_dump(array_reduce(array(), function($a, $b) { return $a }, true);)` result: `bool(true)` that's due to the 3rd arg you provided.
stillstanding
A quick benchmark with array with 1M integers records. foreach: 1.09s, reduce: 0.88s, for: 0.64s. After updating the 5th record to be non-integer... foreach: 0.59s, reduce: 0.54s, for: 0.0s. So to get optimal performance, replace the foreach with a for loop (and use a constant `$c=count($array)`). Your mileage may vary.
konforce
@stillstanding: That's a good point worth mentioning. But note that it could be considered correct behavior depending on how the function is to be used.
konforce
I believe the best approach would be to create a higher order function, like `all` and `any`, which would internally use a `for` loop. that would provide both speed and conciseness. And greater possibility for reuse.
Ionuț G. Stan
+3  A: 
 <?php
 $only_integers = array(1,2,3,4,5,6,7,8,9,10);
 $letters_and_numbers = array('a',1,'b',2,'c',3);

 function arrayHasOnlyInts($array){
    $test = implode('',$array);
    return is_numeric($test);
 }

 echo "numbers:". $has_only_ints = arrayHasOnlyInts($only_integers )."<br />"; // true
 echo "letters:". $has_only_ints = arrayHasOnlyInts($letters_and_numbers )."<br />"; // false
 echo 'goodbye';
 ?>
mcgrailm
+1 for a simple solution, but this won't work with large arrays.
alexn
what happens with large arrays that it won't work ?
mcgrailm
If the integer produced by implode() is larger then the server's max int value it will fail.
John Conde
you wouldn't happen to know what the default is would you ?
mcgrailm
+1  A: 

Another alternative, though probably slower than other solutions posted here:

function arrayHasOnlyInts($arr) {
   $nonints = preg_grep('/\D/', $arr); // returns array of elements with non-ints
   return(count($nonints) == 0); // if array has 0 elements, there's no non-ints
}
Marc B