您从 API 获取 json 格式的数据这一事实并不意味着您必须将其保留为您提供的格式。有几种策略会比您接受的答案更有效。
当前选择的答案的问题是,每次使用它时它都会遍历整个数据集,即使在第一次迭代中找到匹配项也是如此。我不知道你的数据集有多大,但我假设它很重要,否则你不会问这个问题。
我也不知道您想访问数据集多少次,您可能也不知道,但我认为已经有足够的时间让您考虑,或者,再次,您不会问这个问题.
假设您有一个由 1000 个 stdClass 对象组成的数据集,并且您要求每个对象一次,因此您访问它 1000 次。
现在建议给您的 'array_filter()' 方法每次都必须访问所有 1000 个元素(它是 O(n)),即总共 1,000,000 次迭代。
//Access every element once using array_filter()
$objectArray = [];
$objectNames = [];
for($i = 0; $i < 1000; $i ++){
$objName = 'object_name_' . ($i + 1);
$objectNames[] = $objName;
$obj = new stdClass();
$obj->name = $objName;
$obj->description = 'test description';
$obj->accessed = 0;
$objectArray[] = $obj;
}
$start = microtime(true);
foreach($objectNames as $name){
$iterations = getObjectWithArray_Filter($name, $objectArray);
}
$end = microtime(true);
$taken = $end - $start;
echo $iterations . " iterations using array_filter() in $taken seconds<br/>\n";
see it working.
想到的第一个替代方案是一个普通的旧foreach()loop,这也是 O(n),但是一旦找到匹配项,就可以将循环写入保释。因此,假设我们在 500,500 次迭代后访问数组的每个元素,节省了大约 50%。这在现实世界中可能适用,也可能不适用,您将是最好的判断者。
//Access every element once using foreach(){}
$objectArray = [];
$objectNames = [];
for($i = 0; $i < 1000; $i ++){
$objName = 'object_name_' . ($i + 1);
$objectNames[] = $objName;
$obj = new stdClass();
$obj->name = $objName;
$obj->description = 'test description';
$obj->accessed = 0;
$objectArray[] = $obj;
}
$start = microtime(true);
foreach($objectNames as $name){
$iterations = getObjectWithForeach($name, $objectArray);
}
$end = microtime(true);
$taken = $end - $start;
echo $iterations . " iterations using foreach(){} in $taken seconds<br/>\n";
see it working.
我想到的第二种选择是遍历数组一次并将其写入关联数组。第一次运行将是 O(n),1000 次迭代,然后我们将能够直接访问我们想要的元素,而无需遍历数组,即 O(1)。给我们 2000 次迭代来访问数组的每个元素一次。
//Access every element once using Associative array
$objectArray = [];
$objectNames = [];
for($i = 0; $i < 1000; $i ++){
$objName = 'object_name_' . ($i + 1);
$objectNames[] = $objName;
$obj = new stdClass();
$obj->name = $objName;
$obj->description = 'test description';
$obj->accessed = 0;
$objectArray[] = $obj;
}
$associativeArray = [];
$start = microtime(true);
foreach($objectArray as $object){
$associativeArray[$object->name] = $object;
$object->accessed ++;
}
foreach($objectNames as $name){
$iterations = getObjectFromAssociativeArray($objName, $associativeArray);
}
$end = microtime(true);
$taken = $end - $start;
echo $iterations . " iterations using associative array{} in $taken seconds<br/>\n";
see it working.
这是我的其余测试代码:-
//=================================================================
function getObjectWithArray_Filter($objectName, array $objectArray){
$myobjects = array_filter($objectArray, function($e) use($objectName) {
$e->accessed ++;
return strcmp($e->name, $objectName) == 0;
});
$iterations = 0;
foreach($objectArray as $object){
$iterations += $object->accessed;
}
return $iterations;
}
function getObjectWithForeach($objectName, array $objectArray){
$iterations = 0;
$found = false;
$count = 0;
while(!$found){
$objectArray[$count]->accessed ++;
if($objectArray[$count]->name === $objectName){
$found = true;
}
$count ++;
}
foreach($objectArray as $object){
$iterations += $object->accessed;
}
return $iterations;
}
function getObjectFromAssociativeArray($objectName, array $objectArray){
$iterations = 0;
if($objectName === $objectArray[$objectName]->name){
$objectArray[$objectName]->accessed ++;
}
foreach($objectArray as $object){
$iterations += $object->accessed;
}
return $iterations;
}
tl;dr
3v4l.org 上的输出:-
Accessing 1000 elements once took 1000000 iterations using array_filter() in 0.5374710559845 seconds
Accessing 1000 elements once took 500500 iterations using foreach(){} in 0.2077169418335 seconds
Accessing 1000 elements once took 2000 iterations using associative array{} in 0.1438410282135 seconds
see it working.
时间差异也很有趣。您可能需要也可能不需要像这样优化速度,但我建议您对代码进行值得的更改。无论如何,我认为第一次之后的任何迭代都比 1000 次或平均每次 500.5 次要好得多。
我希望你认为这是一个有价值的练习,你的问题引起了我的兴趣,我确信你接受的答案对你来说并不是最好的解决方案。你可能仍然认为它是,但我提供了这个作为替代方案。
实现这一点的最简单方法是使用某种对象存储/工厂:-
class ObjectStore
{
private $decoded;
private $asssocArray;
public function __construct($jsonEncodedObjects)
{
$this->decoded = json_decode($jsonEncodedObjects);
}
public function getObject($objectName)
{
if(!$this->asssocArray){
foreach($this->decoded as $object){
$this->asssocArray[$object->name] = $object;
}
}
return $this->asssocArray[$objectName];
}
}
这样你对对象的第一个请求是 O(n),后续请求将是 O(1)。
要将其与您的代码一起使用:-
$objectStore = new ObjectStore(getJsonEncodedData());
$hashtag = "test-song-poll-03";
$myObject = $objectStore->getObject($hashtag);