tags:

views:

4675

answers:

14

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');
A: 

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:

for i in 0 to len(your_array):
    if not defined(your-array[i]):
        # this is not an array array, it's an associative array :)

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...

Daren Thomas
Normally just assuming the array is the desired type would be the way to go. But in my case I'm looping through a multidimensional array and am formatting the output depending on which type of array a given node is.
Wilco
A: 

One cheap and dirty way would be to check like this:

isset($myArray[count($myArray) - 1])

...you might get a false positive if your array is like this:

$myArray = array("1" => "apple", "b" => "banana");

A more thorough way might be to check the keys:

function arrayIsAssociative($myArray) {
    foreach (array_keys($myArray) as $ind => $key) {
        if (!is_numeric($key) || (isset($myArray[$ind + 1]) && $myArray[$ind + 1] != $key + 1)) {
            return true;
        }
    }
    return false;
}
// this will only return true if all the keys are numeric AND sequential, which
// is what you get when you define an array like this:
// array("a", "b", "c", "d", "e");

or

function arrayIsAssociative($myArray) {
    $l = count($myArray);
    for ($i = 0; $i < $l, ++$i) {
        if (!isset($myArray[$i])) return true;
    }
    return false;
}
// this will return a false positive on an array like this:
$x = array(1 => "b", 0 => "a", 2 => "c", 4 => "e", 3 => "d");
nickf
A: 
function is_associative($arr) {
  return (array_merge($arr) !== $arr || !is_numeric(implode(array_keys($arr))));
}
scronide
implode takes 2 arguments, plus, that function would return false for an array defined like this:$x = array("1" => "b", "0" => "a");
nickf
The glue parameter of implode() became optional in PHP 4.3.0. Your example array -- $x = array("1" => "b", "0" => "a"); -- has an associative index of non-sequential strings. is_associative() will return true for that array, as expected.
scronide
+28  A: 

This will do it for you

<?php
function isAssoc($arr)
{
    return array_keys($arr) !== range(0, count($arr) - 1);
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true

?>
Greg
Interesting, never would have thought of that!
Darryl Hein
This doesn't always work!Try running it against a `array('ham'=>42)`I think this is a result of a string evaluating as 0 in these cases.The fix is therefore to use !== rather than != in the comparison, which ensures type comparison in addition to value comparison
PeterJCLaw
Bear in mind that PHP can have non-sequential arrays with numeric indices. For example, if you do `$a = array('a','b','c'); unset($a[1]);`, the above function will return true.
JW
This won't be able to check arrays that do not start at the 0 index, or have holes in the array.
Mark Story
A: 

If your looking for just non-numeric keys (no matter the order) then you may want to try

function IsAssociative($array)
{
    return preg_match('/[a-z]/i', implode(array_keys($array)));
}
null
A: 

Yet another way to do this.

function array_isassosciative($array)
{
    // Create new Blank Array
    $compareArray = array();

    // Make it the same size as the input array
    array_pad($compareArray, count($array), 0);

    // Compare the two array_keys
    return (count(array_diff_keys($array, $compareArray))) ? true : false;

}
Mez
+16  A: 

Surely this is a better alternative.

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;

?>
Dave Marshall
Good job. I made this suggestion on the same question (http://stackoverflow.com/questions/902857/php-getting-array-type/1178611#1178611), and the more "manual" solutions got voted up, and I had someone tell me I was wrong because when he compared two different arrays, it wasn't == . LOL
grantwparks
It works perfectly :)
Bruno De Barros
A: 
function isAssoc($arr)
{
    $a = array_keys($arr);
    for($i = 0, $t = count($a); $i < $t; $i++)
    {
     if($a[$i] != $i)
     {
      return false;
     }
    }
    return true;
}
A: 

I just use the key() function. Observe:

<?php
var_dump(key(array('hello'=>'world', 'hello'=>'world'))); //string(5) "hello"
var_dump(key(array('world', 'world')));         //int(0)
var_dump(key(array("0" => 'a', "1" => 'b', "2" => 'c'))); //int(0) who makes string sequetial keys anyway????
?>

Thus, just by checking for false, you can determine whether an array is associative or not.

Bretticus
+1  A: 

Actually the most efficient way is thus:

function is_assoc($a){
   return (array_keys($values) != array_keys(array_keys($values)));
}

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).

It would be more efficient to do: `$a = array_keys($a); return ($a != array_keys($a));`.
Alix Axel
A: 

I compare the difference between the keys of the array and the keys of the result of array_values() of the array, which will always be an array with integer indices. If the keys are the same, it's not an associative array.

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}
+3  A: 
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}
dsims
This is the *only* answer (at the time of my comment) that can deal with the following: $array = array(0=>'blah', 2=>'yep', 3=>'wahey')
Shabbyrobe
A: 

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. Interestingly enough I encountered this while trying to figure out why json_encode was failing for a large array, so I wrote my own replacement for it which failed as well (and this was where it failed).

In essence, 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, but not perfect:

function array_type( $obj ){
    $last_key = -1;
    $type = 'index';
    foreach( $obj as $key => $val ){
        if( !is_int( $key ) ){
            return 'assoc';
        }
        if( $key !== $last_key + 1 ){
            $type = 'sparse';
        }
    $last_key = $key;
    }
    return $type;
}

Note that if I wrote foreach( array_keys( $obj ) as $key ){ } then it would fail just as quickly as the earlier described methods.

Also note that if you don't care to differentiate sparse arrays from associative arrays you can simply return 'assoc' for !is_int($key) and $key.

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.

podperson
A: 

This function can handle: - array with holes in index (e.g. 1,2,4,5,8,10) - array with "0x" keys: e.g. key '08' is associative while key '8' is sequential.

the idea is simple: if one of the keys is of String type, it is associative array, otherwise it's sequential.

function is_asso($a){
    foreach(array_keys($a) as $key)
      if (!is_int($key)) return TRUE;
    return FALSE;
}
LazNiko