Как получить доступ к первому элементу в итераторе? - программирование
Подтвердить что ты не робот

Как получить доступ к первому элементу в итераторе?

Я использую фреймворк PHP, который возвращает результаты SQL как итерационные объекты. Проблема в том, что у меня есть SQL-запрос, который возвращает одну строку, и я не хочу создавать foreach -loop, чтобы получить первый и единственный элемент.

Итак, как мне это сделать?

Они не работают:

$obj->item(0)->propName;
$obj->next()->propName;
$obj[0]->propName;

Любые идеи?

4b9b3361

Ответ 1

Предполагая, что "итерируемый" означает, что объект реализует Iterator интерфейс, вы можете использовать $obj->current() для извлечения текущего элемент, поэтому $obj->current()->propName - это то, что вы хотите.

Если указатель итератора был перемещен (например, если он использовался в foreach, который не содержит reset указатель), вы можете вызвать $obj->rewind(), чтобы вернуть указатель к первому, прежде чем вы вызовете $obj->current().

Ответ 2

Есть только два интерфейса классов, которые можно пройти: Iterator и IteratorAggregate (любой другой должен реализовывать один из них).


Итератор

Первый элемент Iterator может быть получен следующим образом:

$iterator->rewind();
if (!$iterator->valid()) {
    throw new Exception('There is no any element!');
}
$firstElement = $iterator->current();

Если вы уверены:

  • $iterator никогда не проходился через foreach или, если это было так, но цикл никогда не останавливался с использованием break или return (начиная с PHP 7 эта точка не имеет значения, поскольку foreach не влияет на указатель)
  • никогда не вызывался $iterator->next();

вы можете опустить $iterator->rewind(); из предыдущего примера.

Если вы уверены, что количество элементов в $iterator не равно нулю, вы можете даже опустить проверку блока условий $iterator->valid().

Так что, если эти предыдущие условия сохраняются, то вам просто нужно:

$firstElement = $iterator->current();

IteratorAggregate

IteratorAggergate на самом деле является просто конвертом для Iterator или другого IteratorAggregate. Логически это означает, что в конце есть Итератор.

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

$iterator = $iteratorAggregate->getIterator();

Но если у вас нет глубины, вы можете использовать решение, которое работает и для Итератора:

$array = iterator_to_array($iteratorAggregate);
// $array is an ordinary array now
if (count($array) === 0) {
    throw new Exception('There is no any element!');
}
$firstElement = reset($array);

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

Есть одно решение, которое работает:

while ($iterator instanceof \IteratorAggregate) {
    $iterator = $iterator->getIterator();
}
// $iterator now contains instance of Iterator

Я сделал простой тест для массива из 10000 членов, и это решение было почти в 6 раз быстрее.

Итак, универсальное решение, которое будет работать для всех случаев:

while ($iterator instanceof \IteratorAggregate) {
    $iterator = $iterator->getIterator();
}
$iterator->rewind();
if (!$iterator->valid()) {
    throw new Exception('There is no any element!');
}
$firstElement = $iterator->current();

LLAP