如何检查PHP数组是关联的还是顺序的?

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

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

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

还有这个:

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

除非PHP有内置功能,否则你将无法在小于O(n)的时间内完成它——枚举所有键并检查整数类型。事实上,你还想确保没有漏洞,所以你的算法可能如下所示:

for i in 0 to len(your_array):if not defined(your-array[i]):# this is not an array array, it's an associative 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");

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");
function is_associative($arr) {return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);}

你问了两个不太对等的问题:

  • 首先,如何确定一个数组是否只有数字键
  • 其次,如何确定一个数组是否有顺序个数字键,从0开始

考虑一下你实际上需要这些行为中的哪一个。(可能两者都符合你的目的。)

第一个问题(简单地检查所有键都是数字)是库罗船长回答得很好

对于第二个问题(检查数组是否为零索引和顺序),您可以使用以下函数:

function isAssoc(array $arr){if (array() === $arr) return false;return array_keys($arr) !== range(0, count($arr) - 1);}
var_dump(isAssoc(['a', 'b', 'c'])); // falsevar_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // falsevar_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // truevar_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true

如果您正在寻找非数字键(无论顺序如何),那么您可能需要尝试

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

还有另一种方法来做到这一点。

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_keysreturn (count(array_diff_key($array, $compareArray))) ? true : false;
}

这当然是一个更好的选择。

<?php$arr = array(1,2,3,4);$isIndexed = array_values($arr) === $arr;
function isAssoc($arr){$a = array_keys($arr);for($i = 0, $t = count($a); $i < $t; $i++){if($a[$i] != $i){return false;}}return true;}

我只是使用key()函数。观察:

<?phpvar_dump(key(array('hello'=>'world', 'hello'=>'world'))); //string(5) "hello"var_dump(key(array('world', 'world')));                  //int(0)var_dump(key(array("0" => 'a', "1" => 'b', "2" => 'c'))); //int(0) who makes string sequetial keys anyway?????>

因此,只需检查false,您就可以确定数组是否是关联的。

其实最有效的方法是:

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

这是有效的,因为它将键(对于顺序数组总是0,1,2等)与键的键(总是将是0,1,2等)进行比较。

Laravel使用这种方法

我比较数组的键和数组array_values()结果的键之间的差异,数组将始终是具有整数索引的数组。如果键相同,则它不是关联数组。

function isHash($array) {if (!is_array($array)) return false;$diff = array_diff_assoc($array, array_values($array));return (empty($diff)) ? false : true;}
function checkAssoc($array){return  ctype_digit( implode('', array_keys($array) ) );}

我使用了array_keys($obj) !== range(0, count($obj) - 1)array_values($arr) !== $arr(它们是彼此的对偶,尽管第二个比第一个便宜),但对于非常大的数组,两者都失败了。

这是因为array_keysarray_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'

最后,虽然这看起来不像本页上的许多“解决方案”那么“优雅”,但实际上它的效率要高得多。几乎所有关联数组都会立即被检测到。只有索引数组会被详尽检查,上面概述的方法不仅会详尽检查索引数组,还会复制它们。

此函数可以处理:

  • 具有索引中的孔的数组(例如1,2,4,5,8,10)
  • 带有“0x”键的数组:例如键“08”是关联的,而键“8”是顺序的。

这个想法很简单:如果其中一个键不是整数,则它是关联数组,否则它是顺序的。

function is_asso($a){foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}return FALSE;}

仅检查数组是否具有非整数键(而不是数组是顺序索引还是零索引):

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

如果至少有一个字符串键,$array将被视为关联数组。

这可能是解决办法吗?

  public static function isArrayAssociative(array $array) {reset($array);return !is_int(key($array));}

注意事项显然是数组光标已重置,但我想说该函数可能在遍历或使用数组之前就已使用。

修改最受欢迎的答案。
这需要更多的处理,但更准确。

<?php//$a is a subset of $bfunction 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'))); // falsevar_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // falsevar_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?>

这个问题中的许多评论者不了解数组在PHP中的工作原理。从数组留档

键可以是整数也可以是字符串。如果键是整数的标准表示,它将被解释为整数(即“8”将被解释为8,而“08”将被解释为“08”)。键中的浮点数被截断为整数。索引和关联数组类型在PHP中是相同的类型,它可以包含整数和字符串索引。

