Reputation: 33356
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 "is a list" (contains only numeric keys starting from 0)?
Basically, I want to be able to differentiate between this:
$sequentialArray = [
'apple', 'orange', 'tomato', 'carrot'
];
and this:
$assocArray = [
'fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot'
];
Upvotes: 934
Views: 351980
Reputation: 109
One way to approach this is to piggyback on json_encode
, which already has its own internal method of differentiating between an associative array and an indexed array in order to output the correct JSON.
You can do this by checking to see if the first character returned after encoding is a {
(associative array) or a [
(indexed array).
// Too short :)
function is_assoc(&$arr) {
return json_encode($arr)[0] === '{';
}
Upvotes: 10
Reputation: 8098
PHP 8.1 adds a built-in function to determine whether an array is a list with those semantics, or not. the function is array_is_list
:
$list = ["a", "b", "c"];
array_is_list($list); // true
$notAList = [1 => "a", 2 => "b", 3 => "c"];
array_is_list($notAList); // false
$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];
array_is_list($alsoNotAList); // false
Please note that this function returns true
on empty arrays.
array_is_list([]); // true
Upvotes: 61
Reputation: 321638
Since 8.1 PHP has a simple answer, array_is_list().
For the legacy code you can use the following function (wrapping it in function_exists()
to make it portable):
if (!function_exists('array_is_list')) {
function array_is_list(array $arr)
{
if ($arr === []) {
return true;
}
return array_keys($arr) === range(0, count($arr) - 1);
}
}
And then you can use the this function with any PHP version.
var_dump(array_is_list([])); // true
var_dump(array_is_list(['a', 'b', 'c'])); // true
var_dump(array_is_list(["0" => 'a', "1" => 'b', "2" => 'c'])); // true
var_dump(array_is_list(["1" => 'a', "0" => 'b', "2" => 'c'])); // false
var_dump(array_is_list(["a" => 'a', "b" => 'b', "c" => 'c'])); // false
Upvotes: 811
Reputation: 405
Check if the data type is array, then json encode then decode the data and check if the new data type is object.
public function is_assoc_array($array)
{
return is_array($array)
&& is_object(json_decode(json_encode($array)));
}
Some examples
echo is_assoc_array(['one', 'two', 'three']) ? 'Yes' : 'No'); \\No
echo is_assoc_array(['one' => 'one', 'two' => 'two', 'three' => 'three']) ? 'Yes' : 'No'; \\Yes
echo is_assoc_array(['1' => 'one', '2' => 'two', '3' => 'three']) ? 'Yes' : 'No'; \\Yes
echo is_assoc_array(['0' => 'one', '1' => 'two', '2' => 'three']) ? 'Yes' : 'No'; \\No
There was a similar solution by @devios1 in one of the answers but this was just another way using the inbuilt json related functions of PHP. I haven't checked how this solution fairs in terms of performance compared to other solutions that have been posted here.
Upvotes: -2
Reputation: 1214
Just adding my two cents, and I think it should be pretty efficient based on @nonsensei 's benchmarks as well as being clearly legible:
function is_associative(array $array): bool
{
foreach ($array as $key => $value)
{
if (!is_string($key)) return false;
}
return true;
}
Upvotes: 1
Reputation: 3049
For those using Laravel:
Arr::isAssoc
returns true if the given array is an associative array. An array is considered "associative" if it doesn't have sequential numerical keys beginning with zero:
use Illuminate\Support\Arr;
$isAssoc = Arr::isAssoc(['product' => ['name' => 'Desk', 'price' => 100]]);
// true
$isAssoc = Arr::isAssoc([1, 2, 3]);
// false
https://laravel.com/docs/8.x/helpers#method-array-isassoc
Upvotes: 4
Actually the most efficient way is thus:
function is_assoc($array){
$keys = array_keys($array);
return $keys !== array_keys($keys);
}
This works because it compares the keys (which for a sequential array are always 0,1,2 etc) to the keys of the keys (which will always be 0,1,2 etc).
Laravel use this approach.
Upvotes: 31
Reputation: 6411
Most answers have sub-optimal time/space complexity or are changing semantics. So, here is another answer with the fastest and most functionally correct solution:
function is_sequential_array(Array &$a) {
$n = count($a);
for($i=0; $i<$n; $i++) {
if(!array_key_exists($i, $a)) {
return false;
}
}
return true;
}
This answer has following advantages over other answers:
O(1)
(many answers here use O(n)
space!)array_key_exists
instead of isset
(remember, isset
additionally checks for 'is not null', thereby changing semantics)O(n)
(many answers here have best-case time complexity of O(n)
)Upvotes: 6
Reputation: 3455
/**
* Determines if an array is associative.
* @param array $array
* @return bool
*/
function isAssoc(array $array)
{
$keys = array_keys($array);
return array_keys($keys) !== $keys;
}
Upvotes: 16
Reputation: 1162
Sometimes you can get away with only checking if the first array's Key is 0.
$isSequential = array_keys($arr)[0] === 0
, or, faster, but more verbose version:
reset($arr); $isSequential = key($arr) === 0
Upvotes: 1
Reputation: 466
This question is actually useless when it comes to array of php because with the nature of php an array should not have to be fully associative or indexing, it can be combination of both, the way user have define and assigned the value an array can be a combination of both. see the example below
$y= array(5);
$y["0x"]="n";
$y["vbg"]="12132";
$y[1] = "k";
var_dump($y); //this will output 4 element array
echo "</br>" .$y["0x"]."</br>".$y[0];
for($x=0;$x<sizeof($y);$x++){ // this will output all index elements & gives error after that
echo "</br> index elements ".$y[$x];
}
so the correct question that has to ask is , is all the element in array are associative or index. if you really know that it will only be either associative or indexing not a combination of these two, you can just simply use this method to find wether it is an index or associative array.
function AssocTest(&$arr){
if(is_array($arr)){
reset($arr); // reset pointer to first element of array
if(gettype(key($arr)) == "string"){ //get the type(nature) of first element key
return true;
}else{
return false;
}
}else{
return false;
}
}
you can use it as normal function
echo(AssocTest($y)? "Associative array": "Not an Associative array/ Not an array at all");
and important thing to remember evan you have initialize an array as associative but the names that you have gave the associative array is just numbers it will treat as an index array when it being read by php if you have not explicitly gave the string names. take a look at the example below.
$y["0"]="n";
$y["1"]="12132";
$y["22"] = "k";
//both will get the same output
echo "<br/> s0 ".$y["22"];
echo "<br/> s0 ".$y[22];
for($x=0;$x<count($y);$x++){
echo "<br/> arr ".$y[$x]; // this will output up to 2nd element and give an error after
}
so if you need to be sure all the elements of the array to be exactly indexed or either associative , there is no other way but go true all the elements and check each and every element key by generated index array as post by many people here.
function fullAssocTest(&$arr)
{
if(is_array($arr)){
return (array_keys($arr) !== range(0, count($arr) - 1));
}
}
its less coding, but this thing is really process intensive and really un-necessary work.
Upvotes: 1
Reputation: 805
A lot of the solutions here are elegant and pretty, but don't scale well, and are memory intensive or CPU intensive. Most are creating 2 new data points in memory with this solution from both sides of the comparison. The larger the array the harder and longer the process and memory used, and you lose the benefit of short circuit evaluation. I Did some testing with a few different ideas. Trying to avoid array_key_exists as it is costly, and also avoiding creating new large datasets to compare. I feel this is a simple way to tell if an array is sequential.
public function is_sequential( $arr = [] ){
if( !is_array( $arr ) || empty( $arr ) ) return false;
$i = 0;
$total = count( $arr );
foreach( $arr as $key => $value ) if( $key !== $i++ ) return false;
return true;
}
You run a single count on the main array and store a single integer. You then loop through the array and check for an exact match while iterating the counter. You should have from 1 to count. If it fails it will short circuit out which gives you performance boost when it is false.
Originally I did this with a for loop and checking for isset( $arr[$i] ) but this will not detect null keys which requires array_key_exists, and as we know that is the worst function to use for speed.
Constantly updating variables via foreach to check along with the iterator never growing past it's integer size let's PHP use it's built in memory optimization, caching and garbage collection to keep you at very low resource usage.
Also, I will argue that using array_keys in a foreach is silly when you can simply run $key => $value and check the key. Why create the new data point? Once you abstract away the array keys you've consumed more memory immediately.
Upvotes: 3
Reputation: 2010
Many commenters in this question don't understand how arrays work in PHP. From the array documentation:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08"). Floats in key are truncated to integer. The indexed and associative array types are the same type in PHP, which can both contain integer and string indices.
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):
function keyedNext( &$arr, &$k){
$k = key($arr);
return next($arr);
}
for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
$onlyIntKeys = is_null($k);
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 "").
Upvotes: 79
Reputation: 3792
After some local benchmarking, debugging, compiler probing, profiling, and abusing 3v4l.org to benchmark across more versions (yes, I got a warning to stop) and comparing against every variation I could find...
I give you an organically derived best-average-worst-case scenario associative array test function that is at worst roughly as good as or better than all other average-case scenarios.
/**
* Tests if an array is an associative array.
*
* @param array $array An array to test.
* @return boolean True if the array is associative, otherwise false.
*/
function is_assoc(array &$arr) {
// don't try to check non-arrays or empty arrays
if (FALSE === is_array($arr) || 0 === ($l = count($arr))) {
return false;
}
// shortcut by guessing at the beginning
reset($arr);
if (key($arr) !== 0) {
return true;
}
// shortcut by guessing at the end
end($arr);
if (key($arr) !== $l-1) {
return true;
}
// rely on php to optimize test by reference or fast compare
return array_values($arr) !== $arr;
}
From https://3v4l.org/rkieX:
<?php
// array_values
function method_1(Array &$arr) {
return $arr === array_values($arr);
}
// method_2 was DQ; did not actually work
// array_keys
function method_3(Array &$arr) {
return array_keys($arr) === range(0, count($arr) - 1);
}
// foreach
function method_4(Array &$arr) {
$idx = 0;
foreach( $arr as $key => $val ){
if( $key !== $idx )
return FALSE;
++$idx;
}
return TRUE;
}
// guessing
function method_5(Array &$arr) {
global $METHOD_5_KEY;
$i = 0;
$l = count($arr)-1;
end($arr);
if ( key($arr) !== $l )
return FALSE;
reset($arr);
do {
if ( $i !== key($arr) )
return FALSE;
++$i;
next($arr);
} while ($i < $l);
return TRUE;
}
// naieve
function method_6(Array &$arr) {
$i = 0;
$l = count($arr);
do {
if ( NULL === @$arr[$i] )
return FALSE;
++$i;
} while ($i < $l);
return TRUE;
}
// deep reference reliance
function method_7(Array &$arr) {
return array_keys(array_values($arr)) === array_keys($arr);
}
// organic (guessing + array_values)
function method_8(Array &$arr) {
reset($arr);
if ( key($arr) !== 0 )
return FALSE;
end($arr);
if ( key($arr) !== count($arr)-1 )
return FALSE;
return array_values($arr) === $arr;
}
function benchmark(Array &$methods, Array &$target, $expected){
foreach($methods as $method){
$start = microtime(true);
for ($i = 0; $i < 2000; ++$i) {
//$dummy = call_user_func($method, $target);
if ( $method($target) !== $expected ) {
echo "Method $method is disqualified for returning an incorrect result.\n";
unset($methods[array_search($method,$methods,true)]);
$i = 0;
break;
}
}
if ( $i != 0 ) {
$end = microtime(true);
echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
}
}
}
$true_targets = [
'Giant array' => range(0, 500),
'Tiny array' => range(0, 20),
];
$g = range(0,10);
unset($g[0]);
$false_targets = [
'Large array 1' => range(0, 100) + ['a'=>'a'] + range(101, 200),
'Large array 2' => ['a'=>'a'] + range(0, 200),
'Tiny array' => range(0, 10) + ['a'=>'a'] + range(11, 20),
'Gotcha array' => $g,
];
$methods = [
'method_1',
'method_3',
'method_4',
'method_5',
'method_6',
'method_7',
'method_8'
];
foreach($false_targets as $targetName => $target){
echo "==== Benchmark using $targetName expecing FALSE ====\n";
benchmark($methods, $target, false);
echo "\n";
}
foreach($true_targets as $targetName => $target){
echo "==== Benchmark using $targetName expecting TRUE ====\n";
benchmark($methods, $target, true);
echo "\n";
}
Upvotes: 4
Reputation: 3694
I've come up with next method:
function isSequential(array $list): bool
{
$i = 0;
$count = count($list);
while (array_key_exists($i, $list)) {
$i += 1;
if ($i === $count) {
return true;
}
}
return false;
}
var_dump(isSequential(array())); // false
var_dump(isSequential(array('a', 'b', 'c'))); // true
var_dump(isSequential(array("0" => 'a', "1" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1a" => 'a', "0b" => 'b', "2c" => 'c'))); // false
var_dump(isSequential(array("a" => 'a', "b" => 'b', "c" => 'c'))); // false
*Note empty array is not considered a sequential array, but I think it's fine since empty arrays is like 0 - doesn't matter it's plus or minus, it's empty.
Here are the advantages of this method compared to some listed above:
array_values
does not involve copying - what!?? It certainly does - as will be seen below)I've used benchmark kindly provided by Artur Bodera, where I changed one of the arrays to 1M elements (array_fill(0, 1000000, uniqid()), // big numeric array
).
Here are the results for 100 iterations:
PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Initial memory: 32.42 MB
Testing my_method (isset check) - 100 iterations
Total time: 2.57942 s
Total memory: 32.48 MB
Testing method3 (array_filter of keys) - 100 iterations
Total time: 5.10964 s
Total memory: 64.42 MB
Testing method1 (array_values check) - 100 iterations
Total time: 3.07591 s
Total memory: 64.42 MB
Testing method2 (array_keys comparison) - 100 iterations
Total time: 5.62937 s
Total memory: 96.43 MB
*Methods are ordered based on their memory consumption
**I used echo " Total memory: " . number_format(memory_get_peak_usage()/1024/1024, 2) . " MB\n";
to display memory usage
Upvotes: 0
Reputation: 146
/*
iszba - Is Zero Based Array
Detects if an array is zero based or not.
PARAMS:
$chkvfnc
Callback in the loop allows to check the values of each element.
Signature:
bool function chkvfnc($v);
return:
true continue looping
false stop looping; iszba returns false too.
NOTES:
○ assert: $array is an array.
○ May be memory efficient;
it doesn't get extra arrays via array_keys() or ranges() into the function.
○ Is pretty fast without a callback.
○ With callback it's ~2.4 times slower.
*/
function iszba($array, $chkvfnc=null){
$ncb = !$chkvfnc;
$i = 0;
foreach($array as $k => $v){
if($k === $i++)
if($ncb || $chkvfnc($v))
continue;
return false;
}
return true;
}
• Without callback it is ~30% faster than current leading answer, and possibly more memory efficient.
• Just negate the answer to know if the array should be considered associative.
Upvotes: -1
Reputation: 3060
There are many answers already, but here is the method that Laravel relies on within its Arr class:
/**
* Determines if an array is associative.
*
* An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
*
* @param array $array
* @return bool
*/
public static function isAssoc(array $array)
{
$keys = array_keys($array);
return array_keys($keys) !== $keys;
}
Source: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php
Upvotes: 7
Reputation: 1022
Or you can just use this:
Arr::isAssoc($array)
which will check if array contains any non-numeric key or:
Arr:isAssoc($array, true)
to check if array is strictly sequencial (contains auto generated int keys 0 to n-1)
using this library.
Upvotes: 1
Reputation: 1677
Improvement from Mark Amery
function isAssoc($arr)
{
// Is it set, is an array, not empty and keys are not sequentialy numeric from 0
return isset($arr) && is_array($arr) && count($arr)!=0 && array_keys($arr) !== range(0, count($arr) - 1);
}
This tests if variable exists, if it is an array, if it is not an empty array and if the keys are not sequential from 0.
To see if the array is associative
if (isAssoc($array)) ...
To see if it numeric
if (!isAssoc($array)) ...
Upvotes: -1
Reputation: 480
answers are already given but there's too much disinformation about performance. I wrote this little benchmark script that shows that the foreach method is the fastest.
Disclaimer: following methods were copy-pasted from the other answers
<?php
function method_1(Array &$arr) {
return $arr === array_values($arr);
}
function method_2(Array &$arr) {
for (reset($arr), $i = 0; key($arr) !== $i++; next($arr));
return is_null(key($arr));
}
function method_3(Array &$arr) {
return array_keys($arr) === range(0, count($arr) - 1);
}
function method_4(Array &$arr) {
$idx = 0;
foreach( $arr as $key => $val ){
if( $key !== $idx )
return FALSE;
$idx++;
}
return TRUE;
}
function benchmark(Array $methods, Array &$target){
foreach($methods as $method){
$start = microtime(true);
for ($i = 0; $i < 1000; $i++)
$dummy = call_user_func($method, $target);
$end = microtime(true);
echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
}
}
$targets = [
'Huge array' => range(0, 30000),
'Small array' => range(0, 1000),
];
$methods = [
'method_1',
'method_2',
'method_3',
'method_4',
];
foreach($targets as $targetName => $target){
echo "==== Benchmark using $targetName ====\n";
benchmark($methods, $target);
echo "\n";
}
results:
==== Benchmark using Huge array ====
Time taken with method_1 = 5504.632ms
Time taken with method_2 = 4509.445ms
Time taken with method_3 = 8614.883ms
Time taken with method_4 = 2720.934ms
==== Benchmark using Small array ====
Time taken with method_1 = 77.159ms
Time taken with method_2 = 130.03ms
Time taken with method_3 = 160.866ms
Time taken with method_4 = 69.946ms
Upvotes: 3
Reputation: 6995
function array_is_assoc(array $a) {
$i = 0;
foreach ($a as $k => $v) {
if ($k !== $i++) {
return true;
}
}
return false;
}
Fast, concise, and memory efficient. No expensive comparisons, function calls or array copying.
Upvotes: 8
Reputation: 12238
function is_associative($arr) {
return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
Upvotes: 0
Reputation: 2588
My solution:
function isAssociative(array $array)
{
return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}
array_merge
on a single array will reindex all integer
keys, but not other. For example:
array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);
// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']
So if a list (a non-associative array) is created ['a', 'b', 'c']
then a value is removed unset($a[1])
then array_merge
is called, the list is reindexed starting from 0.
Upvotes: 2
Reputation: 2383
I've used both array_keys($obj) !== range(0, count($obj) - 1)
and array_values($arr) !== $arr
(which are duals of each other, although the second is cheaper than the first) but both fail for very large arrays.
This is because array_keys
and array_values
are both very costly operations (since they build a whole new array of size roughly that of the original).
The following function is more robust than the methods provided above:
function array_type( $obj ){
$last_key = -1;
$type = 'index';
foreach( $obj as $key => $val ){
if( !is_int( $key ) || $key < 0 ){
return 'assoc';
}
if( $key !== $last_key + 1 ){
$type = 'sparse';
}
$last_key = $key;
}
return $type;
}
Also note that if you don't care to differentiate sparse arrays from associative arrays you can simply return 'assoc'
from both if
blocks.
Finally, while this might seem much less "elegant" than a lot of "solutions" on this page, in practice it is vastly more efficient. Almost any associative array will be detected instantly. Only indexed arrays will get checked exhaustively, and the methods outlined above not only check indexed arrays exhaustively, they duplicate them.
Upvotes: 18
Reputation: 709
To merely check whether the array has non-integer keys (not whether the array is sequentially-indexed or zero-indexed):
function has_string_keys(array $array) {
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
If there is at least one string key, $array
will be regarded as an associative array.
Upvotes: 489
Reputation: 1917
By using xarray PHP extension
You can do this very fast (about 30+ times faster in PHP 5.6):
if (array_is_indexed($array)) { }
Or:
if (array_is_assoc($array)) { }
Upvotes: 3
Reputation: 19
Could this be the solution?
public static function isArrayAssociative(array $array) {
reset($array);
return !is_int(key($array));
}
The caveat is obviously that the array cursor is reset but I'd say probably the function is used before the array is even traversed or used.
Upvotes: 2
Reputation: 414
One more fast from source.
Fit encoding of json_encode
(and bson_encode
). So has javascript Array compliance.
function isSequential($value){
if(is_array($value) || ($value instanceof \Countable && $value instanceof \ArrayAccess)){
for ($i = count($value) - 1; $i >= 0; $i--) {
if (!isset($value[$i]) && !array_key_exists($i, $value)) {
return false;
}
}
return true;
} else {
throw new \InvalidArgumentException(
sprintf('Data type "%s" is not supported by method %s', gettype($value), __METHOD__)
);
}
}
Upvotes: 2
Reputation: 10111
As stated by the OP:
PHP treats all arrays as associative
it is not quite sensible (IMHO) to write a function that checks if an array is associative. So first thing first: what is a key in a PHP array?:
The key can either be an integer or a string.
That means there are 3 possible cases:
We can check each case with the following functions.
Note: This function returns true for empty arrays too.
//! Check whether the input is an array whose keys are all integers.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are all integers.
*/
function IsArrayAllKeyInt($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}
Note: This function returns true for empty arrays too.
//! Check whether the input is an array whose keys are all strings.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are all strings.
*/
function IsArrayAllKeyString($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}
Note: This function returns true for empty arrays too.
//! Check whether the input is an array with at least one key being an integer and at least one key being a string.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}
It follows that:
Now, for an array to be a "genuine" array that we are all accustomed to, meaning:
We can check with the following function.
Note: This function returns true for empty arrays too.
//! Check whether the input is an array whose keys are numeric, sequential, and zero-based.
/*!
\param[in] $InputArray (array) Input array.
\return (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray)
{
if(!is_array($InputArray))
{
return false;
}
if(count($InputArray) <= 0)
{
return true;
}
return array_keys($InputArray) === range(0, count($InputArray) - 1);
}
The keys for these arrays are integers:
array(0 => "b");
array(13 => "b");
array(-13 => "b"); // Negative integers are also integers.
array(0x1A => "b"); // Hexadecimal notation.
The keys for these arrays are strings:
array("fish and chips" => "b");
array("" => "b"); // An empty string is also a string.
array("[email protected]" => "b"); // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow's cool" => "b"); // Strings may contain special characters.
array('$tα€k↔øv∈rflöw⛄' => "b"); // Strings may contain all kinds of symbols.
array("functіon" => "b"); // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b"); // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b"); // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b"); // Strings may even be binary!
If you think the key in array("13" => "b")
is a string, you are wrong. From the doc here:
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
For example, the key for these arrays are integers:
array("13" => "b");
array("-13" => "b"); // Negative, ok.
But the key for these arrays are strings:
array("13." => "b");
array("+13" => "b"); // Positive, not ok.
array("-013" => "b");
array("0x1A" => "b"); // Not converted to integers even though it's a valid hexadecimal number.
array("013" => "b"); // Not converted to integers even though it's a valid octal number.
array("18446744073709551616" => "b"); // Not converted to integers as it can't fit into a 64-bit integer.
What's more, according to the doc,
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers.
So the key for this array may or may not be an integer - it depends on your platform.
array("60000000000" => "b"); // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.
Even worse, PHP tends to be buggy if the integer is near the 231 = 2,147,483,648 boundary (see bug 51430, bug 52899). For example, on my local environment (PHP 5.3.8 on XAMPP 1.7.7 on Windows 7), var_dump(array("2147483647" => "b"))
gives
array(1) {
[2147483647]=>
string(1) "b"
}
but on this live demo on codepad (PHP 5.2.5), the same expression gives
array(1) {
["2147483647"]=>
string(1) "b"
}
So the key is an integer in one environment but a string in another, even though 2147483647
is a valid signed 32-bit integer.
Upvotes: 42
Reputation: 12863
I know it's a bit pointless adding an answer to this huge queue, but here's a readable O(n) solution that doesn't require duplicating any values:
function isNumericArray($array) {
$count = count($array);
for ($i = 0; $i < $count; $i++) {
if (!isset($array[$i])) {
return FALSE;
}
}
return TRUE;
}
Rather than check the keys to see if they are all numeric, you iterate over the keys that would be there for a numeric array and make sure they exist.
Upvotes: 3