Skip to content

Instantly share code, notes, and snippets.

@Thinkscape
Created March 3, 2012 11:35
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save Thinkscape/1965669 to your computer and use it in GitHub Desktop.
Save Thinkscape/1965669 to your computer and use it in GitHub Desktop.
A benchmark of several methods for checking if PHP array is associative
<?php
if(!isset($argv[1])){
echo "Usage: ".$argv[0]." (number of iterations)\n";
exit(1);
}
/**
* Arrays to check
*/
$tests = array(
array(array(
'foo' => 'bar',
)),
array(array(
'bar',
'foo' => 'bar',
'baz',
)),
array(null),
array(true),
array(false),
array(0),
array(1),
array(0.0),
array(1.0),
array('string'),
array(array(0, 1, 2)),
array(new stdClass),
array_fill(0,1000,uniqid()), // big numeric array
array_fill_keys(range(2,1000,3),uniqid()), // big misaligned numeric array (=associative)
array_fill_keys( // big associative array
str_split(
str_repeat(uniqid('',true),100),
3
),
true
)
);
$iterations = (int)$argv[1];
/**
* Common methods to check associative array
*/
$methods = array(
'method1 (array_values check)' =>
function($array){
return (array_values($array) !== $array);
},
'method2 (array_keys comparison)' =>
function($array){
$array = array_keys($array); return ($array !== array_keys($array));
},
'method3 (array_filter of keys)' =>
function($array){
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
);
/**
* Perform benchmark on each method
*/
foreach($methods as $name=>$func){
echo "Testing $name - $iterations iterations\n";
$time = microtime(true);
for($x=0;$x<$iterations;$x++){
foreach($tests as $array){
$func($array);
}
}
/**
* Show results
*/
$totalTime = (microtime(true) - $time);
$avgTime = $totalTime / ($iterations * count($tests));
echo " Total time: ".number_format($totalTime,5,'.',' ')." s\n";
echo " Average : ".number_format($avgTime*1000,5,'.',' ')." ms / test \n";
echo "\n";
}
@alixaxel
Copy link

alixaxel commented Mar 3, 2012

Beware that the fastest method should consume more memory while the GC doesn't kicks in.

@Thinkscape
Copy link
Author

@alixaxel That is correct, it will consume a little bit more memory than method 2 - but that's temporary usage per each fn call (per test) and becomes visible only for flat, long arrays (say 1000+ keys).

@fijiwebdesign
Copy link

Method 3 is what I wanted since it doesn't matter if the array is sequential or not. You can always coerce an array into a sequential one using array_values($array).

I added a method 4 which improves the speed by doing type casting instead of type checking which appears to be much faster.

'method4 (foreach typecast)' =>
        function($array){
            foreach($array as $key => $value) {
                if ($key !== (int) $key) {
                    return true;
                }
            }
            return false;
        },
Testing method1 (array_values check) - 1000 iterations
  Total time: 0.40560 s
  Average   : 0.02704 ms / test

Testing method2 (array_keys comparison) - 1000 iterations
  Total time: 0.57720 s
  Average   : 0.03848 ms / test

Testing method3 (array_filter of keys) - 1000 iterations
  Total time: 5.99041 s
  Average   : 0.39936 ms / test

Testing method4 (foreach typecast) - 1000 iterations
  Total time: 0.54600 s
  Average   : 0.03640 ms / test

Now it is comparable with the first two.

@fijiwebdesign
Copy link

Also, there is no copying of array keys or values so no extra memory consumption. Especially array_values().

In my case I'm saving arrays to storage and schema depends on array type, The array may have blobs of data several Mb in size. So copying all values would be memory expensive. Instead working with indexed arrays I use foreach() so you don't assume keys are sequential.

@olivernybroe
Copy link

PHP 7.1.4

Testing method1 (array_values check) - 100000 iterations
  Total time: 2.32235 s
  Average   : 0.00155 ms / test 

Testing method2 (array_keys comparison) - 100000 iterations
  Total time: 3.17074 s
  Average   : 0.00211 ms / test 

Testing method3 (array_filter of keys) - 100000 iterations
  Total time: 6.25823 s
  Average   : 0.00417 ms / test 

@ScorpioT1000
Copy link

ScorpioT1000 commented Aug 30, 2017

From PHPTester

PHP 5.5

Testing method1 (array_values check) - 10000 iterations
Total time: 0.85214 s
Average : 0.00568 ms / test

Testing method2 (array_keys comparison) - 10000 iterations
Total time: 1.57167 s
Average : 0.01048 ms / test

Testing method3 (array_filter of keys) - 10000 iterations
Total time: 2.99962 s
Average : 0.02000 ms / test

Testing method4 (foreach typecast) - 10000 iterations
Total time: 1.11735 s
Average : 0.00745 ms / test

PHP 5.6

Testing method1 (array_values check) - 10000 iterations
Total time: 1.00357 s
Average : 0.00669 ms / test

Testing method2 (array_keys comparison) - 10000 iterations
Total time: 1.77896 s
Average : 0.01186 ms / test

Testing method3 (array_filter of keys) - 10000 iterations
Total time: 3.71037 s
Average : 0.02474 ms / test

Testing method4 (foreach typecast) - 10000 iterations
Total time: 1.34244 s
Average : 0.00895 ms / test

PHP 7.0

Testing method1 (array_values check) - 10000 iterations
Total time: 0.23163 s
Average : 0.00154 ms / test

Testing method2 (array_keys comparison) - 10000 iterations
Total time: 0.30419 s
Average : 0.00203 ms / test

Testing method3 (array_filter of keys) - 10000 iterations
Total time: 0.60409 s
Average : 0.00403 ms / test

Testing method4 (foreach typecast) - 10000 iterations
Total time: 0.56797 s
Average : 0.00379 ms / test

@bpolaszek
Copy link

Method 1 is even more faster with PHP 7.2:

Testing method1 (array_values check) - 10000 iterations
Total time: 0.02857 s
Average : 0.00019 ms / test (!)

Testing method2 (array_keys comparison) - 10000 iterations
Total time: 0.19137 s
Average : 0.00128 ms / test

Testing method3 (array_filter of keys) - 10000 iterations
Total time: 0.48394 s
Average : 0.00323 ms / test

Testing method4 (foreach typecast) - 10000 iterations
Total time: 0.41342 s
Average : 0.00276 ms / test

@aminnairi
Copy link

Hi, I just came with an idea of a is_associative_array function if you are still open to contributions:

<?php

function is_associative_array( array &$a ): bool {

  $l = count( $a );

  for ( $i = 0; $i < $l; $i++ ) {
    if ( key( $a ) !== $i ) {
      return true;

    }

    next( $a );

  }

  return false;

}

Tell me about it!

@oriadam
Copy link

oriadam commented May 19, 2021

Hi, I just came with an idea of a is_associative_array function if you are still open to contributions:

<?php

function is_associative_array( array &$a ): bool {
  $l = count( $a );
  for ( $i = 0; $i < $l; $i++ ) {
    if ( key( $a ) !== $i ) {
      return true;
    }
    next( $a );
  }
  return false;
}

Tell me about it!

i just tested your method and it is way worse than all others... sorry :)