换句话说,没有数组键“8”这样的东西,因为它总是(默默地)转换为整数8。因此,尝试区分整数和数字字符串是不必要的。

如果你想要最有效的方法来检查数组中的非整数键,而不需要复制数组的一部分(如array_keys())或全部(如foreach):

function keyedNext( &$arr, &$k){$k = key($arr);return next($arr);}
for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))$onlyIntKeys = is_null($k);

这是有效的,因为key()在当前数组位置无效时返回NULL,并且NULL永远不会是有效的键(如果您尝试使用NULL作为数组键,它会被静默转换为“”)。

另一个变体尚未显示,因为它根本不接受数字键,但我非常喜欢Greg的一个:

 /* Returns true if $var associative array */function is_associative_array( $array ) {return is_array($array) && !is_numeric(implode('', array_keys($array)));}

以下是我使用的方法:

function is_associative ( $a ){return in_array(false, array_map('is_numeric', array_keys($a)));}
assert( true === is_associative(array(1, 2, 3, 4)) );
assert( false === is_associative(array('foo' => 'bar', 'bar' => 'baz')) );
assert( false === is_associative(array(1, 2, 3, 'foo' => 'bar')) );

请注意,这并不包括特殊情况,例如:

$a = array( 1, 2, 3, 4 );
unset($a[1]);
assert( true === is_associative($a) );

抱歉,帮不了你。对于大小合适的数组来说,它也有点性能,因为它不会进行不必要的复制。正是这些小事情让Python和Ruby写起来更好…: P

检测关联数组(哈希数组)的最佳功能

<?phpfunction is_assoc($arr) { return (array_values($arr) !== $arr); }?>

速度明智:

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

记忆方面:

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

简单和性能友好的解决方案,只检查第一个关键。

function isAssoc($arr = NULL){if ($arr && is_array($arr)){foreach ($arr as $key => $val){if (is_numeric($key)) { return true; }
break;}}
return false;}

我前几天又遇到这个问题,我想利用array_merge特殊属性:

如果输入数组具有相同的字符串键,则那个键会覆盖前一个键的后一个值。但是,如果数组包含数字键,则稍后的值不会覆盖原始值,但会附加。输入数组中带有数字键的值将使用结果数组中从零开始的递增键重新编号。为什么不使用:

function Is_Indexed_Arr($arr){$arr_copy = $arr;if((2*count($arr)) == count(array_merge($arr, $arr_copy))){return 1;}return 0;}

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

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

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

<?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)这样的数组

这也可以(demo):

function array_has_numeric_keys_only(array $array){try {SplFixedArray::fromArray($array, true);} catch (InvalidArgumentException $e) {return false;}return true;}

请注意,这个答案的要点是告诉你SplFixedArray的存在,而不是鼓励你对这些类型的测试使用异常。

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

关于我的特殊情况的详细信息如下

上面由@null(超文本传输协议://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语法页。

我的特殊情况

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

案件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",),);

问题是filter键是可变的。例如:

案件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>

…但是,如果我正在转换的数组类似于案件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 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));}

第一个函数检查每个键是否是整数值。第二个函数检查每个键是否是整数值,此外检查所有键是否从$base开始是顺序的,默认值为0,因此如果不需要指定另一个基值,可以省略。key($my_array)返回null,如果读取指针移动到数组的末尾,这就是结束for循环的原因,如果所有键都是整数,则for循环后的语句返回true。如果不是,循环过早结束,因为键是字符串类型,for循环后的语句将返回false。后面的函数还在每次比较后向$base添加一个,以便能够检查下一个键是否具有正确的值。严格的比较使其还可以检查键是否为整数类型。当省略$base时,或者如果您确保仅使用整数调用它,则可以省略for循环第一部分中的$base=(int)$base部分。但是由于我不能确定每个人,所以我把它留在了里面。无论如何,语句只执行一次。我认为这些是最有效的解决方案:

  • 内存明智:不复制数据或键范围。执行array_values或array_keys可能看起来更短(代码更少),但请记住,一旦您进行该调用,后台会发生什么。是的,与其他解决方案相比,有更多(可见)语句,但这并不重要,不是吗?
  • 时间明智:除了复制/提取数据和/或键也需要时间的事实之外,这个解决方案比做Foreach更有效。同样,Foreach对某些人来说可能看起来更有效,因为它的符号更短,但在后台,Foreach还调用重置、键和下一步来做它的循环。但除此之外,它还调用有效来检查结束条件,由于与整数检查的结合,这里避免了这一点。

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

