Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to delete elements in a Set while iterating with for..of?

Is it specified that you can delete any element in an instance of Set while iterating using for..of and that

  • you won't iterate more than once on an element
  • you won't miss any other element that was in the set at the start of the iteration other than the ones you remove

?

like image 933
Denys Séguret Avatar asked Feb 03 '15 19:02

Denys Séguret


People also ask

How do you remove from set while iterating?

Using an iterator We can use the remove() method provided by the Iterator interface that removes the latest element returned by the iterator. Please note we should not modify the set after the iterator is created (except through the iterator's own remove method); otherwise, a ConcurrentModificationException is thrown.

Can we remove element from list while iterating?

ArrayList provides the remove() methods, like remove (int index) and remove (Object element), you cannot use them to remove items while iterating over ArrayList in Java because they will throw ConcurrentModificationException if called during iteration.

Can we add elements while iterating?

You can't modify a Collection while iterating over it using an Iterator , except for Iterator. remove() . This will work except when the list starts iteration empty, in which case there will be no previous element. If that's a problem, you'll have to maintain a flag of some sort to indicate this edge case.


1 Answers

Yes, it is perfectly fine to add elements and remove elements to a set while iterating it. This use case was considered and is supported in JavaScript 2015 (ES6). It will leave it in a consistent state. Note this also applies to itearting with forEach.

Intuitively:

The set iteration algorithm basically looks something like this:

Set position to 0 While position < calculateLength() // note it's calculated on each iteration     return the element at set.entryList[position] 

Addition just looks something like this:

If element not in set    Add element to the _end_ of the set 

So it does not interfere with existing iterations - they will iterate it.

Deletion looks something like this:

Replace all elements with are equal to `element` with a special empty value 

Replacing it with an empty value rather than deleting it ensures it will not mess up with iterators' positions.


Formally

Addition

Here is the relevant part of the specification from %SetIteratorPrototype%.next:

Repeat while index is less than the total number of elements of entries. The number of elements must be redetermined each time this method is evaluated.

The set iterator proceeds to iterate the entries one by one.

From Set.prototype.add:

Append value as the last element of entries.

This ensures that when adding elements to the list it will be iterated before the iteration completes since it always gets a new slot in the entries list. Thus this will work as the spec mandates.

As for deletion:

Replace the element of entries whose value is e with an element whose value is empty.

Replacing it with an empty element rather than removing it ensures that the iteration order of existing iterators will not get out or order and they will continue iterating the set correctly.

With code

Here is a short code snippet that demonstrates this ability

var set = new Set([1]); for(let item of set){    if(item < 10) set.add(item+1);    console.log(item); } 

Which logs the numbers 1 to 10. Here is a version not using for... of you can run in your browser today:

var set = new Set([1]);  for (var _i = set[Symbol.iterator](), next; !(next = _i.next()).done;) {     var item = next.value;     if (item < 10) set.add(item + 1);     document.body.innerHTML += " " + item;  }
like image 170
Benjamin Gruenbaum Avatar answered Oct 01 '22 22:10

Benjamin Gruenbaum