Skip to content

Commit

Permalink
Return old => new pair
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf committed Nov 13, 2021
1 parent b1dac99 commit 28900ad
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 21 deletions.
33 changes: 20 additions & 13 deletions base/abstractdict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -528,18 +528,22 @@ function hash(a::AbstractDict, h::UInt)
end

"""
modify!(f, d::AbstractDict{K, V}, key)
modify!(f, d::AbstractDict{K, V}, key) -> (old => new)
Lookup and then update, insert or delete in one go without re-computing the hash.
Return a pair of `old::Union{Some,Nothing}` and `new::::Union{Some,Nothing}`.
`f` is a callable object that must take a single `Union{Some{V}, Nothing}` argument and return
a `Union{T, Some{T}, Nothing}` value, where `T` is a type [`convert`](@ref)-able to the value type
`V`. The value `Some(d[key])` is passed to `f` if `haskey(d, key)`; otherwise `nothing`
is passed. If `f` returns `nothing`, corresponding entry in the dictionary `d` is removed.
If `f` returns non-`nothing` value `x`, `key => something(x)` is inserted or updated in `d`
(equivalent to `d[key] = something(x)` but more efficient).
`f` is a callable object that must take a single `old::Union{Some, Nothing}` argument and
return a `new::Union{T, Some{T}, Nothing}` value, where `T` is a type [`convert`](@ref)-able
to the value type `V`. The value `Some(d[key])` is passed to `f` if `haskey(d, key)`;
otherwise `nothing` is passed. If `f` returns `nothing`, corresponding entry in the
dictionary `d` is removed. If `f` returns non-`nothing` value `x`, `key => something(x)`
is inserted or updated in `d` (equivalent to `d[key] = something(x)` but more efficient).
`modify!` returns whatever `f` returns as-is.
Whether `Some{V}(value)` or `Some{typeof(value)}(value)` is returned is an implementation
defined behavior. The callback function `f` must use `old === nothing` or `old isa Some`
instead of `old isa Some{valtype(d)}` unless the type of the dictionary `d` is known to
define a certain behavior.
# Examples
```jldoctest
Expand All @@ -548,7 +552,7 @@ julia> dict = Dict("a" => 1);
julia> modify!(dict, "a") do val
Some(val === nothing ? 1 : something(val) + 1)
end
Some(2)
Some(1) => Some(2)
julia> dict
Dict{String,Int64} with 1 entry:
Expand All @@ -559,30 +563,33 @@ julia> dict = Dict();
julia> modify!(dict, "a") do val
Some(something(val, 0) + 1)
end
Some(1)
nothing => Some(1)
julia> dict
Dict{Any,Any} with 1 entry:
"a" => 1
julia> modify!(_ -> nothing, dict, "a")
Some("a") => nothing
julia> dict
Dict{Any,Any} with 0 entries
```
"""
function modify!(f, dict::AbstractDict{K,V}, key) where {K, V}
if haskey(dict, key)
val = f(Some{V}(dict[key]))
old = Some{V}(dict[key])
val = f(old)
else
val = f(nothing)
old = nothing
val = f(old)
end
if val === nothing
delete!(dict, key)
else
dict[key] = something(val)
end
return val
return old => val
end

function getindex(t::AbstractDict, key)
Expand Down
8 changes: 5 additions & 3 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,11 @@ function modify!(f, h::Dict{K, V}, key0) where {K, V}
age0 = h.age
if idx > 0
@inbounds vold = h.vals[idx]
vnew = f(Some{V}(vold))
vold = Some{V}(vold)
vnew = f(vold)
else
vnew = f(nothing)
vold = nothing
vnew = f(vold)
end
if h.age != age0
idx = ht_keyindex2!(h, key)
Expand All @@ -428,7 +430,7 @@ function modify!(f, h::Dict{K, V}, key0) where {K, V}
@inbounds _setindex!(h, something(vnew), key, -idx)
end
end
return vnew
return vold => vnew
end

"""
Expand Down
10 changes: 5 additions & 5 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,7 @@ end

@test modify!(dict, "a") do val
Some(val === nothing ? 1 : something(val) + 1)
end == Some(2)
end == (Some(1) => Some(2))

@test Dict(dict) == Dict("a" => 2)
end
Expand All @@ -1228,20 +1228,20 @@ end

@test modify!(dict, "a") do val
Some(val === nothing ? 1 : something(val) + 1)
end == Some(1)
end == (nothing => Some(1))

@test Dict(dict) == Dict("a" => 1)
end

@testset "delete" begin
dict = constructor(Dict("a" => 1))
@test modify!(_ -> nothing, dict, "a") === nothing
@test modify!(_ -> nothing, dict, "a") == (Some(1) => nothing)
@test Dict(dict) == Dict()
end

@testset "no-op" begin
dict = constructor(Dict("a" => 1))
@test modify!(_ -> nothing, dict, "b") === nothing
@test modify!(_ -> nothing, dict, "b") == (nothing => nothing)
@test Dict(dict) == Dict("a" => 1)
end

Expand All @@ -1251,7 +1251,7 @@ end
@test modify!(dict, "a") do val
dict["a"] = 0
Some(val === nothing ? 1 : something(val) + 1)
end == Some(1)
end == (nothing => Some(1))

@test Dict(dict) == Dict("a" => 1)
end
Expand Down

0 comments on commit 28900ad

Please sign in to comment.