任择议定书指出

PHP将所有数组视为关联数组

编写一个函数来检查数组是否为联想是不太明智的(IMHO)。所以首先:PHP数组中的键是什么?:

关键可以是整数字符串

这意味着有三种可能的情况:

  • 案例1.所有键都是数字/整数
  • 情况2.所有键都是字符串
  • 有些键是字符串,有些键是数字/整数

我们可以使用以下功能检查每个案例。

情况1:所有键都是数字/整数

说明对于空数组,此函数也返回true

//! Check whether the input is an array whose keys are all integers./*!\param[in] $InputArray          (array) Input array.\return                         (bool) \b true iff the input is an array whose keys are all integers.*/function IsArrayAllKeyInt($InputArray){if(!is_array($InputArray)){return false;}
if(count($InputArray) <= 0){return true;}
return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);}

案例2:所有键都是字符串

说明对于空数组,此函数也返回true

//! Check whether the input is an array whose keys are all strings./*!\param[in] $InputArray          (array) Input array.\return                         (bool) \b true iff the input is an array whose keys are all strings.*/function IsArrayAllKeyString($InputArray){if(!is_array($InputArray)){return false;}
if(count($InputArray) <= 0){return true;}
return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);}

有些键是字符串,有些键是数字/整数

说明对于空数组,此函数也返回true

//! Check whether the input is an array with at least one key being an integer and at least one key being a string./*!\param[in] $InputArray          (array) Input array.\return                         (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.*/function IsArraySomeKeyIntAndSomeKeyString($InputArray){if(!is_array($InputArray)){return false;}
if(count($InputArray) <= 0){return true;}
return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;}

其结果是:


现在,对于一个数组是我们都习惯的正品阵列,这意味着:

  • 它的键都是数字/整数
  • 它的键是顺序(即按步骤1增加)。
  • 钥匙从零开始

我们可以检查以下功能。

情况3a.键是数字/整数顺序零基

说明对于空数组,此函数也返回true

//! Check whether the input is an array whose keys are numeric, sequential, and zero-based./*!\param[in] $InputArray          (array) Input array.\return                         (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.*/function IsArrayKeyNumericSequentialZeroBased($InputArray){if(!is_array($InputArray)){return false;}
if(count($InputArray) <= 0){return true;}
return array_keys($InputArray) === range(0, count($InputArray) - 1);}

警告/陷阱(或者,关于PHP中数组键的更奇特的事实)

整数键

这些数组的键是整数

array(0 => "b");array(13 => "b");array(-13 => "b");          // Negative integers are also integers.array(0x1A => "b");         // Hexadecimal notation.

字符串键

这些数组的键是字符串

array("fish and chips" => "b");array("" => "b");                                   // An empty string is also a string.array("stackoverflow_email@example.com" => "b");    // Strings may contain non-alphanumeric characters.array("stack\t\"over\"\r\nflow's cool" => "b");     // Strings may contain special characters.array('$tα€k↔øv∈rflöw⛄' => "b");                    // Strings may contain all kinds of symbols.array("functіon" => "b");                           // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)array("ま말轉转ДŁ" => "b");                         // How about Japanese/Korean/Chinese/Russian/Polish?array("fi\x0sh" => "b");                            // Strings may contain null characters.array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b");   // Strings may even be binary!

看起来像字符串的整数键

如果你认为array("13" => "b")中的键是字符串你错了。来自文档这里

包含有效整数的字符串将被转换为整数类型。例如。键“8”实际上将存储在8下。另一方面,“08”将不会被转换,因为它不是有效的十进制整数。

例如,这些数组的键是整数

array("13" => "b");array("-13" => "b");                        // Negative, ok.

但是这些数组的键是字符串

