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

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

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

这:

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

当前回答

还有另一种方法。

function array_isassociative($array)
{
    // Create new Array,  Make it the same size as the input array
    $compareArray = array_pad(array(), count($array), 0);

    // Compare the two array_keys
    return (count(array_diff_key($array, $compareArray))) ? true : false;

}

其他回答

从源头再来一个。 json_encode(和bson_encode)的拟合编码。javascript数组的一致性也是如此。

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

PHP 8.1增加了一个内置函数来确定数组是否是具有这些语义的列表。函数是array_is_list:

$list = ["a", "b", "c"];

array_is_list($list); // true

$notAList = [1 => "a", 2 => "b", 3 => "c"];

array_is_list($notAList); // false

$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];

array_is_list($alsoNotAList); // false

参考

对于使用Laravel的用户:

如果给定数组是关联数组,则isAssoc返回true。如果数组没有以0开头的连续数字键,则数组被认为是“关联的”:

use Illuminate\Support\Arr;

$isAssoc = Arr::isAssoc(['product' => ['name' => 'Desk', 'price' => 100]]);

// true

$isAssoc = Arr::isAssoc([1, 2, 3]);

// false

https://laravel.com/docs/8.x/helpers#method-array-isassoc

我认为下面两个函数是最好的方法去检查'如果一个数组是结合或数字'。由于'numeric'可能只表示数字键或顺序数字键,下面列出了两个函数来检查这两种情况:

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

The first function checks if each key is an integer value. The second function checks if each key is an integer value and in addition checks if all keys are sequential starting at $base, which defaults to 0 and thus can be omitted if you do not need to specify another base value. key($my_array) returns null if the read pointer is moved past the end of the array, which is what ends the for loop and makes the statement after the for loop return true if all keys were integer. If not, the loop ends prematurely because a key is of type string, and the statement after the for loop will return false. The latter function in addition adds one to $base after each compare, to be able to check if the next key is of the correct value. The strict compare makes it also check if the key is of type integer. The $base = (int) $base part in the first section of the for loop can be left out when $base is omitted or if you make sure it is only called using an integer. But since I can't be sure for everybody, I left it in. The statement is executed only once, anyway. I think these are the most efficient solutions:

Memory wise: No copying of data or key ranges. Doing an array_values or array_keys may seem shorter (less code) but keep in mind what goes on in the background once you make that call. Yes there are more (visible) statements than in some other solutions, but that is not what counts, is it? Time wise: Besides the fact that copying/extracting data and/or keys also takes time, this solution is more efficient than doing a foreach. Again a foreach may seem more efficient to some because it is shorter in notation, but in the background foreach also calls reset, key and next to do it's looping. But in addition it also calls valid to check the end condition, which is avoided here due to the combination with the integer check.

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

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

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