34

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.

John Conde
  • 217,595
  • 99
  • 455
  • 496
  • Does it also work for non integers? `array('1','2','3');` I'm not sure if form elements are sent as string. – Chazy Chaz Mar 23 '16 at 15:42
  • 1
    Just replace is_int with is_string – John Conde Mar 23 '16 at 15:44
  • Literally none of the answers given handle early termination. All those answers will walk the entire array even if the answer is already known after examining the first element. The built-in PHP functions for array traversal simply don't support early termination, full stop (pun intended). If you need early termination, you need to loop. – Szczepan Hołyszewski Sep 06 '21 at 18:09

6 Answers6

69
$only_integers       === array_filter($only_integers,       'is_int'); // true
$letters_and_numbers === array_filter($letters_and_numbers, 'is_int'); // false

It helps to define two helper, higher-order functions:

/**
 * Tell whether all members of $elems validate the $predicate.
 *
 * all(array(), 'is_int')           -> true
 * all(array(1, 2, 3),   'is_int'); -> true
 * all(array(1, 2, 'a'), 'is_int'); -> false
 */
function all($elems, $predicate) {
  foreach ($elems as $elem) {
    if (!call_user_func($predicate, $elem)) {
      return false;
    }
  }

  return true;
}

/**
 * Tell whether any member of $elems validates the $predicate.
 *
 * any(array(), 'is_int')               -> false
 * any(array('a', 'b', 'c'), 'is_int'); -> false
 * any(array(1, 'a', 'b'),   'is_int'); -> true
 */
function any($elems, $predicate) {
  foreach ($elems as $elem) {
    if (call_user_func($predicate, $elem)) {
      return true;
    }
  }

  return false;
}
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • Just want to make sure I'm not crazy here. Shouldn't $predicate, $array be reversed in array_filter()? – John Conde Aug 24 '10 at 18:51
  • @John you were right. I've used `array_map` quite a lot these days, hence the mistake. – Ionuț G. Stan Aug 24 '10 at 18:55
  • Should be array_filter( array $input [, callback $callback ] ) – AVProgrammer Oct 14 '11 at 21:32
  • can you give an example on how to use the condition without the function because I can't get it to work :( thanks :) – Michael Samuel Feb 15 '13 at 15:55
  • I think you are wrong with your answer. Your check does return an array, if there are _any_ integers in the test array, only the letters are filtered out. So you cannot check if there are only integers in the array. The array_filter only returns false, if there are no integers at all. – 4thfloorstudios Jul 06 '15 at 09:08
  • @4thfloorstudios sorry, but I don't understand what you're saying. Counter-example code would probably help. – Ionuț G. Stan Jul 06 '15 at 12:55
  • Sure. Sorry if I could not make myself clear. Execute `$testArray = array(1,2,'3a'); var_dump(array_filter($testArray, 'is_int'));` and you will get an array with two integers 1 and 2 but not false as returned value. The poster wanted to have a solution which returns false if there is _any_ character in the array. array_filter returns false only if there are no integers in the tested array at all. – 4thfloorstudios Jul 06 '15 at 16:27
  • @4thfloorstudios I see know. That's why there's an equality comparison in my answer. To test that the original array and the filtered array are the same, which can only mean that all elements in the original satisfy the `is_int` predicate. So, my answer is `array_filter` + `===`. Of course, it's not a very efficient method of doing that, but... that was my answer five years ago :) – Ionuț G. Stan Jul 06 '15 at 20:26
  • Ah, no I see. Just was irrittated by the `$letters_and_numbers === array_filter($letters_and_numbers, 'is_int'); // false` which is not correct. And the sentence `It would help you in the future to define two helper, higher-order functions:` sounded like "The above would work but you can use a helper function to make it more abstract for general use." – 4thfloorstudios Jul 07 '15 at 08:02
  • Terrible. No wonder most of php code is such a mess - when even top rated SO answers recommend practices like this - clearly unnecessary applying predicate bunch of times, and using full array comparison (!) to test if none of the elements where rejected. – Cthulhu Apr 04 '16 at 10:38
  • @Cthulhu I agree, I'd implement `all` and `any` differently today. However, even back then, the question was just about a more concise way, so I presented something concise ;) – Ionuț G. Stan Apr 04 '16 at 10:54
  • 1
    "Any" means "some of" (more than zero) and not "not_all" implemented in the answear – Alex Dvoretsky Sep 27 '16 at 06:27
7
 <?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
  • 17,469
  • 22
  • 83
  • 129
  • 3
    +1 for a simple solution, but this won't work with large arrays. – alexn Aug 24 '10 at 18:20
  • If the integer produced by implode() is larger then the server's max int value it will fail. – John Conde Aug 24 '10 at 19:24
  • you wouldn't happen to know what the default is would you ? – mcgrailm Aug 24 '10 at 19:27
  • 1
    @mcgrailm: I know this post is 4 years old, but it might help future visitors. It's `PHP_INT_MAX`. – Amal Murali Jan 27 '14 at 17:49
  • @AmalMurali do you mean rather than is_numeric ? – mcgrailm Jan 28 '14 at 13:37
  • I was responding to this comment: `you wouldn't happen to know what the default is would you ?`. 2,147,483,647 is the largest number than can be stored in 32 bits. For 64 bit, it is `(2^63) − 1` (which is approx. 9,223,372,036,854,775,807). I was just saying you could use `PHP_INT_MAX` to get this value :-) Also, +1 – Amal Murali Jan 28 '14 at 13:46
6

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
  • 356,200
  • 43
  • 426
  • 500
5

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.

Matthew
  • 47,584
  • 11
  • 86
  • 98
  • Question is, how do you know which is the fastest without benchmarking? – Ionuț G. Stan Aug 24 '10 at 18:21
  • 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. – bcosca Aug 24 '10 at 18:25
  • this solution returns true even when array passed is empty – bcosca Aug 24 '10 at 18:27
  • 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 Aug 24 '10 at 18:31
  • @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. – Matthew Aug 24 '10 at 18:37
  • 1
    @konforce: `var_dump(array_reduce(array(), function($a, $b) { return $a && is_int($b); }, true);)` result: `bool(true)` that's due to the 3rd arg you provided. – bcosca Aug 24 '10 at 18:44
  • 2
    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. – Matthew Aug 24 '10 at 18:49
  • @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. – Matthew Aug 24 '10 at 18:51
  • 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 Aug 24 '10 at 18:53
4
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.

bcosca
  • 17,371
  • 5
  • 40
  • 51
0

Why don't we give a go to Exceptions?

Take any built in array function that accepts a user callback (array_filter(), array_walk(), even sorting functions like usort() etc.) and throw an Exception in the callback. E.g. for multidimensional arrays:

function arrayHasOnlyInts($array)
{
    if ( ! count($array)) {
        return false;
    }

    try {
        array_walk_recursive($array, function ($value) {
            if ( ! is_int($value)) {
                throw new InvalidArgumentException('Not int');
            }

            return true;
        });
    } catch (InvalidArgumentException $e) {
        return false;
    }

    return true;
}

It's certainly not the most concise, but a versatile way.

postrel
  • 134
  • 8