divillysausages
divillysausages

Reputation: 8033

Remove key/value from map while iterating

I'm creating a map like this:

def myMap = [:]

The map is basically an object for a key and an int for a value. When I iterate over the map, I decret the value, and if it's 0, I remove it. I already tried myMap.remove(), but I get a ConcurrentModificationError - which is fair enough. So I move on to using it.remove(), which is giving me weird results.

Basically, my code is this:

myMap.each {
    it.value--;

    if( it.value <= 0 )
        it.remove();
}

Simple enough. My problem is, if I print myMap.size() before and after the remove, they're the same. If I call myMap.containsKey( key ), it gives me true, the key is still in there.

But, if I print out the map like this:

myMap.each { System.out.println( "$it.key: $it.value" ); }

I get nothing, and calling myMap.keySet() and myMap.values() return empty.

Anyone know what's going on?

Upvotes: 7

Views: 19205

Answers (2)

D&#243;nal
D&#243;nal

Reputation: 187529

This should be a bit more efficient than Tim's answer (because you only need to iterate over the map once). Unfortunately, it is also pretty verbose

def map = [2:1, 3:4]
def iterator = map.entrySet().iterator()

while (iterator.hasNext()) {

  if (iterator.next().value - 1 <= 0) {
    iterator.remove()
  }
}

// test that it worked
assert map == [3:4]

Upvotes: 12

tim_yates
tim_yates

Reputation: 171084

Can you do something like this:

myMap = myMap.each { it.value-- }.findAll { it.value > 0 }

That will subtract one from every value, then return you a new map of only those entries where the value is greater than zero.

You shouldn't call the remove method on a Map Entry, it is supposed to be a private method used internally by the Map (see line 325 for the Java 7 implementation), so you calling it yourself is getting the enclosing Map into all sorts of bother (it doesn't know that it is losing entries)

Groovy lets you call private methods, so you can do this sort of trickery behind the back of the Java classes

Edit -- Iterator method

Another way would be:

myMap.iterator().with { iterator ->
  iterator.each { entry ->
    entry.value--
    if( entry.value <= 0 ) iterator.remove()
  }
}

Upvotes: 6

Related Questions