提问



PHP将所有数组视为关联的,因此没有任何内置函数。任何人都可以推荐一种相当有效的方法来检查数组是否只包含数字键吗?


基本上,我希望能够区分这个:


$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');


和这个:


$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');

最佳参考


您提出了两个不完全相同的问题:



  • 首先,如何确定数组是否只有数字键

  • 其次,如何确定数组是否具有顺序数字键,从0开始



考虑一下您实际需要的这些行为。 (这可能是为了你的目的。)


第一个问题(简单地检查所有键都是数字)由kurO船长很好地回答。


对于第二个问题(检查数组是否为零索引和顺序),您可以使用以下函数:


function isAssoc(array $arr)
{
    if (array() === $arr) return false;
    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

其它参考1


仅检查数组是否具有非整数键(不是数组是顺序索引还是零索引):


function has_string_keys(array $array) {
  return count(array_filter(array_keys($array), 'is_string')) > 0;
}


如果至少有一个字符串键,$array将被视为关联数组。

其它参考2


当然这是一个更好的选择。


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

其它参考3


这个问题中的许多评论者都不理解数组如何在PHP中工作。从数组文档:[76]



  密钥可以是整数或字符串。如果一个键是整数的标准表示,它将被解释为这样(即8将被解释为8,而08将被解释为08)。键中的浮点数被截断为整数。索引和关联数组类型在PHP中是相同的类型,它们都可以包含整数和字符串索引。



换句话说,没有数组键8,因为它总是(静默地)转换为整数8.因此,尝试区分整数和数字字符串是不必要的。


如果你想要一种最有效的方法来检查一个非整数键的数组,而不需要复制一部分数组(比如array_keys())或者所有这些(比如foreach那样):


for (reset($my_array); is_int(key($my_array)); next($my_array));
$onlyIntKeys = is_null(key($my_array));


这是有效的,因为当当前数组位置无效时,key()返回NULL,而NULL永远不能是有效键(如果您尝试将NULL用作数组键,则会将其静默转换为)。

其它参考4


如OP所述:



  PHP将所有数组视为关联的



编写一个检查数组是否为 associative 的函数是不太明智的(恕我直言)。首先要说的是:PHP数组中的关键是什么?:[78]



  键可以是整数字符串



这意味着有三种可能的情况:



  • 案例1.所有键都是数字/整数

  • 案例2.所有键都是字符串

  • 案例3.有些键是字符串,有些键是数字/整数



我们可以使用以下功能检查每个案例。


案例1:所有键都是数字/整数



注意:此函数也会为空数组返回 true


//! 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);
}


案例2:所有键都是字符串



注意:此函数也会为空数组返回 true


//! 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);
}


案例3.某些键是字符串,有些键是数字/整数



注意:此函数也会为空数组返回 true


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


它遵循:



  • 如果值不是数组,则所有3 函数返回 false

  • 如果值为空数组,则所有3 函数返回 true

    (根据定义,如空集是任何集合 A 的子集,因为它的所有元素都属于 A )。

  • 如果值为非空数组正好1 函数将返回 true






现在,对于一个我们都习以为常的真正数组的数组,意思是:[79]



  • 其键全部为数字/整数

  • 其键是顺序(即按步骤1增加)。

  • 其键从零开始



我们可以查看以下功能。[80]


案例3a。键是数字/整数顺序从零开始



注意:此函数也会为空数组返回 true


//! 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);
}


警告/陷阱(或者,更多关于PHP中数组键的特殊事实)



整数键



这些数组的键是整数:[81]


array(0 => "b");
array(13 => "b");
array(-13 => "b");          // Negative integers are also integers.
array(0x1A => "b");         // Hexadecimal notation.


字符串键



这些数组的键是字符串:[82]


