PHP将所有数组都视为关联数组,因此没有任何内置函数。谁能推荐一种相当有效的方法来检查数组是否“是一个列表”(只包含从0开始的数字键)?

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

$sequentialArray = [
    'apple', 'orange', 'tomato', 'carrot'
];

这:

$assocArray = [
    'fruit1' => 'apple',
    'fruit2' => 'orange',
    'veg1' => 'tomato',
    'veg2' => 'carrot'
];

当前回答

我想出了下一个方法:

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

*注意空数组不被认为是一个连续数组,但我认为这是好的,因为空数组就像0 -不管它是正负,它是空的。

与上面列出的一些方法相比,这种方法的优点如下:

它不涉及数组的复制(有人在这个要点https://gist.github.com/Thinkscape/1965669中提到array_values不涉及复制-什么!??它确实如此-如下所示) 对于更大的数组,它更快,同时对内存更友好

我使用了Artur Bodera提供的基准测试,其中我将其中一个数组更改为1M个元素(array_fill(0, 1000000, uniqid()), //大数字数组)。

以下是100次迭代的结果:

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

*方法的排序基于它们的内存消耗

**我使用echo“Total memory:”。Number_format (memory_get_peak_usage()/1024/ 1024,2) . "MB \ n”;显示内存使用情况

其他回答

另一个变种还没有显示,因为它根本不接受数字键,但我非常喜欢格雷格的一个:

 /* Returns true if $var associative array */  
  function is_associative_array( $array ) {  
    return is_array($array) && !is_numeric(implode('', array_keys($array)));  
  }

如OP所述:

PHP将所有数组都视为关联数组

恕我直言,写一个函数来检查数组是否关联是不太明智的。首先,PHP数组中的键是什么?:

键可以是整数,也可以是字符串。

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

案例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”)。 如果值是非空数组,则只有一个函数返回true。


现在,一个数组要成为我们都习惯的“真正的”数组,这意味着:

它的键都是数字/整数。 它的键是顺序的(即按步骤1递增)。 它的键从零开始。

我们可以用下面的函数来检查。

例3。键包括数字/整数、顺序和从零开始。

注意:该函数对于空数组也返回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中关于数组键的更奇怪的事实)

整数键

这些数组的键都是整数:

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

字符串键

这些数组的键都是字符串:

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!

看起来像字符串的整数键

如果你认为数组中的键("13" => "b")是一个字符串,那你就错了。医生说:

包含有效整数的字符串将被转换为整数类型。例如,键“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.

而且,据医生说,

整数的大小取决于平台,尽管通常的最大值约为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.

更糟糕的是,如果整数接近231 = 2,147,483,648边界(参见bug 51430, bug 52899), PHP就容易出现bug。例如,在我的本地环境(PHP 5.3.8 on XAMPP 1.7.7 on Windows 7)中,var_dump(array("2147483647" => "b")给出

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

但是在codepad (PHP 5.2.5)上的这个现场演示中,相同的表达式给出

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

因此,密钥在一个环境中是整数,而在另一个环境中是字符串,即使2147483647是一个有效的有符号32位整数。

如果你只是寻找非数字键(无论顺序),那么你可能想尝试一下

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

大多数答案的时间/空间复杂度都不是最佳的,或者语义正在发生变化。所以,这里有另一个最快和功能最正确的解决方案:

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

与其他答案相比,这个答案有以下优点:

O(1)的空间复杂度(这里很多答案使用O(n)个空间!) 不对键应用相等性(这是一种不必要且代价高昂的操作) 将输入数组视为不可变(许多答案通过应用突变函数隐式创建了一个副本) 使用函数array_key_exists代替isset(记住,isset额外检查'is not null',从而改变语义) 最坏情况时间复杂度为O(n)(这里许多答案的最佳情况时间复杂度为O(n))

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

•没有回调,比当前领先的回答快30%。 而且可能内存效率更高。

•只需对答案求反,就可以知道数组是否应该被认为是关联的。