How to check if PHP array is associative or sequential?
PHP将所有数组视为关联数组,因此没有任何内置函数。有人能推荐一种相当有效的方法来检查数组是否只包含数字键吗?
基本上,我希望能够区分:
1 |
还有这个:
1 2 3 4 | $assocArray = array('fruit1' => 'apple', 'fruit2' => 'orange', 'veg1' => 'tomato', 'veg2' => 'carrot'); |
号
您提出了两个不完全相同的问题:
- 首先,如何确定数组是否只有数字键
- 其次,如何确定数组是否具有从0开始的顺序数字键
考虑一下你真正需要哪些行为。(可能两者都是为了你的目的。)
第一个问题(简单地检查所有的键是否都是数字的)由船长Kuro很好地回答。
对于第二个问题(检查数组是否为零索引和顺序),可以使用以下函数:
1 2 3 4 5 6 7 8 9 10 | function isAssoc(array $arr) { if (array() === $arr) return false; return array_keys($arr) !== range(0, count($arr) - 1); } var_dump(isAssoc(array('a', 'b', 'c'))); // false var_dump(isAssoc(array("0" => 'a',"1" => 'b',"2" => 'c'))); // false var_dump(isAssoc(array("1" => 'a',"0" => 'b',"2" => 'c'))); // true var_dump(isAssoc(array("a" => 'a',"b" => 'b',"c" => 'c'))); // true |
。
要仅检查数组是否具有非整数键(而不是数组是否按顺序索引或零索引):
1 2 3 | function has_string_keys(array $array) { return count(array_filter(array_keys($array), 'is_string')) > 0; } |
号
如果至少有一个字符串键,那么
当然,这是一个更好的选择。
1 2 3 |
这个问题中的许多注释者不理解数组如何在PHP中工作。从阵列文档中:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e."8" will be interpreted as 8, while"08" will be interpreted as"08"). Floats in key are truncated to integer. The indexed and associative array types are the same type in PHP, which can both contain integer and string indices.
号
换句话说,没有数组键"8"这样的东西,因为它总是(无声地)转换为整数8。因此,尝试区分整数和数字字符串是不必要的。
如果希望使用最有效的方法检查数组中的非整数键,而不复制数组的一部分(如array_keys()所做的)或全部(如foreach所做的):
1 2 3 4 5 6 7 |
这是因为键()在当前数组位置无效且空永远不能是有效键时返回空值(如果尝试将空值用作数组键,则会自动将其转换为")。
如操作说明:
PHP treats all arrays as associative
号
编写一个检查数组是否关联的函数是不太明智的。所以第一件事是:PHP数组中的键是什么?:
The key can either be an integer or a string.
号
这意味着有3种可能的情况:
- 案例1。所有键都是数字/整数。
- 案例2。所有键都是字符串。
- 案例3。有些键是字符串,有些键是数字/整数。
我们可以使用以下功能检查每个案例。
案例1:所有键都是数字/整数。注意:对于空数组,此函数也返回true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //! Check whether the input is an array whose keys are all integers. /*! \param[in] $InputArray (array) Input array. eturn (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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //! Check whether the input is an array whose keys are all strings. /*! \param[in] $InputArray (array) Input array. eturn (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); } |
号案例3。有些键是字符串,有些键是数字/整数。
注意:对于空数组,此函数也返回true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //! 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. eturn (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; } |
如下:
- 如果该值不是数组,则所有3个函数都返回false。
- 如果值为空数组,则所有3个函数都返回true(根据定义,如"空集是任何集合A的子集,因为它的所有元素都属于A")。
- 如果该值是非空数组,则正好1个函数返回true。
现在,对于一个数组来说,它是我们都习惯的"真正的"数组,意思是:
- 它的键都是数字/整数。
- 它的键是连续的(即按步骤1递增)。
- 它的键从零开始。
我们可以检查以下功能。
案例3a.键是数字/整数、顺序键和基于零的键。注意:对于空数组,此函数也返回true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //! Check whether the input is an array whose keys are numeric, sequential, and zero-based. /*! \param[in] $InputArray (array) Input array. eturn (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中数组键的更为特殊的事实)整数键
这些数组的键是整数:
1 2 3 4 |
字符串键
这些数组的键是字符串:
1 2 3 4 5 6 7 8 9 10 11 | array("fish and chips" =>"b"); array("" =>"b"); // An empty string is also a string. array("[email protected]" =>"b"); // Strings may contain non-alphanumeric characters. array("stack\t"over" flow'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! |
。看起来像字符串的整数键
如果您认为
Strings containing valid integers will be cast to the integer type. E.g. the key"8" will actually be stored under 8. On the other hand"08" will not be cast, as it isn't a valid decimal integer.
号
例如,这些数组的键是整数:
。
但这些数组的关键是字符串:
1 2 3 4 5 6 | 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. |
更重要的是,据医生说,
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers.
号
所以这个数组的键可能是整数,也可能不是整数,这取决于您的平台。
1 | array("60000000000" =>"b"); // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer. |
。
更糟糕的是,如果整数接近231=2147483648的边界,则php可能有问题(参见bug 51430、bug 52899)。例如,在我的本地环境(Windows7上的xampp 1.7.7上的php 5.3.8)中,
1 2 3 4 |
但是在这个代码板(php 5.2.5)上的实况演示中,同样的表达式给出了
1 2 3 4 |
。
因此,键在一个环境中是一个整数,而在另一个环境中是一个字符串,即使
速度方面:
1 2 3 4 |
。
记忆方面:
1 2 3 4 |
。
实际上,最有效的方法是:
1 2 3 4 |
号
这是因为它比较键(对于顺序数组,键总是0、1、2等)和键(键总是0、1、2等)的键。
1 2 3 |
号
我已经使用了
这是因为
以下函数比上面提供的方法更可靠:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
号
还要注意,如果您不想区分稀疏数组和关联数组,您只需从两个
最后,虽然这看起来比本页上的许多"解决方案"要"优雅"得多,但实际上它的效率要高得多。几乎所有的关联数组都会立即被检测到。只有索引数组才会被彻底检查,上面概述的方法不仅彻底检查索引数组,而且会复制它们。
我认为下面两个函数是检查"数组是关联的还是数值的"的最佳方法。由于"numeric"只能表示数字键或顺序数字键,因此下面列出了两个检查任一条件的函数:
1 2 3 4 5 6 7 8 9 |
第一个函数检查每个键是否为整数值。第二个函数检查每个键是否为整数值,另外检查所有键是否从$base开始连续,默认值为0,因此,如果不需要指定另一个基值,则可以省略。如果读取指针移过数组的结尾($my_array),则键($my_array)返回空值,这将结束for循环,如果所有键都是整数,则使for循环后面的语句返回真值。如果不是,则循环将提前结束,因为键的类型为string,并且for循环之后的语句将返回false。后一个函数在每次比较后再加上一个到$base,以便能够检查下一个键的值是否正确。通过严格的比较,还可以检查键是否为整数类型。当省略$base或确保只使用整数调用它时,可以省略for循环第一节中的$base=(int)$base部分。但因为我不能确定每个人,所以我把它放了进去。无论如何,语句只执行一次。我认为这些是最有效的解决方案:
- 内存方面:不复制数据或键范围。做一个数组值或数组键可能看起来更短(代码更少),但记住一旦你做了那个调用在后台会发生什么。是的,有比其他一些解决方案更多的(可见的)声明,但这不重要,是吗?
- 时间方面:除了复制/提取数据和/或键也需要时间之外,此解决方案比执行foreach更有效。同样,foreach对某些人来说似乎更有效,因为它的符号更短,但在后台foreach还调用reset、key和next来执行循环。但除此之外,它还调用valid来检查结束条件,这在这里由于与整数检查的组合而被避免。
请记住,数组键只能是整数或字符串,而严格数字字符串(如"1"(但不是"01")将被转换为整数。这使得检查整数键成为除计数之外唯一需要的操作,如果您希望数组是连续的。当然,如果被索引的数组返回false,则可以将该数组视为关联数组。我说"看见",因为事实上他们都是。
此功能可以处理:
- 索引中有孔的数组(例如1、2、4、5、8、10)
- 带有"0x"键的数组:例如键"08"是关联的,而键"8"是连续的。
这个想法很简单:如果其中一个键不是整数,那么它是关联数组,否则它是连续的。
1 2 3 4 | function is_asso($a){ foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;} return FALSE; } |
号
我注意到这个问题有两种流行的方法:一种使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | $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 "; // 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 "; |
号
CentOS上php 5.2上程序的输出如下:
Time taken with method #1 = 10.745ms
Time taken with method #2 = 18.239ms
号
php 5.3上的输出产生了类似的结果。显然,使用
一种方法是使用
您可以通过检查编码后返回的第一个字符是EDOCX1(关联数组)还是EDOCX1(索引数组)来完成此操作。
1 2 3 4 5 |
。
1 2 3 4 5 6 7 8 9 | function array_is_assoc(array $a) { $i = 0; foreach ($a as $k => $v) { if ($k !== $i++) { return true; } } return false; } |
快速、简洁和记忆效率高。没有昂贵的比较、函数调用或数组复制。
已经有很多答案,但下面是Laravel在其ARR类中所依赖的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 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/light/support/arr.php
通过使用xarray-php扩展
您可以非常快地完成此操作(在php 5.6中大约快30倍):
1 | if (array_is_indexed($array)) { } |
号
或:
1 | if (array_is_assoc($array)) { } |
我的解决方案:
1 2 3 4 | function isAssociative(array $array) { return array_keys(array_merge($array)) !== range(0, count($array) - 1); } |
号
单个数组上的
1 2 3 | array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']); // This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six'] |
号
因此,如果创建了一个列表(非关联数组)
我知道向这个庞大的队列添加一个答案有点毫无意义,但是这里有一个可读的O(N)解决方案,它不需要复制任何值:
1 2 3 4 5 6 7 8 9 |
号
您不必检查这些键是否都是数字的,而是迭代那些将出现在数字数组中的键,并确保它们存在。
1 2 3 4 5 6 7 8 9 10 11 | <?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); } ?> |
号
这两个得分最高的例子都不能正确地处理像
我认为标量数组的定义会因应用程序而异。也就是说,一些应用程序将需要更严格的定义什么是标量数组,而一些应用程序将需要更宽松的定义。
下面我介绍了3种不同严格性的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | <?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 <[email protected]> */ /** * 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; } |
号
在进行了一些本地基准测试、调试、编译器探测、分析和滥用3v4l.org在更多版本之间进行基准测试之后(是的,我收到了停止的警告),并且和我能找到的每一种变化作比较…
我给您一个有机派生的最佳平均最坏情况场景关联数组测试函数,它在最坏情况下大致与所有其他平均情况场景相同或更好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** * 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 arrays if (FALSE === is_array($arr) || 0 === ($l = count($arr))) { return false; } // shortcut by guessing at the beginning reset($arr); if (key($arr) !== 0) { return true; } // shortcut by guessing at the end end($arr); if (key($arr) !== $l-1) { return true; } // rely on php to optimize test by reference or fast compare return array_values($arr) !== $arr; } |
号
来自https://3v4l.org/rkiex:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <?php // array_values function method_1(Array &$arr) { return $arr === array_values($arr); } // method_2 was DQ; did not actually work // array_keys function method_3(Array &$arr) { return array_keys($arr) === range(0, count($arr) - 1); } // foreach function method_4(Array &$arr) { $idx = 0; foreach( $arr as $key => $val ){ if( $key !== $idx ) return FALSE; ++$idx; } return TRUE; } // guessing function 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; } // naieve function method_6(Array &$arr) { $i = 0; $l = count($arr); do { if ( NULL === @$arr[$i] ) return FALSE; ++$i; } while ($i < $l); return TRUE; } // deep reference reliance function 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. "; 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 "; } } } $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 ==== "; benchmark($methods, $target, false); echo" "; } foreach($true_targets as $targetName => $target){ echo"==== Benchmark using $targetName expecting TRUE ==== "; benchmark($methods, $target, true); echo" "; } |
号
这也可以工作(演示):
1 2 3 4 5 6 7 8 9 | function array_has_numeric_keys_only(array $array) { try { SplFixedArray::fromArray($array, true); } catch (InvalidArgumentException $e) { return false; } return true; } |
号
请注意,此答案的主要目的是告知您是否存在
这是解决办法吗?
1 2 3 4 |
号
警告很明显是数组光标被重置了,但我想说的是,可能在数组被遍历或使用之前就已经使用了函数。
我使用的方法如下:
1 2 3 4 5 6 7 8 9 10 |
请注意,这并不能解释以下特殊情况:
。
对不起,帮不了你。它对于大小合适的数组也有一定的性能,因为它不会制作不必要的副本。正是这些小东西使得python和ruby更适合写…P
已经给出了答案,但关于绩效的虚假信息太多了。我编写了这个小的基准脚本,它表明foreach方法是最快的。
免责声明:以下方法是从其他答案复制粘贴的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?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 "; } } $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 ==== "; benchmark($methods, $target); echo" "; } |
。
结果:
1 2 3 4 5 6 7 8 9 10 11 | ==== Benchmark using Huge array ==== Time taken with method_1 = 5504.632ms Time taken with method_2 = 4509.445ms Time taken with method_3 = 8614.883ms Time taken with method_4 = 2720.934ms ==== Benchmark using Small array ==== Time taken with method_1 = 77.159ms Time taken with method_2 = 130.03ms Time taken with method_3 = 160.866ms Time taken with method_4 = 69.946ms |
。
从源头再快一点。拟合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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__) ); } } |
我比较了数组的键和数组的array_values()结果的键之间的差异,后者始终是一个带整数索引的数组。如果键相同,则不是关联数组。
1 2 3 4 5 | function isHash($array) { if (!is_array($array)) return false; $diff = array_diff_assoc($array, array_values($array)); return (empty($diff)) ? false : true; } |
号
还有另一种方法。
1 2 3 4 5 6 7 8 9 | 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_keys return (count(array_diff_key($array, $compareArray))) ? true : false; } |
。
修改最流行的答案。这需要更多的处理,但更准确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?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 ?> |
号
1 2 3 | function is_associative($arr) { return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0); } |
。
我想出了下一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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())); // false var_dump(isSequential(array('a', 'b', 'c'))); // true var_dump(isSequential(array("0" => 'a',"1" => 'b',"2" => 'c'))); // true var_dump(isSequential(array("1" => 'a',"0" => 'b',"2" => 'c'))); // true var_dump(isSequential(array("1a" => 'a',"0b" => 'b',"2c" => 'c'))); // false var_dump(isSequential(array("a" => 'a',"b" => 'b',"c" => 'c'))); // false |
*注:空数组不被认为是一个连续数组,但我认为它是好的,因为空数组类似于0——不管它是正负,它都是空的。
与上面列出的一些方法相比,此方法的优点如下:
- 它不涉及阵列的复制(在这个gist https://gist.github.com/thinkscape/1965669中提到,
array_values 不涉及复制-什么!???当然可以-如下所示) - 更大的阵列速度更快,同时内存更友好
我使用了Artur Bodera提供的基准,在那里我将其中一个数组更改为1米元素(
以下是100次迭代的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS ) Initial memory: 32.42 MB Testing my_method (isset check) - 100 iterations Total time: 2.57942 s Total memory: 32.48 MB Testing method3 (array_filter of keys) - 100 iterations Total time: 5.10964 s Total memory: 64.42 MB Testing method1 (array_values check) - 100 iterations Total time: 3.07591 s Total memory: 64.42 MB Testing method2 (array_keys comparison) - 100 iterations Total time: 5.62937 s Total memory: 96.43 MB |
号
*方法是根据其内存消耗进行排序的
**我用
";
除非PHP有一个内置函数,否则您将无法在小于O(N)的情况下完成这项工作——枚举所有键并检查整数类型。实际上,您还希望确保没有孔,因此您的算法可能如下所示:
1 2 3 |
但是为什么要麻烦呢?假设数组是您期望的类型。如果不是的话,它会在你的脸上爆炸-这是动态编程为你!测试你的代码,一切都会好起来…
在我看来,如果数组的任何键不是整数,例如浮点数和空字符串"",则该数组应被接受为关联数组。
此外,非序列整数也必须被视为类似于(0,2,4,6)的关联整数,因为这些类型的数组不能通过这种方式与for循环一起使用:
1 2 |
号
下面函数的第二部分检查键是否被索引。它也适用于负值的键。例如(-1,0,1,2,3,4,5)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | count() = 7 , max = 5, min=-1 if( 7 == (5-(-1)+1 ) // true return false; // array not associative /** * isAssoc Checks if an array is associative * @param $arr reference to the array to be checked * @return bool */ function IsAssoc(&$arr){ $keys= array_keys($arr); foreach($keys as $key){ if (!is_integer($key)) return true; } // if all keys are integer then check if they are indexed if(count($arr) == (max($keys)-min($keys)+1)) return false; else return true; } |
号
这里的很多解决方案都很优雅和漂亮,但是扩展性不好,而且是内存密集型或CPU密集型的。大多数都是用这个解决方案在内存中创建两个新的数据点。阵列越大,进程和内存使用的越困难,越长,您就失去了短路评估的好处。我用一些不同的想法做了一些测试。试图避免数组键存在,因为它是昂贵的,也避免创建新的大型数据集进行比较。我觉得这是一个简单的方法来判断数组是否是连续的。
1 2 3 4 5 6 7 8 9 10 11 |
。
在主数组上运行单个计数并存储单个整数。然后循环遍历数组,并在迭代计数器时检查是否完全匹配。你应该从1开始计算。如果它失败了,它会短路,当它是错误的时候,它会提高你的性能。
最初,我用for循环和检查isset($arr[$i])来完成这项工作,但这不会检测到需要array_key_存在的空键,正如我们所知,这是用于速度的最差函数。
通过foreach不断更新变量以与迭代器一起检查,永远不会超过它的整数大小。让我们让PHP使用它内置的内存优化、缓存和垃圾收集来保持非常低的资源使用率。
另外,我认为在foreach中使用数组键是愚蠢的,因为您只需运行$key=>value并检查该键即可。为什么要创建新的数据点?一旦提取出数组键,就立即消耗了更多的内存。
或者你也可以用这个:
1 | Arr::isAssoc($array) |
号
它将检查数组是否包含任何非数字键或:
1 | Arr:isAssoc($array, true) |
号
检查数组是否严格按顺序排列(包含自动生成的int键0到n-1)
使用此库。
Mark Amery的改进
1 2 3 4 5 |
如果变量存在,如果它是一个数组,如果它不是一个空数组,如果键不是从0开始的序列,则此测试将进行。
查看数组是否关联
1 | if (isAssoc($array)) ... |
。
看看它是否是数字
1 | if (!isAssoc($array)) ... |
一种既便宜又脏的方法是这样检查:
号
…如果您的数组如下所示,则可能会得到误报:
1 |
更彻底的方法可能是检查钥匙:
1 2 3 4 5 6 7 8 9 10 11 | 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"); |
。
或
1 2 3 4 5 6 7 8 9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /* iszba - Is Zero Based Array Detects if an array is zero based or not. PARAMS: $chkvfnc Callback in the loop allows to check the values of each element. Signature: bool function chkvfnc($v); return: true continue looping false 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%。而且可能更节省内存。
?只需否定答案就可以知道数组是否应被视为关联数组。
这是我的职责-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } } } |
号
一些例子
1 2 3 4 5 6 | print_r((is_assoc_array(['one','two','three']))===true?'Yes':'No'); \ o print_r(is_assoc_array(['one'=>'one','two'=>'two','three'=>'three'])?'Yes':'No'); \\Yes print_r(is_assoc_array(['1'=>'one','2'=>'two','3'=>'three'])?'Yes':'No'); \\Yes print_r(is_assoc_array(['0'=>'one','1'=>'two','2'=>'three'])?'Yes':'No'); \ o |
号
@devios1在其中一个答案中也有类似的解决方案,但这只是使用PHP内置JSON相关函数的另一种方法。我还没有检查这个解决方案在性能方面如何与其他发布在这里的解决方案进行比较。但它确实帮助我解决了这个问题。希望这有帮助。
我的解决方案是获取如下数组的键,并检查该键是否为整数:
1 2 3 4 5 6 | private function is_hash($array) { foreach($array as $key => $value) { return ! is_int($key); } return false; } |
获取哈希数组的数组键是错误的,如下所示:
1 2 3 4 5 |
。
将输出:
1 2 3 4 |
号
所以,将它与一系列排名最高的答案中提到的数字进行比较不是一个好主意。如果您尝试将键与范围进行比较,它总是说它是一个哈希数组。
检查数组是否具有所有关联键。使用EDOCX1[8]和get撔u object vars^)时:
1 2 3 4 5 6 7 8 | $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 |
号
为什么?函数
少数测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $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 |
号
等。
1 2 3 4 5 6 | function is_array_assoc($foo) { if (is_array($foo)) { return (count(array_filter(array_keys($foo), 'is_string')) > 0); } return false; } |
号
实际上,我发现自己也处于类似的情况下,我试图获取一个数组并将其解析为XML。XML元素名称不能以数字开头——我发现的代码片段没有正确处理带有数字索引的数组。
Details on my particular situation are below
号
上面@null(http://stackoverflow.com/a/173589/293332)提供的答案实际上相当接近。我很沮丧,因为它被投票否决了:那些不了解雷鬼的人过着非常令人沮丧的生活。
不管怎样,根据他的回答,我得出的结论是:
1 2 3 4 5 6 7 8 | /** * Checks if an array is associative by utilizing REGEX against the keys * @param $arr Reference to the array to be checked * @return boolean */ private function isAssociativeArray( &$arr ) { return (bool)( preg_match( '/\D/', implode( array_keys( $arr ) ) ) ); } |
号
有关详细信息,请参阅PCRE转义序列和PCRE语法页。
我的特殊情况下面是我要处理的示例数组:
案例A1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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", ), ); |
号
关键是
1 2 3 4 5 6 7 8 9 10 11 |
号为什么我需要关联数组检查器
如果我要转换的数组类似于案例A,那么我想要返回的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?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,那么我想要返回的是:
1 2 3 4 5 6 7 8 9 10 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <GetInventorySummary> <Filters> <Filter> <foo>bar</foo> <bar>foo</bar> </Filter> </Filters> <SummaryField>Length</SummaryField> </GetInventorySummary> |
几天前我又遇到了这个问题,我想利用数组合并的特殊属性:
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended. Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.
So why not to use:
号
1 2 3 4 5 6 7 | function Is_Indexed_Arr($arr){ $arr_copy = $arr; if((2*count($arr)) == count(array_merge($arr, $arr_copy))){ return 1; } return 0; } |
。
检测关联数组的最佳函数(哈希数组)
1 2 3 |
1 2 3 4 5 6 7 8 9 10 11 12 | function isAssoc($arr) { $a = array_keys($arr); for($i = 0, $t = count($a); $i < $t; $i++) { if($a[$i] != $i) { return false; } } return true; } |
。
另一种变体还没有出现,因为它不接受数字键,但我非常喜欢Greg的变体:
1 2 3 4 | /* Returns true if $var associative array */ function is_associative_array( $array ) { return is_array($array) && !is_numeric(implode('', array_keys($array))); } |
号
简单且性能友好的解决方案,只检查第一个密钥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function isAssoc($arr = NULL) { if ($arr && is_array($arr)) { foreach ($arr as $key => $val) { if (is_numeric($key)) { return true; } break; } } return false; } |
简单来说,您可以通过以下步骤检查数组是否关联
上述步骤的功能如下。
1 2 3 4 | function isAssociative(array $array) { return count(array_filter(array_keys($array), function($v){return is_numeric($v);})) !== count($array)); } |
。
我只使用key()函数。观察:
1 2 3 4 5 |
号
因此,只要检查是否为false,就可以确定数组是否是关联的。
如果您只查找非数字键(无论顺序如何),那么您可能需要尝试
1 2 3 4 |