Выбор каждого n-го элемента из массива

Какой самый эффективный способ выбрать каждый n-й элемент из большого массива? Есть ли "умный" способ сделать это или перебирает единственный путь?

Некоторые моменты, которые следует учитывать:

  • Массив довольно большой с 130 000 элементов
  • Мне нужно выбрать каждый 205-й элемент
  • Элементы не индексируются численно, поэтому for($i = 0; $i <= 130000; $i += 205) не будет работать

Пока это самый эффективный метод, с которым я столкнулся:

$result = array();
$i = 0;
foreach($source as $value) {

    if($i >= 205) {
        $i = 0;
    }

    if($i == 0) {
        $result[] = $value;
    }

    $i++;
}

Или то же самое с модулем:

$result = array();
$i = 0;
foreach($source as $value) {
    if($i % 205 == 0) {
        $result[] = $value;
    }
    $i++;
}

Эти методы могут быть довольно медленными, есть ли способ улучшить? Или я просто раскалываю волосы здесь?

ИЗМЕНИТЬ

Хорошие ответы вокруг с правильными объяснениями, старались выбрать наиболее подходящий, как принятый ответ. Спасибо!

Ответы

Ответ 1

Цикл foreach обеспечивает самую быструю итерацию по вашему большому массиву на основе тестирования сравнения. Я бы придерживался чего-то похожего на то, что у вас есть, если кто-то не хочет решить проблему с циклом разворачивания.

Этот ответ должен выполняться быстрее.

$result = array();
$i = 0;
foreach($source as $value) {
    if ($i++ % 205 == 0) {
        $result[] = $value;
    }
}

У меня нет времени для тестирования, но вы можете использовать вариацию решения @haim, если вы сначала индексируете массив массивом. Стоит посмотреть, сможете ли вы получить прибыль от моего предыдущего решения:

$result = array();
$source = array_values($source);
$count = count($source);
for($i = 0; $i < $count; $i += 205) {
    $result[] = $source[$i];
}

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

Ответ 2

Попробуйте ArrayIterator:: seek()

Кроме того, используя одну новую конструкцию Spl datastructures, может дать лучшие результаты, чем использование простых массивов.

Ответ 3

Я рекомендую использовать array_slice

$count = count($array) ;
for($i=205;$i<$count;$i+=205){
    $result[] = array_slice($array,$i,1);
}

Если ваш массив был численно проиндексирован, это было бы очень быстро:

$count = count($array) ;
for($i=205;$i<$count;$i+=205){
    $result[] = $array[$i];
}

Ответ 4

Я думаю, что решение этой проблемы не связано ни с одним синтаксисом PHP, а с дизайном вашего кода.

Вы можете либо сделать числовое индексирование массива (возможно, не правдоподобным для вашего приложения), отслеживать каждый 205-й элемент или только один раз искать массив (кешировать список каждого 205-го элемента).

По моему мнению, отслеживание каждого 205-го элемента было бы проще реализовать. Вы просто сохраните счет всех элементов в базе данных или что-то еще, и каждый раз, когда элемент добавляется, проверьте по модулю счет. Если у вас есть еще 205-й элемент, добавьте его в массив. Ибо, когда элементы удаляются, это было бы сложнее. Возможно, вам придется перепроверить весь массив, чтобы перестроить все ваши 205-е элементы.

Выполнение этого было бы проще, если бы вы могли начать работу с удаленным элементом и двигаться вперед, но опять же это будет работать только для массивов с числовой индексацией - и если бы это было так, вам не пришлось бы двигаться вперед вообще, вы просто сделают небольшую математику, чтобы переосмыслить ее.

  • Числовые индексы - лучшее долгосрочное решение, но сложнее реализовать
  • Отслеживание - проще реализовать, но вам придется снова загрязняться при удалении элементов.
  • Элементы кэширования - вы, вероятно, должны сделать это и для других двух решений, но самостоятельно, это будет быстро, пока массив не будет изменен, и в этом случае вам, вероятно, придется повторить его.

Ответ 5

Если это действительно узкое место, вы можете подумать над переосмыслением своего дизайна, чтобы сделать его численным индексом.

РЕДАКТИРОВАТЬ: или создать и поддерживать отдельный массив только с 205-ю элементами (который обновляется при вставке или что-то в этом роде).

Ответ 6

Вы не можете перемещать указатель массива, кажется, более одного раза за раз. Я лично использовал бы это:

reset($source);
$next = true;
while($next === true){
    $result[] = current($source);
    for(i=0;i<205;i++){
        $next = next($source);
    }
}

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

Ответ 7

  • Создайте два массивных массива [205] [N]
  • Загрузка данных в массив
  • Доступ к 205-му элементу для каждого N

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

Ответ 8

Вы можете использовать array_keys для работы только с ключами массива.

$keys = array_keys($array);
for ($i=0, $n=min(count($keys), 130000); $i<$n; $i += 205) {
    $result[] = $array[$keys[$i]];
}