Remove key/value from map while iterating
Solution 1
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]
Solution 2
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()
}
}
divillysausages
Updated on August 03, 2022Comments
-
divillysausages almost 2 years
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 aConcurrentModificationError
- which is fair enough. So I move on to usingit.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 callmyMap.containsKey( key )
, it gives metrue
, 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()
andmyMap.values()
return empty.Anyone know what's going on?
-
divillysausages over 12 yearsah, groovy and private... do you know why
containsKey()
still returns true even if thekeySet()
is empty? do you have a way that doesn't create a new map (I'm like that :D)? -
divillysausages over 12 yearsI have it working using
for (Iterator<Map.Entry<Object, Integer>> it = myMap.entrySet().iterator(); it.hasNext();)
but, well, that's reallly long :) -
tim_yates over 12 yearsHashMap keeps an internal cache of key hashes (for containsKey calls) separately to where AbstractMap keeps its Set of keys. So pulling items out of the map in this way is going to lead to some pretty odd results ;-)
-
tim_yates over 12 yearsHehe, you beat me to the iterator method ;-)
-
divillysausages over 12 yearsyou've an extra ) in there, and it won't actually remove anything, but that's what I was looking for, thanks :)
-
Dónal over 12 years@tim_yates even a blind squirrel finds a nut every once in a while