检查数组是否为多维数组?

165
  1. 如何高效地检测一个数组是由基本类型值组成的一维数组还是多维数组
  2. 是否有办法在不对数组进行循环并在每个元素上运行is_array()的情况下完成此操作?

10
值得指出的是,PHP没有真正的多维数组——只有简单的关联值数组。因此,你的问题实际上是在问“我的数组中是否有非标量值”? - gahooa
26
实际上,我认为根本不值得指出那个问题。 - Joe
16个回答

232

使用count()函数两次,一次在默认模式下,一次在递归模式下。如果两个值相等,则该数组不是多维数组,因为多维数组的递归计数会更高。

if (count($array) == count($array, COUNT_RECURSIVE)) 
{
  echo 'array is not multidimensional';
}
else
{
  echo 'array is multidimensional';
}

这个选项的第二个值 mode 在PHP 4.2.0中添加。来自PHP文档

如果可选模式参数设置为COUNT_RECURSIVE(或1),则count()将递归计算数组。这对于计算多维数组的所有元素特别有用。 count()无法检测到无限递归。

然而,此方法无法检测到 array(array())


5
如注所述,这对于具有空数组的元素无效。 - Arthur
这个答案显然是错误的,帖子的得分表明许多研究人员被误导了。https://3v4l.org/ptGQ9 - mickmackusa

152
简短的回答是,如果“第二维度”可以出现在任何地方,那么至少需要隐式循环,否则无法实现。如果它必须出现在第一项中,你只需执行以下操作:
is_array($arr[0]);

然而,我找到的最有效的一般方法是在数组上使用foreach循环,在找到匹配项时进行短路(至少隐式循环比直接使用for()更好):

$ more multi.php
<?php

$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
$c = array(1 => 'a',2 => 'b','foo' => array(1,array(2)));

function is_multi($a) {
    $rv = array_filter($a,'is_array');
    if(count($rv)>0) return true;
    return false;
}

function is_multi2($a) {
    foreach ($a as $v) {
        if (is_array($v)) return true;
    }
    return false;
}

function is_multi3($a) {
    $c = count($a);
    for ($i=0;$i<$c;$i++) {
        if (is_array($a[$i])) return true;
    }
    return false;
}
$iters = 500000;
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
    is_multi($a);
    is_multi($b);
    is_multi($c);
}
$end = microtime(true);
echo "is_multi  took ".($end-$time)." seconds in $iters times\n";

$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
    is_multi2($a);
    is_multi2($b);
    is_multi2($c);
}
$end = microtime(true);
echo "is_multi2 took ".($end-$time)." seconds in $iters times\n";
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
    is_multi3($a);
    is_multi3($b);
    is_multi3($c);
}
$end = microtime(true);
echo "is_multi3 took ".($end-$time)." seconds in $iters times\n";
?>

$ php multi.php
is_multi  took 7.53565130424 seconds in 500000 times
is_multi2 took 4.56964588165 seconds in 500000 times
is_multi3 took 9.01706600189 seconds in 500000 times

隐式循环,但是我们不能在找到匹配项后立即进行短路处理...

$ more multi.php
<?php

$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');

function is_multi($a) {
    $rv = array_filter($a,'is_array');
    if(count($rv)>0) return true;
    return false;
}

var_dump(is_multi($a));
var_dump(is_multi($b));
?>

$ php multi.php
bool(true)
bool(false)

4
好的,但我认为您的过滤器应该使用array_map("is_array",$a),而不是裸用is_array。 - Matthew Scharley
很好,那个 sped up is_multi 的方法不错,但仍然不足以匹配 foreach。 - Vinko Vrsalovic
3
值得注意的是,如所述,multi_3 仅适用于以零为基础的、无间隔索引的非关联数组,在这种情况下,它将无法正确识别任何这些示例作为多维数组。 - CragMonkey
在函数 is_multi() 中,通过执行 return count($rv)>0 来优化代码。 - Xorifelse
使用is_array(array_values($arr)[0])作为自定义键的解决方法。 - Vitor Rodrigues

