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

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

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

这:

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

当前回答

已经有很多答案了,下面是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

其他回答

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

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

换句话说,没有数组键“8”这样的东西,因为它总是(无声地)转换为整数8。因此,试图区分整数和数字字符串是不必要的。

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

function keyedNext( &$arr, &$k){
    $k = key($arr);
    return next($arr);
}

for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
    $onlyIntKeys = is_null($k);

这是因为key()在当前数组位置无效时返回NULL,并且NULL永远不会是一个有效的键(如果你试图使用NULL作为数组键,它会被无声地转换为“”)。

你问了两个不完全等价的问题:

首先,如何确定数组是否只有数字键 其次,如何确定数组是否具有从0开始的连续数字键

考虑一下哪些行为是你真正需要的。(也许对你来说,这两种方法都可以。)

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

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

function isAssoc(array $arr)
{
    if (array() === $arr) return false;
    return array_keys($arr) !== range(0, count($arr) - 1);
}

var_dump(isAssoc(['a', 'b', 'c'])); // false
var_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // false
var_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // true
var_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

快速、简洁、高效记忆。没有昂贵的比较、函数调用或数组复制。

答案已经给出了,但关于表现的虚假信息太多了。 我写了这个小的基准测试脚本,它显示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

这个问题实际上在php数组中是没有用的,因为php的本质是数组不应该是完全关联的或索引的,它可以是两者的组合,用户定义和赋值数组的方式可以是两者的组合。参见下面的示例

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

正确的问题是,数组中的所有元素都是关联的还是下标的。如果你真的知道它只能是关联数组或索引数组而不是两者的组合,你可以简单地使用这个方法来确定它是索引数组还是关联数组。

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

你可以正常使用它

echo(AssocTest($y)?  "Associative array": "Not an Associative array/ Not an array at all");

重要的是,evan,你已经初始化了一个关联数组但是你给关联数组的名字只是数字当它被PHP读取时它会被当作一个索引数组如果你没有显式地给字符串名字。看看下面的例子。

$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

}

如果你需要确保数组中的所有元素都被精确索引,或者是关联的,没有其他方法,只能对所有元素赋真,并检查每个元素键通过生成索引数组,就像这里很多人发布的那样。

function fullAssocTest(&$arr)
{
    if(is_array($arr)){
        return (array_keys($arr) !== range(0, count($arr) - 1));
    }
}

它的代码更少,但这个东西真的是过程密集和真正不必要的工作。