Фактът, че получавате данните си от 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";
вижте как работи.
Първата алтернатива, която идва на ум, е обикновен стар 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";
вижте как работи.
Втората алтернатива, която ми хрумва, е да премина през масива веднъж и да го запиша в асоциативен масив. Първото преминаване ще бъде 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";
вижте как работи.
Ето останалата част от тестовия ми код: -
//=================================================================
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
вижте как работи.
Разликите във времето също са интересни. Може или не може да се наложи да оптимизирате за скорост като тази, но бих предложил, че това е полезна промяна, която да направите във вашия код. Във всеки случай бих си помислил, че липсата на итерации след първата е много по-добра от 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);
person
vascowhite
schedule
05.05.2014