array("13." => "b");array("+13" => "b");                        // Positive, not ok.array("-013" => "b");array("0x1A" => "b");                       // Not converted to integers even though it's a valid hexadecimal number.array("013" => "b");                        // Not converted to integers even though it's a valid octal number.array("18446744073709551616" => "b");       // Not converted to integers as it can't fit into a 64-bit integer.

更重要的是,根据doc

整数的大小取决于平台,但通常最大值约为20亿(即32位有符号)。64位平台的最大值通常约为9E18,Windows除外,它始终为32位。PHP不支持无符号整数。

因此,这个数组可能会或可能不会的键是整数-它取决于您的平台。

array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.

更糟糕的是,如果整数接近231=2,147,483,648边界(参见bug51430bug52899),PHP往往是越野车。例如,在我的本地环境(Windows 7上XAMPP 1.7.7上的PHP 5.3.8)中,var_dump(array("2147483647" => "b"))给出了

array(1) {[2147483647]=>string(1) "b"}

但是在这个coDepad上的现场演示(PHP 5.2.5)上,相同的表达式给出

array(1) {["2147483647"]=>string(1) "b"}

因此,密钥在一个环境中是整数,但在另一个环境中是字符串,即使2147483647是有效的有符号32位整数

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

下面我介绍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;}

我注意到这个问题有两种流行的方法:一种使用array_values(),另一种使用key()。为了找出哪个更快,我写了一个小程序:

$arrays = Array('Array #1' => Array(1, 2, 3, 54, 23, 212, 123, 1, 1),'Array #2' => Array("Stack", 1.5, 20, Array(3.4)),'Array #3' => Array(1 => 4, 2 => 2),'Array #4' => Array(3.0, "2", 3000, "Stack", 5 => "4"),'Array #5' => Array("3" => 4, "2" => 2),'Array #6' => Array("0" => "One", 1.0 => "Two", 2 => "Three"),'Array #7' => Array(3 => "asdf", 4 => "asdf"),'Array #8' => Array("apple" => 1, "orange" => 2),);
function is_indexed_array_1(Array &$arr) {return $arr === array_values($arr);}
function is_indexed_array_2(Array &$arr) {for (reset($arr), $i = 0; key($arr) === $i++; next($arr));return is_null(key($arr));}
// Method #1$start = microtime(true);for ($i = 0; $i < 1000; $i++) {foreach ($arrays as $array) {$dummy = is_indexed_array_1($array);}}$end = microtime(true);echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";
// Method #2$start = microtime(true);for ($i = 0; $i < 1000; $i++) {foreach ($arrays as $array) {$dummy = is_indexed_array_2($array);}}$end = microtime(true);echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

CentOS上PHP 5.2程序的输出如下:

使用方法#1=10.745ms
使用方法#2的时间=18.239ms

PHP 5.3上的输出产生了类似的结果。显然使用array_values()要快得多。

我知道向这个巨大的队列添加答案有点毫无意义,但这里有一个可读的O(n)解决方案,不需要复制任何值:

function isNumericArray($array) {$count = count($array);for ($i = 0; $i < $count; $i++) {if (!isset($array[$i])) {return FALSE;}}return TRUE;}

不是检查键以查看它们是否都是数字,而是遍历存在于数字数组中的键并确保它们存在。

function is_array_assoc($foo) {if (is_array($foo)) {return (count(array_filter(array_keys($foo), 'is_string')) > 0);}return false;}

我的解决方案:

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

单个数组上的array_merge将重新索引所有integer键,而不是其他键。例如:

array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);
// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']

因此,如果创建了一个列表(一个非关联数组)['a', 'b', 'c'],然后删除一个值unset($a[1]),然后调用array_merge,该列表从0开始重新索引。

来源开始再快一次。适合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__));}}

使用x阵列 PHP扩展

您可以非常快速地完成此操作(在PHP 5.6中大约快30倍):

if (array_is_indexed($array)) {  }

或:

if (array_is_assoc($array)) {  }

一种方法是搭载json_encode,它已经有自己的内部方法来区分关联数组和索引数组,以便输出正确的JSON。

