PHP将所有数组视为关联的,因此没有任何内置函数 . 任何人都可以推荐一种相当有效的方法来检查一个数组是否只包含数字键?
基本上,我希望能够区分这个:
$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');
还有这个:
$assocArray = array('fruit1' => 'apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
30 回答
从source开始快一点 . 适合
json_encode
(和bson_encode
)的编码 . 所以有javascript阵列合规性 .经过一些本地基准测试,调试,编译器探测,分析和滥用3v4l.org以在更多版本中进行基准测试(是的,我收到警告要停止)并比较我可以找到的每个变化...
我给你一个有机派生的 best-average-worst-case scenario 关联数组测试函数,最差的大致和所有其他平均情况一样好或更好 .
从https://3v4l.org/rkieX:
如stated by the OP:
编写一个检查数组是否是关联的函数是不太明智的(恕我直言) . 首先要做的事情是:what is a key in a PHP array?:
这意味着有三种可能的情况:
案例1.所有键都是 numeric / integers .
案例2.所有键都是 strings .
案例3.有些键是 strings ,有些键是 numeric / integers .
我们可以使用以下功能检查每个案例 .
案例1:所有键都是数字/整数 .
Note :此函数也为空数组返回 true .
案例2:所有键都是字符串 .
Note :此函数也为空数组返回 true .
案例3.一些键是字符串,一些键是数字/整数 .
Note :此函数也为空数组返回 true .
它遵循:
如果值为 not an array ,则 all 3 函数返回 false .
如果值为 an empty array , all 3 函数返回 true
(根据定义,如“the empty set is a subset of any set A because all its elements belong to A”) .
如果值为 a non-empty array ,则 exactly 1 函数返回 true .
现在,对于一个我们都习以为常的"genuine"数组,意思是:
它的键都是 numeric / integers .
其键是 sequential (即按步骤1增加) .
其键 start from zero .
我们可以查看以下功能 .
案例3a . 键是数字/整数,顺序和从零开始 .
Note :此函数也为空数组返回 true .
警告/陷阱(或者,更多关于PHP中数组键的特殊事实)
整数键
这些数组的键是 integers :
字符串键
这些数组的键是 strings :
看起来像字符串的整数键
如果您认为
array("13" => "b")
中的键是一个字符串, you are wrong . 来自doc here:例如,这些数组的键是 integers :
但这些数组的关键是 strings :
更重要的是,根据doc,
所以这个数组 may or may not 的关键是一个整数 - 它取决于你的平台 .
更糟糕的是,如果整数接近231 = 2,147,483,648边界(参见bug 51430,bug 52899),则PHP往往为 buggy . 例如,在我的本地环境(Windows 7上的XAMPP 1.7.7上的PHP 5.3.8)中,
var_dump(array("2147483647" => "b"))
给出了但是在this live demo on codepad(PHP 5.2.5)中,相同的表达式给出了
因此,键是一个环境中的整数,但是另一个环境中的字符串,即使
2147483647
是有效的带符号32位整数 .您提出了两个不完全相同的问题:
首先,如何确定数组是否只有数字键
其次,如何确定数组是否具有从0开始的顺序数字键
考虑一下您实际需要的这些行为 . (这可能是为了你的目的 . )
第一个问题(只是检查所有键都是数字)是answered well by Captain kurO .
对于第二个问题(检查数组是否为零索引和顺序),您可以使用以下函数:
解决这个问题的一种方法是搭载
json_encode
,它已经有了自己的内部方法来区分关联数组和索引数组,以便输出正确的JSON .您可以通过检查编码后返回的第一个字符是
{
(关联数组)还是[
(索引数组)来执行此操作 .我的解决方案
单个阵列上的
array_merge
将重新索引所有integer
键,而不是其他键 . 例如:因此,如果创建列表(非关联数组)
['a', 'b', 'c']
然后删除一个值unset($a[1])
然后调用array_merge
,则从0开始重新索引列表 .通过使用xarray PHP扩展
您可以非常快速地完成此任务(在PHP 5.6中快30倍):
要么:
我比较了数组的键和数组的array_values()结果的键之间的差异,它始终是一个带整数索引的数组 . 如果键是相同的,它不是关联数组 .
仅检查数组是否具有非整数键(不是数组是顺序索引还是零索引):
如果至少有一个字符串键,则
$array
将被视为关联数组 .这个问题中的许多评论者都不理解数组如何在PHP中工作 . 来自array documentation:
换句话说,没有数组键“8”,因为它总是(静默地)转换为整数8.因此,尝试区分整数和数字字符串是不必要的 .
如果你想要一种最有效的方法来检查一个非整数键的数组,而不需要复制一部分数组(比如array_keys())或者所有这些(比如foreach那样):
这是有效的,因为当当前数组位置无效时,key()返回NULL,而NULL永远不能是有效键(如果您尝试将NULL用作数组键,则会将其静默转换为“”) .
除非PHP具有内置功能,否则您将无法在小于O(n)的情况下执行此操作 - 枚举所有键并检查整数类型 . 实际上,您还需要确保没有漏洞,因此您的算法可能如下所示:
但为什么要这么麻烦?假设数组是您期望的类型 . 如果不是这样,它就会在你面前爆炸 - 这是你的动态编程!测试你的代码,一切都会好的......
这可能是解决方案吗?
需要注意的是,数组游标是重置的,但我可能会说,在遍历或使用数组之前可能会使用该函数 .
实际上最有效的方法是:
这是有效的,因为它将键(对于顺序数组总是为0,1,2等)与键的键(总是0,1,2等)进行比较 .
已有很多答案,但这里是Laravel在其Arr类中依赖的方法:
资料来源:https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php
我认为以下两个函数是检查“如果数组是关联的还是数字的”的最佳方法 . 由于'numeric'可能仅表示数字键或仅表示顺序数字键,因此下面列出了两个检查任一条件的函数:
第一个函数检查每个键是否为整数值 . 第二个函数检查每个键是否为整数值,另外检查所有键是否从$ 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,则可以将数组视为关联数组 . 我说'看过',因为事实上它们都是 .
得分最高的这两个示例都无法正常使用像
$array = array('foo' => 'bar', 1)
这样的数组这个功能可以处理:
带索引孔的
具有"0x"键的
这个想法很简单:如果其中一个键不是整数,则它是关联数组,否则它是顺序的 .
这是我使用的方法:
请注意,这不包括以下特殊情况:
对不起,帮不了你 . 它对于尺寸合适的阵列也有一定的性能,因为它不会制作不必要的副本 . 正是这些小东西使Python和Ruby更好地写入......:P
当然这是一个更好的选择 .
我已经使用了
array_keys($obj) !== range(0, count($obj) - 1)
和array_values($arr) !== $arr
(它们是彼此的双重,虽然第二个比第一个便宜)但是对于非常大的数组都失败了 .这是因为
array_keys
和array_values
都是非常昂贵的操作(因为它们构建了一个大小与原始大小相同的全新数组) .以下函数比上面提供的方法更强大:
另请注意,如果您不想将稀疏数组与关联数组区分开来,则只需从
if
块中返回'assoc'
即可 .最后,虽然这看起来不像本页面上的许多“解决方案”那么“优雅”,但实际上它的效率要高得多 . 几乎任何关联数组都会立即被检测到 . 只有索引数组才能得到详尽的检查,上面列出的方法不仅详尽地检查了索引数组,而且还复制了它们 .
我认为标量数组的定义会因应用程序而异 . 也就是说,某些应用程序需要更严格意义上的标量数组,而某些应用程序需要更宽松的意义 .
下面我介绍3种不同严格的方法 .
我知道添加这个庞大队列的答案有点无意义,但这是一个可读的O(n)解决方案,不需要复制任何值:
而不是检查键以查看它们是否都是数字,而是迭代数字数组所在的键并确保它们存在 .
我注意到这个问题有两个流行的方法:一个使用
array_values()
,另一个使用key()
. 为了找出哪个更快,我写了一个小程序:CentOS上PHP 5.2程序的输出如下:
PHP 5.3的输出产生了类似的结果 . 显然使用
array_values()
要快得多 .快速,简洁,内存高效 . 没有昂贵的比较,函数调用或数组复制 .
这也可以(demo):
请注意,这个答案的要点是告知您
SplFixedArray
的存在,而不是鼓励您使用例外进行这些类型的测试 .已经给出了答案但是有太多关于性能的虚假信息 . 我写了这个小基准脚本,表明foreach方法是最快的 .
免责声明:以下方法是从其他答案中复制粘贴的
结果:
修改最流行的答案 .
这需要更多的处理,但更准确 .
Speed-wise:
Memory-wise:
在我看来,如果一个数组的任何键不是整数,那么它应该被接受为关联数组 . 浮点数和空字符串'' .
同样,非序列整数必须被视为关联式(0,2,4,6),因为这种类型的数组不能通过这种方式与for循环一起使用:
下面的函数的第二部分确实检查键是否已编入索引 . 它也适用于具有负值的键 . 例如(-1,0,1,2,3,4,5)