对您最初的问题的简短回答:您为什么不能写作$json['count'] where $json['id'] = 3
?仅仅是因为 PHP 不是一种查询语言。您提出问题的方式就像一个简单的 SQL 选择查询。SQL 将遍历其索引,并且(如果需要)也将执行全表扫描,其结构化查询语言仅使您无需费心写出数据库将执行的循环。
不是这样的,因为你不写循环,就没有循环(没有证据不是没有证据)。我不会把所有的图灵都放在你身上,但我们只能在机器层面上做很多事情。在较低的级别上,您只需一次迈出一步。通常,这意味着递增、检查和再次递增……AKA 递归和遍历。
PHP 会认为它理解你的意思$json['id']
,它会认为你的意思是让它返回id
数组中, 引用的值$json
,而你实际上想要$json[n]['id']
被获取。要确定n
,您必须编写一个循环。有些人建议对数组进行排序。就像任何其他array_*
映射/过滤/合并的函数一样,这也意味着循环整个数组。没有办法周围。由于没有开箱即用的核心功能可以完全满足您的需求,因此您将不得不自己编写循环。
如果性能对您很重要,您可以编写更有效的循环。下面,你可以找到一个稍微不那么粗暴的循环,一个半插值搜索。你也可以在这里使用三元搜索,实现它是你可以做的。
for ($i = 1, $j = count($bar), $h = round($j/2);$i<$j;$i+= $h)
{
if ($bar[++$i]->id === $search || $bar[--$i]->id === $search || $bar[--$i]->id === $search)
{//thans to short-circuit evaluation, we can check 3 offsets in one go
$found = $bar[$i];
break;
}//++$i, --$i, --$i ==> $i === $i -1, increment again:
if ($bar[++$i]->id > $search)
{// too far
$i -= $h;//return to previous offset, step will be halved
}
else
{//not far enough
$h = $j - $i;//set step the remaining length, will be halved
}
$h = round($h/2);//halve step, and round, in case $h%2 === 1
//optional:
if(($i + $h + 1) === $j)
{//avoid overflow
$h -= 1;
}
}
$bar
您的 json 解码数组在哪里。
下面解释了它的确切工作原理,以及这种方法的缺点,但现在,与您的问题更相关:如何实现:
function lookup(array $arr, $p, $val)
{
$j = count($arr);
if ($arr[$j-1]->{$p} < $val)
{//highest id is still less value is still less than $val:
return (object) array($p => $val, 'count' => 0, 'error' => 'out of bounds');
}
if ($arr[$j-1]->{$p} === $val)
{//the last element is the one we're looking for?
return $end;
}
if ($arr[0]->{$p} > $val)
{//the lowest value is still higher than the requested value?
return (object) array($p => $val, 'count' => 0, 'error' => 'underflow');
}
for ($i = 1, $h = round($j/2);$i<$j;$i+= $h)
{
if ($arr[++$i]->{$p} === $val || $arr[--$i]->{$p} === $val || $arr[--$i]->{$p} === $val)
{//checks offsets 2, 1, 0 respectively on first iteration
return $arr[$i];
}
if ($arr[$i++]->{$p} < $val && $arr[$i]->{$p} > $val)
{//requested value is in between? don't bother, it won't exist, then
return (object)array($p => $val, 'count' => 0, 'error' => 'does not exist');
}
if ($arr[++$i]->{$p} > $val)
{
$i -= $h;
}
else
{
$h = ($j - $i);
}
$h = round($h/2);
}
}
$count = lookup($json, 'id', 3);
echo $count['count'];
//or if you have the latest version of php
$count = (lookup($json, 'id', 3))['count'];//you'll have to return default value for this one
就个人而言,如果未找到属性值对,我不会返回默认对象,我会返回null
或抛出 a RuntimeException
,但这由您决定。
循环基本上是这样工作的:
- 在每次迭代中,都会检查 offset 和处
$i
的$i+1
对象$i-1
。
如果找到对象,则分配对其的引用$found
并结束循环
- 找不到对象。执行以下两个步骤之一:
- offset 处的 ID 大于我们要查找的 ID,
$h
从 offset 中减去 step ( ) $i
,然后将 step 减半。再次循环
- ID小于搜索(我们还没有):将步长更改为数组剩余长度的一半
一张图将说明为什么这是一种更“聪明”的循环方式:
|==========x=============================|//suppose x is what we need, offset 11 of a total length 40:
//iteration 1:
012 //checked offsets, not found
|==========x=============================|
//offset + 40/2 == 21
//iteration 2:
012//offsets 20, 21 and 22, not found, too far
|==========x=============================|
//offset - 21 + round(21/2)~>11 === 12
//iteration 3:
123 //checks offsets 11, 12, 13) ==> FOUND
|==========x=============================|
assign offset-1
break;
而不是 11 次迭代,我们在仅仅 3 次迭代后就设法找到了我们需要的对象!虽然这个循环有点贵(涉及更多的计算),但缺点很少超过好处。
不过,就目前而言,这个循环有一些盲点,因此在极少数情况下它会变慢,但平均而言,它的表现相当不错。我已经测试了这个循环几次,一个包含 100,000 个对象的数组,寻找 id random(1,99999)
,我没有看到它花费的时间超过 0.08 毫秒,平均而言,它管理 0.0018 毫秒,这一点也不差。
当然,您可以通过使用偏移处的 id 和搜索到的 id 之间的差异来改进循环,或者如果偏移$i
处的 id 大于搜索值和偏移处的 id 则中断$i-1
小于搜索值以避免无限循环。不过,总的来说,这是迄今为止这里提供的最具可扩展性和性能的循环算法。