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

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

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

这:

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

当前回答

我已经使用了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'。

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

其他回答

实际上,我发现自己遇到了类似的情况,试图获取一个数组并将其解析为XML。XML元素名称不能以数字开头——我发现的代码片段不能正确处理带有数字索引的数组。

我的具体情况如下

上面@null (http://stackoverflow .com/a/173589/293332)提供的答案实际上非常接近。我对它被否决感到沮丧:那些不懂正则表达式的人过着非常令人沮丧的生活。

总之,根据他的回答,我得出了以下结论:

/** 
 * Checks if an array is associative by utilizing REGEX against the keys
 * @param   $arr    <array> Reference to the array to be checked
 * @return  boolean
 */     
private function    isAssociativeArray( &$arr ) {
    return  (bool)( preg_match( '/\D/', implode( array_keys( $arr ) ) ) );
}

更多细节请参见PCRE转义序列和PCRE语法页面。

我的特殊情况

这是我正在处理的一个示例数组:

Case A
return  array(
    "GetInventorySummary"  => array(
        "Filters"  => array( 
            "Filter"  => array(
                array(
                    "FilterType"  => "Shape",
                    "FilterValue"  => "W",
                ),
                array(
                    "FilterType"  => "Dimensions",
                    "FilterValue"  => "8 x 10",
                ),
                array(
                    "FilterType"  => "Grade",
                    "FilterValue"  => "A992",
                ),
            ),
        ),
        "SummaryField"  => "Length",
    ),
);

问题是过滤器键是可变的。例如:

Case B
return  array(
    "GetInventorySummary"  => array(
        "Filters"  => array( 
            "Filter"  => array(
                "foo"   =>  "bar",
                "bar"   =>  "foo",
            ),
        ),
        "SummaryField"  => "Length",
    ),
);

为什么我需要协会。检查数组

如果我要转换的数组像情况A一样,我想要返回的是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
    <Filters>
        <Filter>
            <FilterType>Shape</FilterType>
            <FilterValue>W</FilterValue>
        </Filter>
        <Filter>
            <FilterType>Dimensions</FilterType>
            <FilterValue>8 x 10</FilterValue>
        </Filter>
        <Filter>
            <FilterType>Grade</FilterType>
             <FilterValue>A992</FilterValue>
        </Filter>
    </Filters>
    <SummaryField>Length</SummaryField>
</GetInventorySummary>

... 然而,如果我要转换的数组像Case B一样,我想要返回的是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GetInventorySummary>
    <Filters>
        <Filter>
            <foo>bar</foo>
            <bar>foo</bar>
        </Filter>
    </Filters>
    <SummaryField>Length</SummaryField>
</GetInventorySummary>
function isAssoc($arr)
{
    $a = array_keys($arr);
    for($i = 0, $t = count($a); $i < $t; $i++)
    {
        if($a[$i] != $i)
        {
            return false;
        }
    }
    return true;
}

Mark Amery的改进

function isAssoc($arr)
{
    // Is it set, is an array, not empty and keys are not sequentialy numeric from 0
    return isset($arr) && is_array($arr) && count($arr)!=0 && array_keys($arr) !== range(0, count($arr) - 1);
}

这个测试变量是否存在,是否为数组,是否为空数组,以及键是否从0开始连续。

来查看数组是否是关联的

if (isAssoc($array)) ...

看看它是不是数值

if (!isAssoc($array)) ...

一种廉价而肮脏的方法是这样检查:

isset($myArray[count($myArray) - 1])

...如果你的数组是这样的,你可能会得到一个假阳性:

$myArray = array("1" => "apple", "b" => "banana");

一个更彻底的方法可能是检查钥匙:

function arrayIsAssociative($myArray) {
    foreach (array_keys($myArray) as $ind => $key) {
        if (!is_numeric($key) || (isset($myArray[$ind + 1]) && $myArray[$ind + 1] != $key + 1)) {
            return true;
        }
    }
    return false;
}
// this will only return true if all the keys are numeric AND sequential, which
// is what you get when you define an array like this:
// array("a", "b", "c", "d", "e");

or

function arrayIsAssociative($myArray) {
    $l = count($myArray);
    for ($i = 0; $i < $l, ++$i) {
        if (!isset($myArray[$i])) return true;
    }
    return false;
}
// this will return a false positive on an array like this:
$x = array(1 => "b", 0 => "a", 2 => "c", 4 => "e", 3 => "d");

从源头再来一个。 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__)
        );
    }
}