SQL の結果を反復可能なオブジェクトとして返す PHP フレームワークを使用しています。問題は、1 つの行を返す SQL クエリがありforeach
、最初で唯一の要素を取得するためにループを作成する必要がないことです。
では、どうすればいいですか?
これらは機能しません:
$obj->item(0)->propName;
$obj->next()->propName;
$obj[0]->propName;
何か案は?
「反復可能」とは、オブジェクトがIterator
インターフェイス$obj->current()
を実装していることを意味し、現在の要素を取得するために使用できるため、$obj->current()->propName
おそらく必要なものです。
イテレータ ポインタが移動された場合 (たとえば、foreach
ポインタをリセットしない で使用された場合)、 を$obj->rewind()
呼び出す前に を呼び出して、ポインタを最初の要素に戻すことができます$obj->current()
。
トラバースできるクラス インターフェイスは、Iteratorと IteratorAggregateの2 つだけです(その他のインターフェイスはいずれかを実装する必要があります)。
Iterator の最初の要素は、次のように取得できます。
$iterator->rewind();
if (!$iterator->valid()) {
throw new Exception('There is no any element!');
}
$firstElement = $iterator->current();
確信がある場合:
$iterator
によってトラバースされたことはありませんが、ループがorforeach
で停止されたことはありません( PHP 7 以降、 foreach はポインターに影響を与えないため、この点は無関係です) 。break
return
$iterator->next();
$iterator->rewind();
前の例からを省略できます。
の要素数がゼロでないことが確実な場合は$iterator
、条件ブロックの testing を省略することもできます$iterator->valid()
。
したがって、これらの以前の条件が保持されている場合、必要なのは次のとおりです。
$firstElement = $iterator->current();
IteratorAggergate は、実際には、Iterator または別の IteratorAggregate の単なるエンベロープです。論理的には、最後に Iterator があることを意味します。
Iterator の深さが何レベルかわかっている場合は、それを取得して、最初の例のように使用します。
$iterator = $iteratorAggregate->getIterator();
しかし、深さを理解していない場合は、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);
残念ながら、biig 配列の場合、必要な要素が 1 つだけであるにもかかわらず、すべての要素のコピーを作成する必要があるため、これは少しやり過ぎです。さらに、Iterator が無限のGeneratorである場合、メモリが不足します。
機能するソリューションが1つあります。
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();
ラップ
あらゆるタイプの iterables( array
、Iterator
、IteratorAggregate
) に対して、この関数を使用できます。
/* public static */ function first(iterable $iterable, $default = null) {
foreach ($iterable as $item){
return $item;
}
return $default;
}
iterable が空の場合に例外をスローしたい場合は、そのバージョンを使用できます。
/* public static */ function firstOrThrow(iterable $iterable, \Throwable $e) {
foreach ($iterable as $item){
return $item;
}
throw $e;
}
これは、配列と反復子オブジェクトの両方で機能し、パフォーマンスを向上させることができる反復子の最初の項目のみを計算する場合もあります。