确定数组是否为关联(哈希)


Determine whether an array is associative (hash) or not

我希望能够将数组传递给函数,并根据它是"列表"样式数组还是"哈希"样式数组而使函数的行为有所不同。 例如:

myfunc(array("One", "Two", "Three")); // works
myfunc(array(1=>"One", 2=>"Two", 3=>"Three")); also works, but understands it's a hash

可能会输出类似以下内容:

One, Two, Three
1=One, 2=Two, 3=Three

IE:当函数"检测到"它被传递的是哈希而不是数组时,它会做一些不同的事情。你能看出我来自Perl背景,其中%hashes是与@arrays不同的引用吗?

我相信我的例子很重要,因为我们不能只测试键是否是数字,因为你很可能在哈希中使用数字键。

我特别希望避免使用myfunc(array(array(1=>"One"), array(2=>"Two"), array(3=>"Three")))的混乱结构

直接从kohana框架中拉出来。

public static function is_assoc(array $array)
{
    // Keys of the array
    $keys = array_keys($array);
    // If the array keys of the keys match the keys, then the array must
    // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
    return array_keys($keys) !== $keys;
}

该基准测试给出了 3 种方法。

下面是一个摘要,从最快到最慢排序。有关更多信息,请在此处阅读完整的基准测试。

1. 使用 array_values()

function($array) {
    return (array_values($array) !== $array);
}

2. 使用 array_keys()

function($array){
    $array = array_keys($array); return ($array !== array_keys($array));
}

3. 使用 array_filter()

function($array){
    return count(array_filter(array_keys($array), 'is_string')) > 0;
}

从技术上讲,PHP 将所有数组视为哈希,因此没有确切的方法可以做到这一点。 我相信你最好的选择是以下几点:

if (array_keys($array) === range(0, count($array) - 1)) {
   //it is a hash
}

不,PHP 不会区分键是数字字符串的数组和键是整数的数组,如下所示:

$a = array("0"=>'a', "1"=>'b', "2"=>'c');
$b = array(0=>'a', 1=>'b', 2=>'c');
var_dump(array_keys($a), array_keys($b));

它输出:

array(3) {
    [0]=> int(0) [1]=> int(1) [2]=> int(2)
}
array(3) {
    [0]=> int(0) [1]=> int(1) [2]=> int(2)
}

(以上格式为便于阅读)

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

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

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

有点沮丧,试图编写一个函数来处理所有组合,一个想法在我脑海中点击:解析json_encode结果。

当 json 字符串包含大括号时,它必须包含一个对象!

当然,看完这里的解决方案,我的有点好笑......无论如何,我想与社区分享它,只是为了展示从另一个潜在客户(更"视觉")解决问题的尝试。


function isAssociative(array $arr): bool
    {
        // consider empty, and [0, 1, 2, ...] sequential
        if(empty($arr) || array_is_list($arr)) {
            return false;
        }
        // first scenario:
        // [  1  => [*any*] ]
        // [ 'a' => [*any*] ]
        foreach ($arr as $key => $value) {
            if(is_array($value)) {
                return true;
            }
        }
         // second scenario: read the json string
        $jsonNest = json_encode($arr, JSON_THROW_ON_ERROR);
        return str_contains($jsonNest, '{'); // {} assoc, [] sequential
    }

笔记

php@8.1必需的,请查看 github 上的要点,其中包含此方法的单元测试 + Polyfills (php>=7.3)。

我也测试了Hussard发布的解决方案,A&B通过了所有测试,C无法识别:{"1":0,"2":1}

基准

这里的 json 解析比 B~200 毫秒,但仍然比解决方案 C1.7 秒

您如何看待这个版本?欢迎改进!