940

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'
];
4
  • 13
    This method has caveats, but often I just do if (isset($array[0])), which is simple and fast. Of course, you should first be sure the array isn't empty, and you should have some knowledge on the possible contents of the array so that the method couldn't fail (like mixed numeric/associative, or non-sequential). Commented Mar 12, 2016 at 17:58
  • 1
    @OlleHärstedt Not according to US High Court. ;-) Commented Mar 6, 2020 at 13:56
  • 1
    @MCEmperor Wiki's own page about "tomato" states it's a fruit en.wikipedia.org/wiki/Tomato definitions change drastically when money comes into play :P Just ask Jaffa "cake" about it Commented Apr 18, 2021 at 23:33
  • Since PHP 8.1 there is the function "array_is_list", which helps in determin if an array is sequential or associative. ["car","dog","house"] and [0 => ["car", "dog", "house"]] are true, rest is false array_is_array($myarray) == true means $myarray is sequential Doc's can be found here:php.net/manual/en/function.array-is-list.php Commented Sep 8 at 20:31

47 Answers 47

1
2
2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

-1; this is O(n) in additional memory used (since it created multiple new arrays with as many elements as $array), the answer doesn't address the ambiguity of the question that was asked nor explain exactly how it's defining a list / non-associative array, and even if neither of these points were true it's unclear that this adds any value compared to other answers already posted.
1

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.

Comments

1

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.

Comments

1

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

Comments

1

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;
}

Comments

0

Modification on the most popular answer.
This takes a little more processing, but is more accurate.

<?php
//$a is a subset of $b
function isSubset($a, $b)
{
    foreach($a =>$v)
        if(array_search($v, $b) === false)
            return false;

    return true;

    //less effecient, clearer implementation. (uses === for comparison)
    //return array_intersect($a, $b) === $a;
}

