PHP将所有数组视为关联的,因此没有任何内置函数.任何人都可以推荐一种相当有效的方法来检查数组是否只包含数字键?
基本上,我希望能够区分这个:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
还有这个:
$assocArray = array('fruit1' => 'apple', 'fruit2' => 'orange', 'veg1' => 'tomato', 'veg2' => 'carrot');
Greg.. 587
你问过两个不完全相同的问题:
首先,如何确定数组是否只有数字键
其次,如何确定数组是否具有从0开始的顺序数字键
考虑一下您实际需要的这些行为.(这可能是为了你的目的.)
第一个问题(简单地检查所有键都是数字)由kurO船长很好地回答.
对于第二个问题(检查数组是否为零索引和顺序),您可以使用以下函数:
function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
var_dump(isAssoc(['a', 'b', 'c'])); // false
var_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // false
var_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // true
var_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true
很优雅的解决方案 请注意,它在空数组的(不明确的)情况下返回TRUE. (29认同)
我认为将顺序数组视为关联数组的一种特殊情况更有用.所以每个数组都是关联的,但只有一些是顺序的.因此,函数`isSequential()`比`isAssoc()`更有意义.在这样的函数中,空数组*应该被视为顺序的.公式可以是`array()=== $ arr || !isAssoc($ ARR)`. (27认同)
我认为如果在提取所有键之前检查isset($ arr [0])是否为false,这将避免大量潜在的cpu时间和内存,如果数组不是空的但是在0中没有元素则它是明确关联的位置.由于"大多数"真正的关联数组具有字符串作为键,因此对于此类函数的一般情况应该是一个很好的优化. (13认同)
@OderWat - 您的优化应该使用`array_key_exists`而不是`isset`,因为如果零元素是空值,则isset将错误地返回false.空值通常应该是这种数组中的合法值. (8认同)
Captain kurO.. 424
仅检查数组是否具有非整数键(而不是数组是顺序索引还是零索引):
function has_string_keys(array $array) { return count(array_filter(array_keys($array), 'is_string')) > 0; }
如果至少有一个字符串键,$array
则将其视为关联数组.
你问过两个不完全相同的问题:
首先,如何确定数组是否只有数字键
其次,如何确定数组是否具有从0开始的顺序数字键
考虑一下您实际需要的这些行为.(这可能是为了你的目的.)
第一个问题(简单地检查所有键都是数字)由kurO船长很好地回答.
对于第二个问题(检查数组是否为零索引和顺序),您可以使用以下函数:
function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
var_dump(isAssoc(['a', 'b', 'c'])); // false
var_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // false
var_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // true
var_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true
仅检查数组是否具有非整数键(而不是数组是顺序索引还是零索引):
function has_string_keys(array $array) { return count(array_filter(array_keys($array), 'is_string')) > 0; }
如果至少有一个字符串键,$array
则将其视为关联数组.
当然这是一个更好的选择.
这将复制数组中的值,这可能非常昂贵.你检查数组键要好得多.
我只是用==; 我认为这里不需要===.但要回答"未设置且不起作用":一旦取消设置第一个元素,它就不再是从0开始的整数索引数组.所以IMO确实有效.
我唯一的问题是`===`会浪费时间检查值是否相等,即使我们只对键感兴趣.出于这个原因,我更喜欢`$ k = array_keys($ arr); return $ k === array_keys($ k);`version.
同意@grantwparks:稀疏数组未编入索引.有趣的是,因为没有办法真正从索引数组的中间删除一个元素,PHP基本上将所有数组声明为关联数组,而数字只是一个"构成我的关键"版本.
一个补充说明,这在使用无序的数字键指定的数组上失败.例如$ myArr = array(0 =>'a',3 =>'b',4 => 1,2 => 2,1 =>'3'); 一个潜在的解决方法是在进行测试之前运行ksort($ arr)
4> squirrel..:这个问题中的许多评论者都不理解数组如何在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作为数组键,它会被静默转换为"").
非关联数组必须具有从"0"到"count($ array)-1"的键,按此严格顺序.使用`is_array()`进行初步检查可能有所帮助.添加一个递增变量来检查键序列:`for($ k = 0,reset($ array); $ k === key($ array); next($ array))++ $ k;`这解决了应对.
@DavidJ,“不工作”是什么意思?它成功地确定所有键都是整数。您是否声称不应将像您发布的数组那样的数组视为“数字数组”?
使用`foreach`而不是显式迭代大约快两倍.
5> Pang..:如OP所述:
PHP将所有数组视为关联的
编写一个检查数组是否是关联的函数是不太明智的(恕我直言).首先要说的是:PHP数组中的键是什么?:
该键可以是一个整数或字符串.
这意味着有3种可能的情况:
情况1.所有键都是数字/整数.
案例2.所有键都是字符串.
情况3.一些键是字符串,一些键是数字/整数.
我们可以使用以下功能检查每个案例.
情况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); }情况3.一些键是字符串,一些键是数字/整数.
注意:对于空数组,此函数也返回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; }它遵循:
如果该值不是数组,则所有3个函数都返回false.
如果该值为空数组,则所有3个函数都返回true
(根据定义,如" 空集合是任何集合A的子集,因为它的所有元素都属于A ").如果该值是非空数组,则恰好1个函数返回true.
现在,对于一个我们都习以为常的"真正"数组,意思是:
它的键都是数字/整数.
其键是顺序的(即通过步骤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 /sf/ask/17360801/) 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.根据文件,更重要的是,
整数的大小取决于平台,尽管最大值约为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.更糟的是,PHP趋向于车如果整数是2的附近31 = 2,147,483,648边界(参见错误51430,错误52899).例如,在我的本地环境(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位整数.
除了,如下所述,它涉及为正在检查的数组创建一个重复数组,这使得它对于大型数组来说非常昂贵,并且在共享主机上可能会出现内存崩溃的原因.
6> Alix Axel..:速度明智的:
function isAssoc($array) { return ($array !== array_values($array)); }内存明智的:
function isAssoc($array) { $array = array_keys($array); return ($array !== array_keys($array)); }
以下是上述的性能基准:https://gist.github.com/1965669
7> 小智..:实际上最有效的方法是:
function is_assoc($array){ $keys = array_keys($array); return $keys !== array_keys($keys); }这是有效的,因为它将键(对于顺序数组总是为0,1,2等)与键的键(总是为0,1,2等)进行比较.
这个函数为`array(1 =>"a")`返回`true`,但```('a"=>"a")`返回`false`.如果`!=`替换为`!==`会更有意义.
它是无效的,因为它涉及调用array_keys而不是只检查,直到找到非顺序整数索引.你正在做什么_anyway_,但你已经复制了一个大阵列.
8> dsims..:function checkAssoc($array){ return ctype_digit( implode('', array_keys($array) ) ); }
如果其中一个键是空字符串,那会有效吗?
这是*唯一*答案(在我的评论时)可以处理以下内容:$ array = array(0 =>'blah',2 =>'yep',3 =>'wahey')
9> podperson..:我已经使用了两个
array_keys($obj) !== range(0, count($obj) - 1)
和array_values($arr) !== $arr
(它们是彼此的双重,虽然第二个比第一个便宜)但是对于非常大的数组都失败了.这是因为
array_keys
并且array_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; }另请注意,如果您不关心将稀疏数组与关联数组区分开来,则只需
'assoc'
从两个if
块返回即可.最后,虽然这看起来不像本页面上的许多"解决方案"那么"优雅",但实际上它的效率要高得多.几乎任何关联数组都会立即被检测到.只有索引数组才能得到详尽的检查,上面列出的方法不仅详尽地检查了索引数组,而且还复制了它们.
10> 小智..:我认为以下两个函数是检查"数组是关联还是数字"的最佳方法.由于"数字"可能仅表示数字键或仅表示顺序数字键,因此下面列出了两个检查任一条件的函数:
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.如果不是,则循环过早结束,因为键的类型为string,而for循环后的语句将返回false.后一个函数在每次比较后另外添加一个$ base,以便能够检查下一个键是否具有正确的值.严格比较使它还检查键是否为整数类型.当省略$ base或者确保仅使用整数调用时,可以省略for循环第一部分中的$ base =(int)$ base部分.但是因为我不能确定每个人,所以我把它留了进去.无论如何,该声明只执行一次.我认为这些是最有效的解决方案:
内存方面:不复制数据或密钥范围.执行array_values或array_keys可能看起来更短(代码更少),但请记住,一旦进行调用,后台会发生什么.是的,有比其他一些解决方案更多(可见)的陈述,但这不重要,是吗?
时间方面:除了复制/提取数据和/或密钥也需要时间之外,这种解决方案比做foreach更有效.对于某些人来说,foreach似乎更有效,因为它的符号更短,但在后台foreach也调用reset,key和next来循环.但另外它也调用有效来检查结束条件,由于与整数检查相结合,这里避免了这种情况.
请记住,数组键只能是整数或字符串,严格的数字字符串(如"1"(但不是"01"))将转换为整数.除了计算是否希望数组是顺序的,除了计算之外,检查整数键的唯一需要的操作是什么.当然,如果is_indexed_array返回false,则可以将该数组视为关联数组.我说'看',因为事实上他们都是.
11> LazNiko..:这个功能可以处理:
索引中有孔的数组(例如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; }
12> Manu Manjuna..:我注意到这个问题有两个流行的方法:一个使用
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.239msPHP 5.3的输出产生了类似的结果.显然使用
array_values()
速度要快得多.
13> 小智..:解决这个问题的一种方法是搭载
json_encode
,它已经有了自己的内部方法来区分关联数组和索引数组,以便输出正确的JSON.您可以通过检查编码后返回的第一个字符是
{
(关联数组)还是[
(索引数组)来执行此操作.// Too short :) function is_assoc($arr) { ksort($arr); return json_encode($arr)[0] === '{'; }
14> Ben..:已有很多答案,但这里是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
15> Jesse..:function array_is_assoc(array $a) { $i = 0; foreach ($a as $k => $v) { if ($k !== $i++) { return true; } } return false; }快速,简洁,内存高效.没有昂贵的比较,函数调用或数组复制.