Я имею дело с классом, который реализует ArrayAccess А ТАКЖЕ Итератор (скажем $a
) и, проходя по ней с foreach
Я хотел бы удалить / сбросить некоторые элементы (на основе некоторых if
условия).
foreach($a as $item) {
if(mustBeRemoved()) {
$a->remove($item);
}
}
Теперь проблема в моей реализации заключается в том, что это вызывает неожиданное поведение в foreach
цикл, потому что он не будет знать об изменениях и будет продолжать идти и останавливаться преждевременно, независимо от того, удален ли элемент (или добавлен).
Есть ли хороший / элегантный способ решить эту проблему?
Просто прямой подход:
$tmp = array();
foreach($a as $item) {
if(!mustBeRemoved()) {
$tmp[] = $item;
}
}
$a = tmp;
Соберите все элементы, которые не должны быть удалены, и просто создайте новый массив. В конце назначьте временный массив вашему старому.
Когда вы повторяете, используйте также ключ элемента:
foreach($a as $key => $value) {
if(mustBeRemoved()) {
unset($a[$key]);
}
}
У меня нет вашей проблемы, когда я реализовал класс, который реализует 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);