source: https://gist.github.com/oriadam/5e8424608fb3f8d70ec3e9ae344dbf8f

Testing 10000 iterations on PHP version 7.3.25

if (array_key_first($array)!==0) return true; return array_values($array) !== $array;
  Total time: 0.26179 s
  Average   : 0.00175 ms / test 

return array_values($array) !== $array;
  Total time: 0.23978 s
  Average   : 0.00160 ms / test 

return range(0,count($array)) !== array_keys($array);
  Total time: 0.44664 s
  Average   : 0.00298 ms / test 

return array_keys($array); return ($array !== array_keys($array));
  Total time: 0.50882 s
  Average   : 0.00339 ms / test 

return count(array_filter(array_keys($array), "is_string")) > 0;
  Total time: 3.99202 s
  Average   : 0.02661 ms / test 

foreach...$i++
  Total time: 1.23879 s
  Average   : 0.00826 ms / test 

for...key...next
  Total time: 7.73038 s
  Average   : 0.05154 ms / test 

@funder7
Copy link

funder7 commented Jan 15, 2022

check this out:

$methods = [
 
    // [...]

    'method4 (revision of method1, with php8.1 fn)' =>
       function ($array) {
            return (array_is_list($array) === false);
        },

    'method5 (json_encode output parsing with array_is_list - php>=8.1)' =>
       static function ($array) {
            // consider empty, and [0, 1, 2, ...] sequential
            if (empty($array) || array_is_list($array)) {
                return false;
            }

            // first scenario:
            // [  1  => [*any*] ]
            // [ 'a' => [*any*] ]
            foreach ( $array as $key => $value ) {
                if (is_array($value)) {
                    return true;
                }
            }

            // second scenario: read the json string
            $jsonNest = json_encode($array, JSON_THROW_ON_ERROR);

            return str_contains($jsonNest, '{');
        },


    'method6 (json_encode output parsing - php=8.0)' =>
       static function ($array) {

            if (empty($array) || array_values($array) !== $array) { return false; }

            foreach ( $array as $key => $value ) { if (is_array($value)) { return true; } }
            
            $jsonNest = json_encode($array, JSON_THROW_ON_ERROR);

            return str_contains($jsonNest, '{');
        },

];

Results

Testing method1 (array_values check) - 10000 iterations
  Total time: 0.60444 s
  Average   : 0.00403 ms / test 

Testing method2 (array_keys comparison) - 10000 iterations
  Total time: 0.32689 s
  Average   : 0.00218 ms / test 

Testing method3 (array_filter of keys) - 10000 iterations
  Total time: 2.22523 s
  Average   : 0.01483 ms / test 

Testing method4 (revision of method1, with php8.1 fn) - 10000 iterations
  Total time: 0.09834 s
  Average   : 0.00066 ms / test 

Testing method5 (json_encode output parsing with array_is_list - php>=8.1) - 10000 iterations
  Total time: 0.47469 s
  Average   : 0.00316 ms / test

Testing method6 (json_encode output parsing - php<=8.0) - 10000 iterations
  Total time: 1.01134 s
  Average   : 0.00674 ms / test 

Anyone curious about the benchmark without the static closure?

Testing method5 (json_encode output parsing with array_is_list - php>=8.1) - 10000 iterations
  Total time: 1.08914 s
  Average   : 0.00726 ms / test 

Testing method6 (json_encode output parsing - php=8.0) - 10000 iterations
  Total time: 4.92953 s
  Average   : 0.03286 ms / test 

They are much slower!

other methods perform the same instead. Somebody can explain me why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment