I'm using a PHP framework that returns SQL results as iteratable objects. Problem is I have a SQL query that that returns one row and I don't want to have to create a foreach
-loop to get at the first - and only - element.
So how do I do it?
These don't work:
$obj->item(0)->propName;
$obj->next()->propName;
$obj[0]->propName;
Any ideas?
List interface provides a get() method to get the element at particular index. You can specify index as 0 to get the first element of the List.
This has nothing to do with the position of the iterator. next() picks out and remembers the element at the pointer, then advances the pointer, then returns the remembered element. You're overthinking it. next() returns the next element in the sequence, starting with the first element.
The firstElement() method of Java Vector class is used to get the first element ( at index 0 ) of the vector which is in use.
rewind() Moves the iterator to the first element in the array.
Assuming by "iterable", you mean that the object implements the Iterator
interface, you can use $obj->current()
to retrieve the current element, so $obj->current()->propName
is probably what you want.
If the iterator pointer has been moved (for example, if it was used in a foreach
, which doesn't reset the pointer), then you can call $obj->rewind()
to set the pointer back to the first element before you call $obj->current()
.
There are only two class interfaces that can be traversed: Iterator and IteratorAggregate (any other must implement one of them).
First element of Iterator can be obtained as follows:
$iterator->rewind();
if (!$iterator->valid()) {
throw new Exception('There is no any element!');
}
$firstElement = $iterator->current();
If you are sure:
$iterator
was never been traversed by foreach
, or if it was but the loop was never stopped with break
or return
(since PHP 7 this point is irrelevant because foreach does not affect pointer)$iterator->next();
you can omit the $iterator->rewind();
from the previous example.
If you are sure the count of elements in $iterator
is not zero, you can even omit the condition block testing $iterator->valid()
.
So if these previous conditions are preserved then what you need is just:
$firstElement = $iterator->current();
IteratorAggergate is actually just an envelope for an Iterator or another IteratorAggregate. Logically that means there is an Iterator at the end.
If you know how many levels deep the Iterator is, just grab it and use as in the very first example:
$iterator = $iteratorAggregate->getIterator();
But if you don't now the deepness, you may use a solution which works also for an Iterator:
$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);
Unfortunately in case of biig array this is a little overkill because copy of all elements must be created despite we need just one. Besides if the Iterator is an infinite Generator you will run out of memory.
There is one solution that works:
while ($iterator instanceof \IteratorAggregate) {
$iterator = $iterator->getIterator();
}
// $iterator now contains instance of Iterator
I made a simple benchmark for an array of 10000 members and this solution was almost 6 times faster.
So, the universal solution which will work for all cases is:
while ($iterator instanceof \IteratorAggregate) {
$iterator = $iterator->getIterator();
}
$iterator->rewind();
if (!$iterator->valid()) {
throw new Exception('There is no any element!');
}
$firstElement = $iterator->current();
LLAP
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With