您可以通过检查编码后返回的第一个字符是{(关联数组)还是[(索引数组)来做到这一点。

// Too short :)function is_assoc($arr) {ksort($arr);return json_encode($arr)[0] === '{';}
function array_is_assoc(array $a) {$i = 0;foreach ($a as $k => $v) {if ($k !== $i++) {return true;}}return false;}

快速、简洁、内存高效。无需昂贵的比较、函数调用或数组复制。

答案已经给出,但关于性能的虚假信息太多了。我写了这个小基准脚本,它表明Foreach方法是最快的。

免责声明:以下方法是从其他答案复制粘贴的

<?php
function method_1(Array &$arr) {return $arr === array_values($arr);}
function method_2(Array &$arr) {for (reset($arr), $i = 0; key($arr) !== $i++; next($arr));return is_null(key($arr));}
function method_3(Array &$arr) {return array_keys($arr) === range(0, count($arr) - 1);}
function method_4(Array &$arr) {$idx = 0;foreach( $arr as $key => $val ){if( $key !== $idx )return FALSE;$idx++;}return TRUE;}



function benchmark(Array $methods, Array &$target){foreach($methods as $method){$start = microtime(true);for ($i = 0; $i < 1000; $i++)$dummy = call_user_func($method, $target);
$end = microtime(true);echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";}}


$targets = ['Huge array' => range(0, 30000),'Small array' => range(0, 1000),];$methods = ['method_1','method_2','method_3','method_4',];foreach($targets as $targetName => $target){echo "==== Benchmark using $targetName ====\n";benchmark($methods, $target);echo "\n";}

结果:

==== Benchmark using Huge array ====Time taken with method_1 = 5504.632msTime taken with method_2 = 4509.445msTime taken with method_3 = 8614.883msTime taken with method_4 = 2720.934ms
==== Benchmark using Small array ====Time taken with method_1 = 77.159msTime taken with method_2 = 130.03msTime taken with method_3 = 160.866msTime taken with method_4 = 69.946ms

马克·阿梅里的改进

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

这将测试变量是否存在,它是否是一个数组,它是否不是一个空数组,以及键是否不是从0开始的顺序。

查看数组是否是关联的

if (isAssoc($array)) ...

看看是不是数字

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

用简单的方法,你可以通过下面的步骤检查数组是否是关联的

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

上述步骤的功能如下。

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

已经有很多答案,但这是Laravel在其Arr类中依赖的方法:

/*** Determines if an array is associative.** An array is "associative" if it doesn't have sequential numerical keys beginning with zero.** @param  array  $array* @return bool*/public static function isAssoc(array $array){$keys = array_keys($array);
return array_keys($keys) !== $keys;}

来源:https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php

检查数组是否具有所有assoc键。使用stdClassget_object_vars^):

$assocArray = array('fruit1' => 'apple','fruit2' => 'orange','veg1' => 'tomato','veg2' => 'carrot');
$assoc_object = (object) $assocArray;$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));var_dump($isAssoc); // true

为什么?函数get_object_vars只返回无障碍个属性(请参阅更多关于将array转换为object这里期间发生的事情)。然后,从逻辑上讲:如果基本数组的元素数等于对象可访问属性计数-所有键都是assoc。

很少测试

$assocArray = array('apple', 'orange', 'tomato', 'carrot');$assoc_object = (object) $assocArray;$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));var_dump($isAssoc); // false//...
$assocArray = array( 0 => 'apple', 'orange', 'tomato', '4' => 'carrot');$assoc_object = (object) $assocArray;$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));var_dump($isAssoc); // false
//...$assocArray = array('fruit1' => 'apple',NULL => 'orange','veg1' => 'tomato','veg2' => 'carrot');
$assoc_object = (object) $assocArray;$isAssoc = (count($assocArray) === count (get_object_vars($assoc_object)));var_dump($isAssoc); //false

等等。

经过一些本地基准测试、调试、编译器探测、分析和滥用3v4l.org在更多版本之间进行基准测试(是的,我收到了停止的警告)

我能找到的每一个变化…

我给你一个有机派生的最佳-平均-最差情况关联数组测试函数,它的最糟糕大致与所有其他平均情况下的情况一样好或更好。