29

对于 PHP 4.2.0 或更新版本:

function is_multi($array) {
    return (count($array) != count($array, 1));
}

对于 array(array())array(array(), array()) 也不起作用。通常,如果内部数组为空,则递归计数将正确地为其添加0,从而使其与正常计数匹配。 - Fanis Hatzidakis
这个答案的可靠性无法证实。https://3v4l.org/LLg2t - mickmackusa

12

我认为这是最直接的方式,而且它还是最先进的:

function is_multidimensional(array $array) {
    return count($array) !== count($array, COUNT_RECURSIVE);
}

这种不可靠的技术在多年前的此页面上已经被多个答案推荐过了。 这个答案没有为页面增加任何新价值。 - mickmackusa

9

在 PHP 7 之后,您可以简单地执行以下操作:

public function is_multi(array $array):bool
{
    return is_array($array[array_key_first($array)]);
}

1
你只检查第一个数组元素是否为数组。如果它不是,但第二个元素是数组呢?例如 $array = ['0' => 0, '1' => ['0' => 1]]; 这是一个多维数组,但你的函数返回 false。 - aProgger

7
您可以查看第一个元素并使用is_array()函数进行检查,假设如果数组的第一个元素是一个数组,那么其余的元素也是数组。

这实际上是一个很好的观点。在我的特定情况下,这是一个二选一的情况,因为我控制着原始数组的创建。不过,如果有更普遍适用的解决方案,我会暂时保留这个问题。 - Wilco
5
像这样:if( is_array(current($arr)) ) { // 是多维数组 } - Jonas Äppelgran
只有当数组的第一级具有一致的数据类型时,这才是一个有效的选项。 - mickmackusa

4
我认为您会发现这个函数是最简单、最高效和最快的方法。
function isMultiArray($a){
    foreach($a as $v) if(is_array($v)) return TRUE;
    return FALSE;
}

您可以这样测试它:
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');

echo isMultiArray($a) ? 'is multi':'is not multi';
echo '<br />';
echo isMultiArray($b) ? 'is multi':'is not multi';

如果你要将它压成一行,请确保整个代码都在同一行上;foreach($a as $v) is_array($v) ? return TRUE : return FALSE; - Robert Pounder
@RobertPounder 或者 foreach($a as $v) return is_array($v) ? true : false; - Yassine Sedrani
1
嗯,不行,RobertPounder和YassineSedrani。你不能像那样一行代码就解决问题;那会产生不同的逻辑。在第一次迭代中返回结果并不能检查整个数组。如果你要无条件地提前返回结果,那么最好直接在第一个元素上进行评估。但是,为了得到完全可靠的解决方案,你需要允许迭代整个数组以确定相反的数据类型。 - mickmackusa

3

不要使用COUNT_RECURSIVE

点击此链接了解原因

使用rsort,然后使用isset

function is_multi_array( $arr ) {
rsort( $arr );
return isset( $arr[0] ) && is_array( $arr[0] );
}
//Usage
var_dump( is_multi_array( $some_array ) );

$arr[0] 可能不是一个数组,但 $arr[1] 可能是一个数组。 - Supun Praneeth
1
这个答案的时间复杂度比必要的要高,因为排序比仅迭代数组中的每个元素更昂贵。出于任何原因,我都不建议使用这个答案。 - mickmackusa

2
即使这样也可以工作。
is_array(current($array));

如果false,它是一个单维数组,如果true,它是一个多维数组current将给出数组的第一个元素,并通过is_array函数检查第一个元素是否为数组。

2
如果您想确保任何其他元素也没有嵌套,那么这样做将不可靠。 - vanamerongen

0

我认为这个很棒(向另一个用户致敬,我不知道他的用户名):

static public function isMulti($array)
{
    $result = array_unique(array_map("gettype",$array));

    return count($result) == 1 && array_shift($result) == "array";
}

这不会是一个高性能的解决方案--array_map()不允许短路,因此将遍历整个数组。此页面上有其他比这更好的解决方案。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接