array("fish and chips" => "b");
array("" => "b");                                   // An empty string is also a string.
array("stackoverflow_email@example.com" => "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!


看起来像字符串的整数键



如果您认为array("13" => "b")中的键是字符串,您错了。来自这里的文件:[83]



  包含有效整数的字符串将强制转换为整数类型。例如。键8实际上将存储在8下。另一方面,08将不会被强制转换,因为它不是有效的十进制整数。



例如,这些数组的键是整数:


array("13" => "b");
array("-13" => "b");                        // Negative, ok.


但这些数组的关键是字符串:


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.


根据文件,还有更多,[84]



  整数的大小取决于平台,尽管最大值约为20亿是通常的值(32位签名。)64位平台的最大值通常约为9E18,Windows除外,总是32位.PHP不支持无符号整数。



因此,此阵列的关键可能是也可能不是 整数 - 这取决于您的平台。


array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.


更糟糕的是,如果整数接近2 31 =2,147,483,648边界,则PHP倾向于 buggy (参见错误51430,错误52899)。例如,在我的本地环境(Windows 7上的XAMPP 1.7.7上的PHP 5.3.8)中,var_dump(array("2147483647" => "b"))给出[85] [86]


array(1) {
    [2147483647]=>
    string(1) "b"
}   


但是在这个关于键盘的实时演示(PHP 5.2.5)中,相同的表达式给出了[87]


array(1) {
    ["2147483647"]=>
    string(1) "b"
}


所以关键是一个环境中的整数,而另一个环境中的字符串,即使2147483647是有效的有符号32位整数。

其它参考5


速明智:


function isAssoc($array)
{
    return ($array !== array_values($array));
}


内存明智:


function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}

其它参考6


function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}

其它参考7


实际上最有效的方法是:


function is_assoc($array){
   $keys = array_keys($array);
   return $keys !== array_keys($keys);
}


这是有效的,因为它将键(对于顺序数组总是为0,1,2等)与键的键(总是为0,1,2等)进行比较。

其它参考8


我已经使用array_keys($obj) !== range(0, count($obj) - 1)array_values($arr) !== $arr(它们是彼此的双重,虽然第二种比第一种便宜)但是对于非常大的数组都失败了。


这是因为array_keysarray_values都是非常昂贵的操作(因为它们构建了一个大小与原始大小相同的全新数组)。


以下函数比上面提供的方法更强大:


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


另请注意,如果您不关心将稀疏数组与关联数组区分开来,您只需从两个if块返回'assoc'


最后,虽然这看起来不像本页面上的许多解决方案那么优雅,但实际上它的效率要高得多。几乎任何关联数组都会立即被检测到。只有索引数组才能得到详尽的检查,上面列出的方法不仅详尽地检查了索引数组,而且还复制了它们。

其它参考9


我认为以下两个函数是检查如果数组是关联的还是数字的的最佳方法。由于数字可能仅表示数字键或仅表示顺序数字键,因此下面列出了两个检查任一条件的函数:


function is_indexed_array(&$arr) {
  for (reset($arr); is_int(key($arr)); next($arr));
  return is_null(key($arr));
}

function is_sequential_array(&$arr, $base = 0) {
  for (reset($arr), $base = (int) $base; key($arr) === $base++; next($arr));
  return is_null(key($arr));
}


第一个函数检查每个键是否为整数值。第二个函数检查每个键是否为整数值,另外检查所有键是否从$ base开始是连续的,默认为0,因此如果不需要指定另一个基值,则可以省略。如果读指针移过数组的末尾,那么key($ my_array)返回null,这就是for循环的结束,如果所有键都是整数,则使for循环后的语句返回true。如果不是,则循环过早结束,因为键的类型为string,而for循环后的语句将返回false。后一个函数在每次比较后另外添加一个$ base,以便能够检查下一个键是否具有正确的值。严格比较使它还检查键是否为整数类型。当省略$ base或者确保仅使用整数调用时,可以省略for循环第一部分中的$ base =(int)$ base部分。但是因为我不能确定每个人,所以我把它留了下来。无论如何,这句话只执行一次。我认为这些是最有效的解决方案:



  • 内存明智:不复制数据或键范围。执行array_values或array_keys可能看起来更短(代码更少)但请记住一旦进行调用后背景中发生的事情。是的,有更多(可见)陈述,而不是其他一些解决方案,但这不重要,是吗?

  • 时间方面:除了复制/提取数据和/或密钥也需要时间之外,这种解决方案比做foreach更有效。对于某些人来说,foreach似乎更有效,因为它的符号更短,但在后台foreach也调用reset,key和next来进行循环。但另外它也调用有效来检查结束条件,这是由于结合整数检查,因此避免使用。



请记住,数组键只能是整数或字符串,严格的数字字符串(如1(但不是01))将转换为整数。除了计算是否希望数组是顺序的之外,检查整数键的唯一需要的操作是什么。当然,如果is_indexed_array返回false,则可以将数组视为关联数组。我说看到了,因为事实上它们都是。

其它参考10


这个功能可以处理:



  • 带索引孔的数组(例如1,2,4,5,8,10)

  • 带有0x键的数组:例如键08是关联的,而键8是顺序的。



这个想法很简单:如果其中一个键不是整数,则它是关联数组,否则它是连续的。


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

