PHP treats all arrays as associative, so there aren't any built in functions. Can anyone recommend a fairly efficient way to check if an array contains only numeric keys?
Basically, I want to be able to differentiate between this:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
and this:
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
My solution:
array_merge
on a single array will reindex allinteger
keys, but not other. For example:So if a list (a non-associative array) is created
['a', 'b', 'c']
then a value is removedunset($a[1])
thenarray_merge
is called, the list is reindexed starting from 0.Both of these examples, which scored the most points do not work correctly with arrays like
$array = array('foo' => 'bar', 1)
Unless PHP has a builtin for that, you won't be able to do it in less than O(n) - enumerating over all the keys and checking for integer type. In fact, you also want to make sure there are no holes, so your algorithm might look like:
But why bother? Just assume the array is of the type you expect. If it isn't, it will just blow up in your face - that's dynamic programming for you! Test your code and all will be well...
I think the following two functions are the best way to go for checking 'if an array is associative or numeric'. Since 'numeric' could mean only numeric keys or only sequential numeric keys, two functions are listed below that check either condition:
The first function checks if each key is an integer value. The second function checks if each key is an integer value and in addition checks if all keys are sequential starting at $base, which defaults to 0 and thus can be omitted if you do not need to specify another base value. key($my_array) returns null if the read pointer is moved past the end of the array, which is what ends the for loop and makes the statement after the for loop return true if all keys were integer. If not, the loop ends prematurely because a key is of type string, and the statement after the for loop will return false. The latter function in addition adds one to $base after each compare, to be able to check if the next key is of the correct value. The strict compare makes it also check if the key is of type integer. The $base = (int) $base part in the first section of the for loop can be left out when $base is omitted or if you make sure it is only called using an integer. But since I can't be sure for everybody, I left it in. The statement is executed only once, anyway. I think these are the most efficient solutions:
Remember that an array key can only be an integer or a string, and a strictly numeric string such as "1" (but not "01") will be translated into an integer. Which is what makes checking for an integer key the only needed operation besides counting if you want the array to be sequential. Naturally, if is_indexed_array returns false the array can be seen as associative. I say 'seen', because in fact they all are.
Fast, concise, and memory efficient. No expensive comparisons, function calls or array copying.
Many commenters in this question don't understand how arrays work in PHP. From the array documentation:
In other words, there is no such thing as an array key of "8" because it will always be (silently) converted to the integer 8. So trying to differentiate between integers and numeric strings is unnecessary.
If you want the most efficient way to check an array for non-integer keys without making a copy of part of the array (like array_keys() does) or all of it (like foreach does):
This works because key() returns NULL when the current array position is invalid and NULL can never be a valid key (if you try to use NULL as an array key it gets silently converted to "").