【问题标题】:How to access first element in iterator?如何访问迭代器中的第一个元素?
【发布时间】:2013-01-05 08:42:41
【问题描述】:

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

那我该怎么做呢?

这些不起作用:

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

有什么想法吗?

【问题讨论】:

  • $obj->current()->propName 也许?
  • 放一个var_dump 并检查结果对象的结构
  • Crisp 的回答成功了。将其设为 sep 答案,我会将其标记为答案。 var_dump 给了我这样的无用垃圾:pastebin.com/GeNDbiN4
  • 嗯,已经有人给出了相同的答案,再次写它似乎有点多余,很高兴它虽然@neubert有所帮助
  • @Crisp 看起来你是第一个发布的。如果您想自己发布一个答案,我很乐意删除我的答案。

标签: php iterator


【解决方案1】:

假设“可迭代”,你的意思是对象实现了Iterator interface,你可以使用$obj->current()来检索当前元素,所以$obj->current()->propName可能就是你想要的。

如果迭代器指针已被移动(例如,如果它用于foreach,它不会重置指针),那么您可以调用$obj->rewind() 将指针设置回之前的第一个元素你打电话给$obj->current()

【讨论】:

    【解决方案2】:

    只有两个类接口可以被遍历:IteratorIteratorAggregate任何其他都必须实现其中之一)。


    迭代器

    Iterator的第一个元素可以通过如下方式获取:

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

    如果你确定:

    • $iterator 从未被foreach 遍历过,或者如果是,但循环从未被breakreturn 停止过(从 PHP 7 开始,这一点无关紧要,因为 foreach 不会影响指针)
    • 从来没有叫过$iterator->next();

    您可以省略前面示例中的$iterator->rewind();

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

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

    $firstElement = $iterator->current();
    

    迭代器聚合

    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 数组,这有点矫枉过正,因为尽管我们只需要一个,但必须创建所有元素的副本。此外,如果迭代器是无限的Generator,您将耗尽内存。

    有一个可行的解决方案:

    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

    【讨论】:

      【解决方案3】:

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

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

      当您知道您的迭代器将只返回一个“迭代”时,这非常有用。但是,如果您将拥有数千个,请注意您将首先使用所有数据填充一个数组,这可能会增加您的内存使用量,直到数组再次为空。

      【讨论】:

        【解决方案4】:

        对于任何类型的可迭代对象(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;
        }
        

        这对数组和迭代器对象都有效,并且只计算迭代器中的第一项,这在某些情况下可以提高性能。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-11-23
          • 1970-01-01
          • 1970-01-01
          • 2018-01-08
          • 2018-12-28
          • 1970-01-01
          • 2014-07-26
          • 1970-01-01
          相关资源
          最近更新 更多