其它参考11


我注意到这个问题有两种流行的方法:一种使用array_values(),另一种使用key()。为了找出哪个更快,我写了一个小程序:



$arrays = Array(
  'Array #1' => Array(1, 2, 3, 54, 23, 212, 123, 1, 1),
  'Array #2' => Array("Stack", 1.5, 20, Array(3.4)),
  'Array #3' => Array(1 => 4, 2 => 2),
  'Array #4' => Array(3.0, "2", 3000, "Stack", 5 => "4"),
  'Array #5' => Array("3" => 4, "2" => 2),
  'Array #6' => Array("0" => "One", 1.0 => "Two", 2 => "Three"),
  'Array #7' => Array(3 => "asdf", 4 => "asdf"),
  'Array #8' => Array("apple" => 1, "orange" => 2),
);

function is_indexed_array_1(Array &$arr) {
  return $arr === array_values($arr);
}

function is_indexed_array_2(Array &$arr) {
  for (reset($arr), $i = 0; key($arr) === $i++; next($arr))
    ;
  return is_null(key($arr));
}

// Method #1
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_1($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

// Method #2
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_2($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";


CentOS上PHP 5.2程序的输出如下:



  方法#1的时间= 10.745ms

  方法#2的时间= 18.239ms



PHP 5.3的输出产生了类似的结果。显然使用array_values()要快得多。

其它参考12


已有很多答案,但这里是Laravel在其Arr类中依赖的方法:


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


资料来源:https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php [88]

其它参考13


这也可以(演示):[89]


function array_has_numeric_keys_only(array $array)
{
    try {
        SplFixedArray::fromArray($array, true);
    } catch (InvalidArgumentException $e) {
        return false;
    }
    return true;
}


请注意,这个答案的要点是告知您SplFixedArray的存在,而不是鼓励您使用例外进行这些类型的测试。

其它参考14


通过使用xarray PHP扩展[90]


您可以非常快速地完成此任务(在PHP 5.6中快30倍以上):


if (array_is_indexed($array)) {  }


要么:


if (array_is_assoc($array)) {  }

其它参考15


我的解决方案


function isAssociative(array $array)
{
    return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}


单个阵列上的array_merge将重新索引所有integer键,而不是其他键。例如:


array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);

// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']


因此,如果创建了一个列表(非关联数组)['a', 'b', 'c'],则删除一个值unset($a[1])然后调用array_merge,从0开始重新索引列表。

其它参考16


function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}


快速,简洁,内存高效。没有昂贵的比较,函数调用或数组复制。

其它参考17


解决这个问题的一种方法是搭载json_encode,它已经有了自己的内部方法来区分关联数组和索引数组,以便输出正确的JSON。