function isAssoc($arr)
{
    return !isSubset(array_keys($arr), range(0, count($arr) - 1));
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false 
//(use === in isSubset to get 'true' for above statement)
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
?>

1 Comment

-1; this will take O(n²) time to complete given a sequential array of size n. That's going to be horribly inefficient for large enough arrays.
0

Yet another way to do this.

function array_isassociative($array)
{
    // Create new Array,  Make it the same size as the input array
    $compareArray = array_pad(array(), count($array), 0);

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

}

Comments

0
function is_associative($arr) {
  return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}

5 Comments

implode takes 2 arguments, plus, that function would return false for an array defined like this: $x = array("1" => "b", "0" => "a");
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.
I like this one. The first conditional will detect associative arrays where numeric indices are not numerically sequential, or where the first index is not "0", because array_merge will re-index keys of a numerically indexed (but possibly associative) array.
-1; this uses O(n) additional memory when $arr has n items, plus there's no explanation of what it does nor exploration of the ambiguity of the question that was asked. It also treats an array that has sequential numeric keys and the empty string as a key as non-associative, which defies any sane definition one might draw up between an 'associative' and 'sequential' array.
@MarkAmery Interesting point about the empty string as a key.
0

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:

  • It does not involve copying of arrays (someone mentioned in this gist https://gist.github.com/Thinkscape/1965669 that array_values does not involve copying - what!?? It certainly does - as will be seen below)
  • It's faster for bigger arrays and more memory friendly at the same time

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

3 Comments

if you /1024 the unit is MiB (Mebibyte) if you /1000 the unit is MB (Megabyte). Mega === 1000000, in software engineering and in phsyics, on the moon and on the earth and also inside your computer. Mega is never 1024*1024. And Kilo is always 1000, not 1024.
wow @DanFromGermany, thanks for you incredibly useful comment! That is good to know. PS. by the way in fact Mega is exactly 1024*1024 inside my computer, it's a non standard one :)
Nice to do a benchmark, but have a look at stackoverflow.com/a/35858728/4152976 where there is no call to array_key_exists()
-1

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;
}

1 Comment

-1; this uses O(n) additional memory when $array has n items, and writing (someboolean) ? false : true instead of !someboolean is horrible and gratuitously verbose.
-1

My solution is to get keys of an array like below and check that if the key is not integer:

private function is_hash($array) {
    foreach($array as $key => $value) {
        return ! is_int($key);
    }
    return false;
}

It is wrong to get array_keys of a hash array like below:

array_keys(array(
       "abc" => "gfb",
       "bdc" => "dbc"
       )
);

will output:

array(
       0 => "abc",
       1 => "bdc"
)

So, it is not a good idea to compare it with a range of numbers as mentioned in top rated answer. It will always say that it is a hash array if you try to compare keys with a range.

2 Comments

The question is how to check if an array is sequential though. The array array(1 => 'foo', 0 => 'bar') is not sequential but will pass your test. For why that makes a difference, try json_encode($array) with sequential and associative arrays.
yes, i guess i got pretty confused and stuck with the above answers. Which kept comparing array_keys with a range and thought they will have an output which is comparison whether it is a hash or not. So my answer is to them and also to whom thinks that array_keys gives values are sequential. that's all. And also function name is is_hash so yes it doesn't tell you whether it is sequential or not
-1

Actually, I found myself in a similar situation trying to take an array and parse it into XML. XML element names cannot begin with numbers -- and the code snippets I found did not correctly deal with arrays with numeric indexes.

Details on my particular situation are below

The answer provided above by @null ( http:// stackoverflow .com/a/173589/293332 ) was actually pretty darn close. I was dismayed that it got voted down tho: Those who do not understand regex lead very frustrating lives.

Anyway, based upon his answer, here is what I ended up with:

/** 
 * Checks if an array is associative by utilizing REGEX against the keys
 * @param   $arr    <array> Reference to the array to be checked
 * @return  boolean
 */     
private function    isAssociativeArray( &$arr ) {
    return  (bool)( preg_match( '/\D/', implode( array_keys( $arr ) ) ) );
}

See the PCRE Escape Sequences and PCRE Syntax pages for further details.

My Particular Situation

Here is an example array that I am dealing with:

Case A
return  array(
    "GetInventorySummary"  => array(
        "Filters"  => array( 
            "Filter"  => array(
                array(
                    "FilterType"  => "Shape",
                    "FilterValue"  => "W",
                ),
                array(
                    "FilterType"  => "Dimensions",
                    "FilterValue"  => "8 x 10",
                ),
                array(
                    "FilterType"  => "Grade",
                    "FilterValue"  => "A992",
                ),
            ),
        ),
        "SummaryField"  => "Length",
    ),
);

The catch is that the filter key is variable. For example:

Case B
return  array(
    "GetInventorySummary"  => array(
        "Filters"  => array( 
            "Filter"  => array(
                "foo"   =>  "bar",
                "bar"   =>  "foo",
            ),
        ),
        "SummaryField"  => "Length",
    ),
);

Why I Need Assoc. Array Checker

If the array I am transforming is like Case A, what I want returned is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
    <Filters>
        <Filter>
            <FilterType>Shape</FilterType>
            <FilterValue>W</FilterValue>
        </Filter>
        <Filter>
            <FilterType>Dimensions</FilterType>
            <FilterValue>8 x 10</FilterValue>
        </Filter>
        <Filter>
            <FilterType>Grade</FilterType>
             <FilterValue>A992</FilterValue>
        </Filter>
    </Filters>
    <SummaryField>Length</SummaryField>
</GetInventorySummary>

... However, if the array I am transforming is like Case B, what I want returned is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
    <Filters>
        <Filter>
            <foo>bar</foo>
            <bar>foo</bar>
        </Filter>
    </Filters>
    <SummaryField>Length</SummaryField>
</GetInventorySummary>

1 Comment

This isAssociativeArray() returns false for array(4=>"four",9=>"nine"), array("002"=>"two","007"=>"james") and array("a", ""=>"empty", "b"), which are clearly associative.
-1

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

2 Comments

Wait, what, where did I contribute this? Or did you mean to say "Improvement on Mark Amery", as in an improvement on the accepted answer that is attributed to me? Please be aware, in the latter case, that I didn't author that answer - my edits to it have simply caused the authorship attribution algorithm for Community Wiki posts to put my name on it.
isset() is entirely useless here, right? $arr is unconditionally declared in the function signature, the $arr is checked for being an array, so null is no concern.
-1
/*
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.

Comments

-2

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

1 Comment

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

Best function to detect associative array (hash array)

<?php
function is_assoc($arr) { return (array_values($arr) !== $arr); }
?>

3 Comments

fails for single element associative arrays
Isn't this a duplicate answer ?
Can you give an example, Jinu? I don't see this behavior. (Although I use == not ===; I don't know why so many use ===.)
-3

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.

Comments

1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.