/*** Tests if an array is an associative array.** @param array $array An array to test.* @return boolean True if the array is associative, otherwise false.*/function is_assoc(array &$arr) {// don't try to check non-arrays or empty arraysif (FALSE === is_array($arr) || 0 === ($l = count($arr))) {return false;}
// shortcut by guessing at the beginningreset($arr);if (key($arr) !== 0) {return true;}
// shortcut by guessing at the endend($arr);if (key($arr) !== $l-1) {return true;}
// rely on php to optimize test by reference or fast comparereturn array_values($arr) !== $arr;}

https://3v4l.org/rkieX

<?php
// array_valuesfunction method_1(Array &$arr) {return $arr === array_values($arr);}
// method_2 was DQ; did not actually work
// array_keysfunction method_3(Array &$arr) {return array_keys($arr) === range(0, count($arr) - 1);}
// foreachfunction method_4(Array &$arr) {$idx = 0;foreach( $arr as $key => $val ){if( $key !== $idx )return FALSE;++$idx;}return TRUE;}
// guessingfunction method_5(Array &$arr) {global $METHOD_5_KEY;$i = 0;$l = count($arr)-1;
end($arr);if ( key($arr) !== $l )return FALSE;
reset($arr);do {if ( $i !== key($arr) )return FALSE;++$i;next($arr);} while ($i < $l);return TRUE;}
// naievefunction method_6(Array &$arr) {$i = 0;$l = count($arr);do {if ( NULL === @$arr[$i] )return FALSE;++$i;} while ($i < $l);return TRUE;}
// deep reference reliancefunction method_7(Array &$arr) {return array_keys(array_values($arr)) === array_keys($arr);}

