Удалить элемент из Iterator в PHP

Я имею дело с классом, который реализует ArrayAccess AND Iterator (скажем, $a) и, проходя по ней с foreachЯ хотел бы удалить / сбросить некоторые элементы (на основе некоторых if условия).

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

Теперь проблема в моей реализации заключается в том, что это вызывает неожиданное поведение в foreach цикл, потому что он не будет знать об изменениях и будет продолжать идти и останавливаться преждевременно, независимо от того, удален ли элемент (или добавлен).

Есть ли хороший / элегантный способ решить эту проблему?

3 ответа

У меня нет вашей проблемы, когда я реализовал класс, который реализует 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" возвращать различные элементы при вызове "next" и "current".

В противном случае вы можете попробовать это:

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

Когда вы повторяете, используйте также ключ элемента:

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

Просто прямой подход:

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

Соберите все элементы, которые не должны быть удалены, и просто создайте новый массив. В конце назначьте временный массив вашему старому.

Другие вопросы по тегам