【问题标题】:Remove element from Iterator in PHP从 PHP 中的迭代器中删除元素
【发布时间】:2014-12-16 15:33:17
【问题描述】:

我正在处理一个实现 ArrayAccess AND Iterator 的类(比如说$a),并且在循环时使用foreach,我想删除/取消设置一些元素(基于一些 if 条件)。

foreach($a as $item) {
    if(mustBeRemoved()) {
        $a->remove($item);
    }
}

现在,我的实现中的问题是,这会在foreach 循环中引发意外行为,导致它不会意识到更改,并且无论删除(或添加)元素都会继续运行和过早停止。

有没有好的/优雅的方法来解决这个问题?

【问题讨论】:

  • 使用 for 循环并倒退。
  • 让 $item 在 'foreach' 循环中成为一个引用,然后直接“取消设置”它。
  • @RyanVincent 我试过了,但我得到了一个 “一个迭代器不能通过引用与 foreach 一起使用”。抱歉,我没有提到我正在研究实现 ArrayAccess AND Iterator,而不是 ArrayIterator 的类. (感到困惑)

标签: php foreach iterator arrayaccess


【解决方案1】:

当我实现一个实现 Iterator 和 ArrayAccess 用于测试目的的类时,我没有遇到您的问题:

<?php
class a implements Iterator, ArrayAccess {
    public $items = array();
    private $index = 0;

    public function current() {
        return $this->items[$this->index];
    }

    public function key() {
        return $this->index;
    }

    public function next() {
        ++$this->index;
    }

    public function rewind() {
        $this->index = 0;
    }

    public function valid() {
        return array_key_exists($this->index, $this->items);
    }

    public function offsetExists($offset) {
        return array_key_exists($offset, $this->items);
    }

    public function offsetGet($offset) {
        return $this->items[$offset];
    }

    public function offsetSet($offset, $value) {
        $this->items[$offset] = $value;
    }

    public function offsetUnset($offset) {
        unset($this->items[$offset]);
    }

    public function remove($item) {
        foreach($this->items as $index => $itemsItem) {
            if( $itemsItem == $item) {
                unset($this->items[$index]);
                break;
            }
        }
    }
}

$a = new a();
array_map(array($a, 'offsetSet'), range(0, 100), range(0, 100));

foreach($a as $item) {
    if( $item % 2 === 0 ) {
        $a->remove($item);
    }
}

var_dump($a->items);

如果您实现了迭代器,请对其进行更改,以确保在调用“下一个”和“当前”时,删除一个项目不会使“a”实例返回不同的项目。

否则,你可以试试这个:

$mustBeRemoved = array();
foreach($a as $item) {
    if(mustBeRemoved()) {
        $mustBeRemoved []= $item;
    }
}
foreach($mustBeRemoved as $item) {
    $a->remove($item);
}
unset($mustBeRemoved);

【讨论】:

    【解决方案2】:

    当你迭代时也使用元素的键:

    foreach($a as $key => $value) {
      if(mustBeRemoved()) {
        unset($a[$key]);
      }
    }
    

    【讨论】:

    • 对不起,我混淆了实现。我的对象是 ArrayAccessIterator 的实现,而不是 ArrayIterator。它似乎对这种对象不起作用,循环过早结束。
    【解决方案3】:

    只是一个直截了当的方法:

    $tmp = array();
    foreach($a as $item) {
        if(!mustBeRemoved()) {
            $tmp[] = $item;
        }
    }
    $a = tmp;
    

    收集所有不应删除的项目并简单地创建一个新数组。最后将临时数组分配给您的旧数组。

    【讨论】:

    • 我正在考虑,但我不确定这是一个 优雅 解决方案,因为我的循环集合的内部对象相当内存贵。我想避免它,因为依赖另一个集合对性能来说不是最佳的。
    • 这种方法会破坏 Iterator 类。迭代器!=数组
    猜你喜欢
    • 2016-07-19
    • 2020-03-18
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 2012-03-28
    • 2013-11-25
    • 1970-01-01
    • 2020-05-21
    相关资源
    最近更新 更多