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

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

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

这:

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

当前回答

这是函数-

public function is_assoc_array($array){

    if(is_array($array) !== true){
        return false;
    }else{

        $check = json_decode(json_encode($array));

        if(is_object($check) === true){
            return true;
        }else{
            return false;
        }

    }

}

一些例子

    print_r((is_assoc_array(['one','two','three']))===true?'Yes':'No'); \\No
    print_r(is_assoc_array(['one'=>'one','two'=>'two','three'=>'three'])?'Yes':'No'); \\Yes
    print_r(is_assoc_array(['1'=>'one','2'=>'two','3'=>'three'])?'Yes':'No'); \\Yes
    print_r(is_assoc_array(['0'=>'one','1'=>'two','2'=>'three'])?'Yes':'No'); \\No

在其中一个答案中@devios1有一个类似的解决方案,但这只是使用PHP的内置json相关函数的另一种方式。我还没有检查这个解决方案在性能方面如何与其他已经在这里发布的解决方案相比。但它确实帮助我解决了这个问题。希望这能有所帮助。

其他回答

下面是我使用的方法:

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

只是补充我的意见,我认为它应该是相当有效的基于@nonsensei的基准,以及清晰易读:

function is_associative(array $array): bool
{
    foreach ($array as $key => $value)
    {
        if (!is_string($key)) return false;
    }
    return true;
}

我的解决方案是获得如下数组的键,并检查是否键不是整数:

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

像下面这样获取哈希数组的array_keys是错误的:

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

将输出:

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

因此,将它与评分最高的答案中提到的一系列数字进行比较并不是一个好主意。它总是说它是一个哈希数组如果你试着比较键和范围。

检查数组是否有所有的关联键。使用stdClass & get_object_vars ^):

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

$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));  
var_dump($isAssoc); // true

为什么?函数get_object_vars只返回可访问的属性(请参阅在将数组转换为对象期间发生的更多信息)。然后,只是逻辑上:如果基本数组元素的计数等于对象的可访问属性的计数-所有键都是关联的。

一些测试:

$assocArray = array('apple', 'orange', 'tomato', 'carrot');
$assoc_object = (object) $assocArray; 
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); // false 
//...

$assocArray = array( 0 => 'apple', 'orange', 'tomato', '4' => 'carrot');
$assoc_object = (object) $assocArray; 
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));
var_dump($isAssoc); // false 

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

$assoc_object = (object) $assocArray;
$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));  
var_dump($isAssoc); //false

Etc.

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

这是因为array_keys和array_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'。

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