diff --git a/base/dict.jl b/base/dict.jl index ebb39d6a91959..90e502de04d62 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -241,14 +241,27 @@ function merge(d::Associative, others::Associative...) end function filter!(f, d::Associative) + badkeys = Array(keytype(d), 0) for (k,v) in d - if !f(k,v) - delete!(d,k) - end + # don't delete!(d, k) here, since associative types + # may not support mutation during iteration + f(k,v) || push!(badkeys, k) + end + for k in badkeys + delete!(d, k) end return d end -filter(f, d::Associative) = filter!(f,copy(d)) +function filter(f, d::Associative) + # don't just do filter!(f, copy(d)): avoid making a whole copy of d + df = similar(d) + for (k,v) in d + if f(k,v) + df[k] = v + end + end + return df +end eltype{K,V}(::Type{Associative{K,V}}) = Pair{K,V} @@ -866,6 +879,16 @@ function next{K,V}(t::WeakKeyDict{K,V}, i) end length(t::WeakKeyDict) = length(t.ht) +# For these Associative types, it is safe to implement filter! +# by deleting keys during iteration. +function filter!(f, d::Union{ObjectIdDict,Dict,WeakKeyDict}) + for (k,v) in d + if !f(k,v) + delete!(d,k) + end + end + return d + end immutable ImmutableDict{K, V} <: Associative{K,V} parent::ImmutableDict{K, V} diff --git a/test/dict.jl b/test/dict.jl index 135729d6821a1..af6e815478b4e 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -439,3 +439,10 @@ let d = ImmutableDict{UTF8String, UTF8String}(), @test_throws KeyError d[k1] @test_throws KeyError d1["key2"] end + +# filtering +let d = Dict(zip(1:1000,1:1000)), f = (k,v) -> iseven(k) + @test filter(f, d) == filter!(f, copy(d)) == + invoke(filter!, (Function, Associative), f, copy(d)) == + Dict(zip(2:2:1000, 2:2:1000)) +end