отфильтровывать значения в многомерном массиве в php - PullRequest
1 голос
/ 02 июня 2011

У меня есть ситуация, когда у меня есть два массива, и я использую один массив (A), чтобы определить, какие элементы второго массива (B) мне нужно использовать. Поскольку я буду расчесывать B несколько раз и каждый раз, когда мне не понадобятся ранее использованные элементы, я написал функцию, которую я вызываю sift_array.

function sift_array(&$array, $key, $value){
   foreach($array as $element){
      if($element[$key] == $value){$temp[] = $element;}
      else{$temp2[] = $element;}
   }
   $array = $temp2;
   return $temp;
}

Мне любопытно, если есть более быстрый способ выполнить эту функцию.

Вот пример того, как я использую эту функцию.

В моем веб-приложении пользователи могут настроить «бота» для выполнения действий столько раз, пока они отсутствуют / выходят из системы. У меня есть программа, которая будет периодически запускаться для работы ботов. Боты не могут выполнять одно и то же действие, если это был последний бот, который это сделал.

Массив ботов имеет 3 ключевых элемента: account_id, action_id, job_id Массив job содержит 1 ключевой элемент: job_id.

Я передаю массив ботов, затем для ключа $ я передаю 'job_id' и для значения $ я передаю идентификатор задания, с которым я работаю, скажем, задание 1.

$runTheseBots = sift_array($bots, 'job_id', 1);
executeBots($runTheseBots);

Это будет иметь более быстрое время выполнения, потому что все боты, которые имеют дело с заданием 1, уже обработаны.

$runTheseBots = sift_array($bots, 'job_id', 2); 
executeBots($runTheseBots);

Ответы [ 3 ]

3 голосов
/ 02 июня 2011

Есть много способов реализовать это, но я просто прокомментирую, как вы это сделали:

function sift_array(&$array, $key, $value){
   foreach($array as $i => $element){
      if($element[$key] == $value){
        $temp[] = $element;
        unset($array[$i]);
      }      
   }
   return $temp;
}

Здесь мы экономим память и время (ну, может быть, не время ... тест, чтобы увидеть), не делая копию $array. Просто удалите элемент после перемещения его в новый массив.

Опять же, я не утверждаю, что это лучший способ сделать это ... просто способ оптимизировать то, что вы уже сделали.

1 голос
/ 02 июня 2011

Я бы пошел с подходом сгиба:

function sift($array, $key, $value) {
    return array_reduce($array, function ($a, $elem) use ($key, $value) {
        $elem[$key] == $value ? $a[0] = $elem : $a[1] = $elem;
    }, array());
}

$bots = sift($array, 'job_id', 2);
executeBots($bots[0]);

Этот IMO в основном улучшает «интерфейс», устраняя переход по ссылке + возврат и возвращая «сгруппированный» массив. PHP может быть в состоянии оптимизировать это лучше внутри, и, следовательно, может быть более эффективным, но я не знаю, насколько это действительно так. Хотя стоит попробовать.


Хорошо, небольшое тестирование показывает, что на самом деле это намного медленнее . Но, если вы только фильтруете интересующих вас ботов и отбрасываете остальных, array_filter может дать толчок. Если вы все равно отказываетесь, полное удаление ненужных значений является самым быстрым:

$bots = array();
for ($i = 0; $i < 100000; $i++) {
    $bots = array('job_id' => mt_rand(1, 100));
}

function sift_reduce($array, $key, $value) {
    return array_reduce($array, function ($a, $elem) use ($key, $value) {
        $elem[$key] == $value ? $a[0] = $elem : $a[1] = $elem;
    }, array());
}

function sift_filter($array, $key, $value) {
    return array_filter($array, function ($elem) use ($key, $value) { return $elem[$key] == $value; });
}

function sift_unset(&$array, $key, $value) {
    foreach ($array as $k => $elem) {
        if ($elem[$key] != $value) {
            unset($array[$k]);
        }
    }
}

function sift_array(&$array, $key, $value){
   foreach($array as $element){
      if($element[$key] == $value){$temp[] = $element;}
      else{$temp2[] = $element;}
   }
   $array = $temp2;
   return $temp;
}

$start = microtime(true);
$sifted = sift_reduce($bots, 'job_id', 5);
echo "reduce:  " . (microtime(true) - $start) * 1000 . "\n";

$start = microtime(true);
$sifted = sift_filter($bots, 'job_id', 5);
echo "filter:  " . (microtime(true) - $start) * 1000 . "\n";

$botsCopy = $bots;
$start = microtime(true);
sift_unset($botsCopy, 'job_id', 5);
echo "unset:   " . (microtime(true) - $start) * 1000 . "\n";

$start = microtime(true);
$sifted = sift_array($bots, 'job_id', 5);
echo "array:   " . (microtime(true) - $start) * 1000 . "\n";

Дает:

reduce:  0.072002410888672
filter:  0.013828277587891
unset:   0.0088214874267578
array:   0.016927719116211
0 голосов
/ 02 июня 2011

Лучшим вариантом будет что-то, называемое «сортировка по сегментам».

Это самый быстрый вариант для сортировки, если имеется относительно небольшое количество «сегментов».case, каждый job_id является 'bucket'.

Вы хотите отсеивать массив только один раз.Для каждого элемента вы помещаете его в соответствующий блок, в зависимости от job_id (один блок на job_id.).

Если оптимизация абсолютно необходима, у вас может быть две или более функции сортировки.Выбор алгоритма сортировки выполняется во время выполнения в зависимости от количества сегментов и элементов, с которыми необходимо работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...