您可以通过检查编码后返回的第一个字符是{(关联数组)还是[(索引数组)来执行此操作。


echo substr(json_encode($the_array), 0, 1) == '{' ? 'yes' : 'no';

其它参考18


除非PHP有内置功能,否则你不能在小于O(n)的情况下执行它 - 枚举所有键并检查整数类型。事实上,你还要确保没有漏洞,所以你的算法看起来像:


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


但为什么要这么麻烦?假设数组是您期望的类型。如果它不是,那就会在你脸上爆炸 - 这是你的动态编程!测试你的代码,一切都会好的......

其它参考19


这是我使用的方法:


function is_associative ( $a )
{
    return in_array(false, array_map('is_numeric', array_keys($a)));
}

assert( true === is_associative(array(1, 2, 3, 4)) );

assert( false === is_associative(array('foo' => 'bar', 'bar' => 'baz')) );

assert( false === is_associative(array(1, 2, 3, 'foo' => 'bar')) );


请注意,这并不能解决以下特殊情况:


$a = array( 1, 2, 3, 4 );

unset($a[1]);

assert( true === is_associative($a) );


对不起,不能帮助你。对于尺寸合适的数组而言,它也有一定的性能,因为它不会制作不必要的副本。正是这些小东西使Python和Ruby更好地写入...: P

其它参考20


<?php

function is_list($array) {
    return array_keys($array) === range(0, count($array) - 1);
}

function is_assoc($array) {
    return count(array_filter(array_keys($array), 'is_string')) == count($array);
}

?>


得分最高的这两个例子都无法正常使用像$array = array('foo' => 'bar', 1)这样的数组

其它参考21


我认为标量数组的定义会因应用程序而异。也就是说,某些应用程序需要更严格意义上的标量数组,而某些应用程序需要更宽松的意义。


下面我介绍3种不同严格的方法。


<?php
/**
 * Since PHP stores all arrays as associative internally, there is no proper
 * definition of a scalar array.
 * 
 * As such, developers are likely to have varying definitions of scalar array,
 * based on their application needs.
 * 
 * In this file, I present 3 increasingly strict methods of determining if an
 * array is scalar.
 * 
 * @author David Farrell <DavidPFarrell@gmail.com>
 */

/**
 * isArrayWithOnlyIntKeys defines a scalar array as containing
 * only integer keys.
 * 
 * If you are explicitly setting integer keys on an array, you
 * may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    foreach ($a as $k => $v)
        if (!is_int($k))
            return false;
    return true;
}

/**
 * isArrayWithOnlyAscendingIntKeys defines a scalar array as
 * containing only integer keys in ascending (but not necessarily
 * sequential) order.
 * 
 * If you are performing pushes, pops, and unsets on your array,
 * you may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyAscendingIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $prev = null;
    foreach ($a as $k => $v)
    {
        if (!is_int($k) || (null !== $prev && $k <= $prev))
            return false;
        $prev = $k;
    }
    return true;
}

/**
 * isArrayWithOnlyZeroBasedSequentialIntKeys defines a scalar array
 * as containing only integer keys in sequential, ascending order,
 * starting from 0.
 * 
 * If you are only performing operations on your array that are
 * guaranteed to either maintain consistent key values, or that
 * re-base the keys for consistency, then you can use this function.
 * 
 * @param array $a
 * @return boolean
 */
function isArrayWithOnlyZeroBasedSequentialIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $i = 0;
    foreach ($a as $k => $v)
        if ($i++ !== $k)
            return false;
    return true;
}

其它参考22


这可能是解决方案吗?


  public static function isArrayAssociative(array $array) {
      reset($array);
      return !is_int(key($array));
  }


需要注意的是,数组游标是重置的,但我可能会说,在遍历或使用数组之前,可能会使用该函数。

其它参考23


我知道添加这个巨大队列的答案有点无意义,但这里是一个可读的O(n)解决方案,不需要复制任何值:


function isNumericArray($array) {
    $count = count($array);
    for ($i = 0; $i < $count; $i++) {
        if (!isset($array[$i])) {
            return FALSE;
        }
    }
    return TRUE;
}


而不是检查键以查看它们是否都是数字键,而是迭代将存在于数字数组中的键并确保它们存在。

其它参考24


来源更快一点。
适合编码json_encode(和bson_encode)。 javascript阵列符合性也是如此。[91]


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

其它参考25


已经给出了答案但是对于表现有太多的虚假信息。
我写了这个小基准脚本,表明foreach方法是最快的。


免责声明:以下方法是从其他答案中复制粘贴的


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


结果:


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

其它参考26


经过一些本地基准测试,调试,编译器探测,分析和滥用3v4l.org以在更多版本中进行基准测试(是的,我收到警告要停止)和
比较我能找到的每一个变化......


我给你一个有机衍生的最佳平均最差情况关联数组测试函数,其最差大致与所有其他平均情况一样好或更好。


愿上帝怜悯我们的灵魂。

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


来自https://3v4l.org/rkieX:[92]


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

其它参考27


我比较了数组的键和数组的array_values()结果的键之间的差异,它始终是一个带整数索引的数组。如果键是相同的,它不是关联数组。



function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}

其它参考28


修改最流行的答案。

这需要更多的处理,但更准确。


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

其它参考29


在我看来,如果一个数组的任何键不是整数,那么它应该被接受为关联的。浮点数和空字符串。


同样,非序列整数必须被视为关联式(0,2,4,6),因为这种类型的数组不能通过这种方式与for循环一起使用:


$n =count($arr);
for($i=0,$i<$n;$i++) 


下面的函数的第二部分确实检查键是否已编入索引。它也适用于具有负值的键。例如(-1,0,1,2,3,4,5)


count() = 7 , max = 5, min=-1



if( 7 == (5-(-1)+1 ) // true
    return false; // array not associative


/** 
 * isAssoc Checks if an array is associative
 * @param $arr reference to the array to be checked
 * @return bool 
 */     
function IsAssoc(&$arr){
    $keys= array_keys($arr);
    foreach($keys as $key){
        if (!is_integer($key))
            return true;
    }
    // if all keys are integer then check if they are indexed
    if(count($arr) == (max($keys)-min($keys)+1))
        return false;
    else
        return true;
}