如何访问迭代器中的第一个元素?

时间:2013-01-22 06:55:13

标签: php iterator

我正在使用一个PHP框架,它将SQL结果作为可迭代对象返回。问题是我有一个返回一行的SQL查询,我不想创建一个foreach - 循环来获取第一个 - 也是唯一 - 元素。

那我该怎么办?

这些不起作用:

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

有什么想法吗?

4 个答案:

答案 0 :(得分:20)

假设“iterable”表示对象实现了Iterator interface,您可以使用$obj->current()来检索当前元素,因此$obj->current()->propName可能是您想要的。

如果已移动迭代器指针(例如,如果它在foreach中使用,但未重置指针),则可以调用$obj->rewind()将指针设置回在致电$obj->current()之前的第一个元素。

答案 1 :(得分:10)

只有两个类接口可以遍历:IteratorIteratorAggregate任何其他接口必须实现其中一个)。< / p>

迭代

迭代器的第一个元素可以如下获得:

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

如果您确定:

  • $iterator从未遍历过foreach,或者是break,但是从未使用return$iterator->next();停止循环(自PHP 7起point是无关紧要的,因为foreach不会影响指针
  • 从未调用$iterator->rewind();

您可以省略上一个示例中的$iterator

如果您确定$iterator->valid()中的元素数量为零,您甚至可以省略条件块测试$firstElement = $iterator->current();

因此,如果保留这些先前的条件,那么您需要的只是:

$iterator = $iteratorAggregate->getIterator();

IteratorAggregate

IteratorAggergate实际上只是Iterator或其他IteratorAggregate的信封。从逻辑上讲,这意味着最后有一个迭代器。

如果你知道迭代器的深度是多少,只需抓住它并使用第一个例子:

$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);

但如果你现在没有深度,你可以使用一个也适用于迭代器的解决方案:

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

不幸的是如果是biig数组,这有点矫枉过正,因为必须创建所有元素的副本,尽管我们只需要一个。此外,如果迭代器是无限的Generator,你将会耗尽内存。

有一种解决方案可行:

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

我为10000个成员的数组做了一个简单的基准测试,这个解决方案快了近6倍。

因此,适用于所有情况的通用解决方案是:

@IntDef

LLAP

答案 2 :(得分:0)

执行此操作的另一种方法,我个人认为不太冗长,是先将迭代器转换为数组,然后检查该数组是否为空。

$result = iterator_to_array($iterator, true);
if(!empty($result)){
   $user = end($result)->id;
}

当您知道迭代器仅返回一个“迭代”时非常有用。但是,如果要有成千上万个,请小心,首先要用所有数据填充数组,这可能会增加内存使用率,直到该数组再次为空。

答案 3 :(得分:0)

对于任何类型的可迭代对象(arrayIteratorIteratorAggregate),您可以使用以下功能:

/* 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;
}

这既适用于数组又适用于迭代器对象,并且在某些情况下也仅计算迭代器中的第一项,可以提高性能。