// organic (guessing + array_values)function method_8(Array &$arr) {reset($arr);if ( key($arr) !== 0 )return FALSE;
end($arr);if ( key($arr) !== count($arr)-1 )return FALSE;
return array_values($arr) === $arr;}
function benchmark(Array &$methods, Array &$target, $expected){foreach($methods as $method){$start = microtime(true);for ($i = 0; $i < 2000; ++$i) {//$dummy = call_user_func($method, $target);if ( $method($target) !== $expected ) {echo "Method $method is disqualified for returning an incorrect result.\n";unset($methods[array_search($method,$methods,true)]);$i = 0;break;}}if ( $i != 0 ) {$end = microtime(true);echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";}}}


$true_targets = ['Giant array' => range(0, 500),'Tiny array' => range(0, 20),];

$g = range(0,10);unset($g[0]);
$false_targets = ['Large array 1' => range(0, 100) + ['a'=>'a'] + range(101, 200),'Large array 2' => ['a'=>'a'] + range(0, 200),'Tiny array' => range(0, 10) + ['a'=>'a'] + range(11, 20),'Gotcha array' => $g,];
$methods = ['method_1','method_3','method_4','method_5','method_6','method_7','method_8'];

foreach($false_targets as $targetName => $target){echo "==== Benchmark using $targetName expecing FALSE ====\n";benchmark($methods, $target, false);echo "\n";}foreach($true_targets as $targetName => $target){echo "==== Benchmark using $targetName expecting TRUE ====\n";benchmark($methods, $target, true);echo "\n";}

这是我的功能——

public function is_assoc_array($array){
if(is_array($array) !== true){return false;}else{
$check = json_decode(json_encode($array));
if(is_object($check) === true){return true;}else{return false;}
}
}

一些例子

    print_r((is_assoc_array(['one','two','three']))===true?'Yes':'No'); \\Noprint_r(is_assoc_array(['one'=>'one','two'=>'two','three'=>'three'])?'Yes':'No'); \\Yesprint_r(is_assoc_array(['1'=>'one','2'=>'two','3'=>'three'])?'Yes':'No'); \\Yesprint_r(is_assoc_array(['0'=>'one','1'=>'two','2'=>'three'])?'Yes':'No'); \\No

有一个类似的解决方案,在其中一个答案中,但这只是使用PHP的内置json相关函数的另一种方式。我还没有检查这个解决方案与这里发布的其他解决方案相比,在性能方面是如何公平的。但它确实帮助我解决了这个问题。希望这有帮助。

或者你可以只使用这个:

Arr::isAssoc($array)

这将检查数组是否包含任何非数字键或:

Arr:isAssoc($array, true)

检查数组是严格的顺序(包含自动生成的int键n-1

使用这个库。

我想到了下一个方法:

function isSequential(array $list): bool{$i = 0;$count = count($list);while (array_key_exists($i, $list)) {$i += 1;if ($i === $count) {return true;}}
return false;}

var_dump(isSequential(array())); // falsevar_dump(isSequential(array('a', 'b', 'c'))); // truevar_dump(isSequential(array("0" => 'a', "1" => 'b', "2" => 'c'))); // truevar_dump(isSequential(array("1" => 'a', "0" => 'b', "2" => 'c'))); // truevar_dump(isSequential(array("1a" => 'a', "0b" => 'b', "2c" => 'c'))); // falsevar_dump(isSequential(array("a" => 'a', "b" => 'b', "c" => 'c'))); // false

*注意空数组不被认为是顺序数组,但我认为这很好,因为空数组就像0-不管它是正数还是负数,它都是空的。

以下是此方法与上面列出的一些方法相比的优点:

  • 它不涉及数组的复制(有人在这个要点https://gist.github.com/Thinkscape/1965669中提到array_values不涉及复制-什么!??它当然是这样——正如下面将要看到的
  • 对于更大的数组,速度更快,同时对内存更友好

我使用了arturbodera提供的基准测试,我将其中一个数组更改为1M元素(array_fill(0, 1000000, uniqid()), // big numeric array)。

以下是100次迭代的结果:

PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Initial memory: 32.42 MBTesting my_method (isset check) - 100 iterationsTotal time: 2.57942 sTotal memory: 32.48 MB
Testing method3 (array_filter of keys) - 100 iterationsTotal time: 5.10964 sTotal memory: 64.42 MB
Testing method1 (array_values check) - 100 iterationsTotal time: 3.07591 sTotal memory: 64.42 MB
Testing method2 (array_keys comparison) - 100 iterationsTotal time: 5.62937 sTotal memory: 96.43 MB

*方法根据其内存消耗进行排序

**我使用echo " Total memory: " . number_format(memory_get_peak_usage()/1024/1024, 2) . " MB\n";来显示内存使用情况

/*iszba - Is Zero Based Array
Detects if an array is zero based or not.
PARAMS:$chkvfncCallback in the loop allows to check the values of each element.Signature:bool function chkvfnc($v);return:true    continue loopingfalse   stop looping; iszba returns false too.
NOTES:○ assert: $array is an array.○ May be memory efficient;it doesn't get extra arrays via array_keys() or ranges() into the function.○ Is pretty fast without a callback.○ With callback it's ~2.4 times slower.*/function iszba($array, $chkvfnc=null){
$ncb = !$chkvfnc;$i = 0;
foreach($array as $k => $v){if($k === $i++)if($ncb || $chkvfnc($v))continue;
return false;}
return true;}

•没有回调,它比当前领先答案快约30%,可能更高效的内存。

•只需否定答案即可知道数组是否应被视为关联。

这里的许多解决方案都很优雅和漂亮,但不能很好地扩展,并且是内存密集型或CPU密集型的。大多数人从比较的两边用这个解决方案在内存中创建2个新数据点。数组越大,进程和内存使用的难度和时间越长,你失去了短路评估的好处。我用一些不同的想法做了一些测试。试图避免array_key_exists,因为它是昂贵的,也避免创建新的大型数据集进行比较。我觉得这是一个判断数组是否是顺序的简单方法。

public function is_sequential( $arr = [] ){if( !is_array( $arr ) || empty( $arr ) ) return false;
$i = 0;
$total = count( $arr );
foreach( $arr as $key => $value ) if( $key !== $i++ ) return false;
return true;}

你在主数组上运行一个计数并存储一个整数。然后在数组中循环并在迭代计数器时检查是否完全匹配。你应该从1开始计数。如果它失败了,它会短路,这会在它为假时提高你的性能。

最初我使用for循环并检查isset($arr[$i]),但这不会检测到需要array_key_exists的空键,并且我们知道这是用于速度的最差函数。

不断更新变量,通过foreach来检查迭代器,永远不会超过它的整数大小,让PHP使用它内置的内存优化,缓存和垃圾回收机制来保持非常低的资源使用率。

另外,我认为,当你可以简单地运行$key=>$value并检查键时,在foreach中使用array_keys是愚蠢的。为什么要创建新的数据点?一旦你抽象掉数组键,你就会立即消耗更多的内存。

这个问题实际上是无用的,当涉及到PHP的数组,因为与PHP的性质,数组不应该是完全关联或索引,它可以是两者的组合,用户定义和分配值的方式数组可以是两者的组合

$y= array(5);$y["0x"]="n";$y["vbg"]="12132";$y[1] = "k";
var_dump($y); //this will output 4 element array
echo "</br>" .$y["0x"]."</br>".$y[0];
for($x=0;$x<sizeof($y);$x++){ // this will output all index elements & gives error after thatecho "</br> index elements ".$y[$x];}

所以正确的问题是,必须问的是,是所有的元素在数组是关联或索引.如果你真的知道它只会是关联或索引不是这两个的组合,你可以只是简单地使用这个方法来找到它是一个索引或关联数组。

function AssocTest(&$arr){if(is_array($arr)){
reset($arr); // reset pointer to first element of array
if(gettype(key($arr)) == "string"){ //get the type(nature) of first element keyreturn true;}else{return false;}}else{return false;}}

您可以将其用作正常功能

echo(AssocTest($y)?  "Associative array": "Not an Associative array/ Not an array at all");

重要的是要记住,你已经初始化一个数组为关联,但你给关联数组的名字只是数字,如果你没有明确给出字符串名称,它将被php读取时视为索引数组。

$y["0"]="n";$y["1"]="12132";$y["22"] = "k";
//both will get the same outputecho "<br/> s0 ".$y["22"];echo "<br/> s0 ".$y[22];
for($x=0;$x<count($y);$x++){echo "<br/> arr ".$y[$x]; // this will output up to 2nd element and give an error after
}

因此,如果您需要确保数组的所有元素都被精确索引或关联,那么没有其他方法,只能通过生成索引数组来检查每个元素键,因为这里有很多人都在发布。

function fullAssocTest(&$arr){if(is_array($arr)){return (array_keys($arr) !== range(0, count($arr) - 1));}}

它的编码较少,但这件事确实是过程密集型的,而且真的是不必要的工作。

有时你可以只检查第一个数组的Key是否为0。

$isSequential = array_keys($arr)[0] === 0

,或者,更快,但更冗长的版本:

reset($arr); $isSequential = key($arr) === 0

大多数答案都有次优的时间/空间复杂度或正在改变语义学。所以,这是另一个答案,带有最快功能最正确的解决方案:

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

这个答案比其他答案有以下优点:

  1. O(1)的空间复杂度(这里的许多答案使用O(n)空间!)
  2. 不对键应用相等(这是一个不必要且昂贵的操作)
  3. 将输入数组视为不可变(许多答案通过应用可变函数隐式创建了副本)
  4. 使用函数array_key_exists而不是isset(记住,isset还检查'is not null',从而改变语义学)
  5. 最坏情况时间复杂度为O(n)(这里的许多答案的最佳情况时间复杂度为O(n)

我很惊讶没有人提到array_key_first()

对于您的测试用例:

$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');$isIndexedArray = is_int( array_key_first($sequentialArray) ); // true

$assocArray = array('fruit1' => 'apple','fruit2' => 'orange','veg1' => 'tomato','veg2' => 'carrot');
$isIndexedArray = is_int( array_key_first($assocArray) ); // false

阅读有关此函数这里的更多信息。

这是另一个简单但强大的逻辑(伟大的Laravel框架也在其内部机制中使用)

/*** Determines if an array is associative.* @param  array  $array* @return bool*/function isAssoc(array $array){$keys = array_keys($array);
return array_keys($keys) !== $keys;}

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

function is_assoc(array $array): bool{foreach ($array as $iValue) {if (is_array($iValue)) {return  true;}}return false;
}
$d= is_assoc(["id",2,3]);
var_dump($d);

对于使用Laravel的人:

如果给定的数组是关联数组,则Arr::isAssoc返回true。如果数组没有以零开头的顺序数字键,则将其视为“关联”:

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

只是加上我的两分钱,我认为它应该是非常有效的基于@nonsensei的基准以及清晰易读:

function is_associative(array $array): bool{foreach ($array as $key => $value){if (!is_string($key)) return false;}return true;}