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

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

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

这:

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

当前回答

function is_associative($arr) {
  return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}

其他回答

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

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

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

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

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

可以通过检查编码后返回的第一个字符是{(关联数组)还是[(索引数组)来做到这一点。

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}

A lot of the solutions here are elegant and pretty, but don't scale well, and are memory intensive or CPU intensive. Most are creating 2 new data points in memory with this solution from both sides of the comparison. The larger the array the harder and longer the process and memory used, and you lose the benefit of short circuit evaluation. I Did some testing with a few different ideas. Trying to avoid array_key_exists as it is costly, and also avoiding creating new large datasets to compare. I feel this is a simple way to tell if an array is sequential.

public function is_sequential( $arr = [] ){
    if( !is_array( $arr ) || empty( $arr ) ) return false;

    $i = 0;

    $total = count( $arr );

    foreach( $arr as $key => $value ) if( $key !== $i++ ) return false;

    return true;
}

在主数组上运行一个计数并存储一个整数。然后循环遍历数组并在迭代计数器时检查精确匹配。你应该从1开始计数。如果它失败了,它会短路,当它为假时,它会给你性能的提升。

最初,我这样做了一个for循环和检查isset($arr[$ I]),但这将不检测null键,这需要array_key_exists,我们知道,这是最糟糕的函数使用速度。

通过foreach不断更新变量,并检查迭代器,永远不会超过它的整数大小,让PHP使用它内置的内存优化,缓存和垃圾收集,以保持非常低的资源使用。

此外,我认为在foreach中使用array_keys是愚蠢的,因为您可以简单地运行$key => $value并检查键。为什么要创建新的数据点?一旦抽象出数组键,就会立即消耗更多内存。

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

但为什么要麻烦呢?只需假设数组是您所期望的类型。如果不是,它就会在你面前爆炸——这就是动态编程!测试你的代码,一切都会很好……