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

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

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

这:

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

当前回答

修改最流行的答案。 这需要更多的处理,但更准确。

<?php
//$a is a subset of $b
function isSubset($a, $b)
{
    foreach($a =>$v)
        if(array_search($v, $b) === false)
            return false;

    return true;

    //less effecient, clearer implementation. (uses === for comparison)
    //return array_intersect($a, $b) === $a;
}

function isAssoc($arr)
{
    return !isSubset(array_keys($arr), range(0, count($arr) - 1));
}

var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false 
//(use === in isSubset to get 'true' for above statement)
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
?>

其他回答

如果你只是寻找非数字键(无论顺序),那么你可能想尝试一下

function IsAssociative($array)
{
    return preg_match('/[a-z]/i', implode(array_keys($array)));
}

实际上,我发现自己遇到了类似的情况,试图获取一个数组并将其解析为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>

简单地说,你可以通过以下步骤检查数组是否关联

使用array_keys()将数组的所有键转换为一个数组 使用array_filter()和从数组中过滤非数字键 is_numeric () 比较过滤数组和实际数组中的元素数量,如果两个数组中的元素数量不相等,则为关联数组。

以上步骤的功能如下所示。

 function isAssociative(array $array)
    {
        return count(array_filter(array_keys($array), function($v){return is_numeric($v);})) !== count($array));
    }
<?php

function is_list($array) {
    return array_keys($array) === range(0, count($array) - 1);
}

function is_assoc($array) {
    return count(array_filter(array_keys($array), 'is_string')) == count($array);
}

?>

这两个得分最高的例子都不能正确地使用$array = array('foo' => 'bar', 1)这样的数组

我认为标量数组的定义因应用程序而异。也就是说,一些应用程序将需要更严格的定义标量数组,而一些应用程序将需要更宽松的定义。

下面我将介绍3种不同严格程度的方法。

<?php
/**
 * Since PHP stores all arrays as associative internally, there is no proper
 * definition of a scalar array.
 * 
 * As such, developers are likely to have varying definitions of scalar array,
 * based on their application needs.
 * 
 * In this file, I present 3 increasingly strict methods of determining if an
 * array is scalar.
 * 
 * @author David Farrell <DavidPFarrell@gmail.com>
 */

/**
 * isArrayWithOnlyIntKeys defines a scalar array as containing
 * only integer keys.
 * 
 * If you are explicitly setting integer keys on an array, you
 * may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    foreach ($a as $k => $v)
        if (!is_int($k))
            return false;
    return true;
}

/**
 * isArrayWithOnlyAscendingIntKeys defines a scalar array as
 * containing only integer keys in ascending (but not necessarily
 * sequential) order.
 * 
 * If you are performing pushes, pops, and unsets on your array,
 * you may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyAscendingIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $prev = null;
    foreach ($a as $k => $v)
    {
        if (!is_int($k) || (null !== $prev && $k <= $prev))
            return false;
        $prev = $k;
    }
    return true;
}

/**
 * isArrayWithOnlyZeroBasedSequentialIntKeys defines a scalar array
 * as containing only integer keys in sequential, ascending order,
 * starting from 0.
 * 
 * If you are only performing operations on your array that are
 * guaranteed to either maintain consistent key values, or that
 * re-base the keys for consistency, then you can use this function.
 * 
 * @param array $a
 * @return boolean
 */
function isArrayWithOnlyZeroBasedSequentialIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $i = 0;
    foreach ($a as $k => $v)
        if ($i++ !== $k)
            return false;
    return true;
}