From 86e325f2fcd086739ac986fc100d6e1ee64e9876 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Thu, 28 Feb 2019 12:46:23 -0500 Subject: [PATCH 01/18] Initial version of mapDict --- base/dict.jl | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/base/dict.jl b/base/dict.jl index a8b85631586d1..9df3ad1fdfa08 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -686,6 +686,90 @@ end filter!(f, d::Dict) = filter_in_one_pass!(f, d) + +""" + mapdict!(f, dict) -> dict +Takes the function f(value) and transforms the stored values with essentially value=f(value). + +If the value returned by f(value) is of a differnet type than the input function used +must take the for dict = map(f,dict) which will mutate dict in a memory efficent manner. + +# Examples +```jldoctest +julia> D=Dict(:a=>1,:b=>2) +Dict{Symbol,Int64} with 2 entries: + :a => 1 + :b => 2 + +julia> mapdict!(v->v-1,D) +Dict{Symbol,Int64} with 2 entries: + :a => 0 + :b => 1 + +julia> D=mapdict!(v->Float64(v),D) +Dict{Symbol,Float64} with 2 entries: + :a => 0.0 + :b => 1.0 + ``` +""" +function mapdict!(f, d::Dict) + isempty(d) && return d + f_return_type = typeof(f(first(d)[2])) + _mapdict!(f_return_type,f,d) +end + +""" + mapdict(f, dict) -> dict +Takes the function f(value) and returns a new Dict with the values transfor with new_value=f(value). +""" +function mapdict(f, d::Dict) + isempty(d) && return d + f_return_type = typeof(f(first(d)[2])) + return _mapdict(f_return_type,f,d) +end + +# This is the typesafe version +function _mapdict!(::Type{V}, f, d::Dict{K,V}) where V where K + return _mapdict_apply!(f, d, d.vals) +end + +function _mapdict(::Type{V}, f, d::Dict{K,V}) where V where K + new_d=Dict(d) + return _mapdict_apply!(f, new_d, new_d.vals) +end + +# Mutating verion +function _mapdict!(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew + L = length(d.vals) + new_vals = Vector{Vnew}(undef,L) + + new_d = Dict{K, Vnew}(d.slots, d.keys, new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) + _mapdict_apply!(f, new_d, d.vals) + return new_d +end + +function _mapdict(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew + L = length(d.vals) + new_vals = Vector{Vnew}(undef,L) + + new_d = Dict{K, Vnew}(copy(d.slots), copy(d.keys), new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) + + _mapdict_apply!(f, new_d, d.vals) + return new_d +end + +@inline function _mapdict_apply!(f,d::Dict{K,V}, old_vals::Vector{Vold}) where V where K where Vold + L = length(d.vals) + i = d.idxfloor + vals = d.vals + @inbounds while i < L + isslotfilled(d, i) && (vals[i] = f(old_vals[i])) + i += 1 + end + return d +end + + struct ImmutableDict{K,V} <: AbstractDict{K,V} parent::ImmutableDict{K,V} key::K From b97922b3c923c01ed996cda7464917c623e1c6b4 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Thu, 28 Feb 2019 14:42:41 -0500 Subject: [PATCH 02/18] mapDict will now broaden its type if needed --- base/dict.jl | 66 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 9df3ad1fdfa08..c6a4826a6f862 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -712,27 +712,35 @@ Dict{Symbol,Float64} with 2 entries: :b => 1.0 ``` """ -function mapdict!(f, d::Dict) +function mapdict!(f, d::Dict{K,V}) where K where V isempty(d) && return d f_return_type = typeof(f(first(d)[2])) - _mapdict!(f_return_type,f,d) + ret = _mapdict!(f_return_type,f,d) # This function will return a DataType if it isn't typestable + ret isa Dict && return ret# If ret is a Dict the function complete in a typesafe way + # If it didn't we have to broaden the type + ret= _mapdict_unioning!(f,d,ret) + return ret end """ mapdict(f, dict) -> dict Takes the function f(value) and returns a new Dict with the values transfor with new_value=f(value). """ -function mapdict(f, d::Dict) +function mapdict(f, d::Dict) where K where V isempty(d) && return d f_return_type = typeof(f(first(d)[2])) - return _mapdict(f_return_type,f,d) + + ret = _mapdict(f_return_type,f,d) + ret isa Dict && return ret # If ret is a Dict the function complete in a typesafe way + # If it didn't we have to broaden the type + ret= _mapdict_unioning!(f,d,ret) + return ret end # This is the typesafe version function _mapdict!(::Type{V}, f, d::Dict{K,V}) where V where K - return _mapdict_apply!(f, d, d.vals) + return _mapdict_apply!(V,f, d, d.vals) end - function _mapdict(::Type{V}, f, d::Dict{K,V}) where V where K new_d=Dict(d) return _mapdict_apply!(f, new_d, new_d.vals) @@ -744,32 +752,62 @@ function _mapdict!(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew new_vals = Vector{Vnew}(undef,L) new_d = Dict{K, Vnew}(d.slots, d.keys, new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - _mapdict_apply!(f, new_d, d.vals) - return new_d + return _mapdict_apply!(Vnew,f, new_d, d.vals) end - function _mapdict(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew L = length(d.vals) new_vals = Vector{Vnew}(undef,L) new_d = Dict{K, Vnew}(copy(d.slots), copy(d.keys), new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - _mapdict_apply!(f, new_d, d.vals) + _mapdict_apply!(Vnew,f, new_d, d.vals) return new_d end -@inline function _mapdict_apply!(f,d::Dict{K,V}, old_vals::Vector{Vold}) where V where K where Vold +@inline function _mapdict_apply!(f,d::Dict{K,V}, old_vals::Vector{Vold},i_start=d.idxfloor) where V where K where Vold L = length(d.vals) - i = d.idxfloor + type_test(v)=false + if isconcretetype(V) + @inline type_test(v)= typeof(v)===V + else + @inline type_test(v)= typeof(v) <: V + end + + i = i_start vals = d.vals @inbounds while i < L - isslotfilled(d, i) && (vals[i] = f(old_vals[i])) - i += 1 + (Base.isslotfilled(d, i) || (i+=1; continue )) && #This first line is to check the slot and iterate if it isn't used + (new_val = f(old_vals[i]); true) && type_test(new_val) && # This line is getting the new val checking the type + (vals[i] = new_val; true) && (i += 1; true) && continue #this line is finishing the iteration + # If anything fails above we return a tuple with the new type,dict, and the current index so we don't reapply the function + return (typeof(new_val),d,i) end return d end +# This function will keep broadening the new type for 5 iterations then assume the output should be any +function _mapdict_unioning!(f, old_d::Dict{K,Vold},ret::Tuple{DataType,Dict{K,Vnew},Int}) where K where Vnew where Vold + num_types_before_any = 5 + type_counter=0 + union_type=Vnew + d=ret[2] + while ret isa Tuple && type_counter <= num_types_before_any + type_counter+=1 + new_type=ret[1] + union_type=type_counter < num_types_before_any ? Union{union_type,new_type} : Any + d = Dict{K, union_type}(d.slots, d.keys, convert(Vector{union_type}, d.vals), d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) + ret=_mapdict_apply!(union_type,f, d, old_d.vals,ret[3]) + end + if ret isa Dict + return ret + else + error("mapdict could not converge on function output type") + end + +end + + struct ImmutableDict{K,V} <: AbstractDict{K,V} parent::ImmutableDict{K,V} key::K From 9c2189ffa17f52f1126336c1a9939e283617fc81 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Thu, 28 Feb 2019 15:33:44 -0500 Subject: [PATCH 03/18] Cleaned up version --- base/dict.jl | 54 ++++++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index c6a4826a6f862..911e479799f0a 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -715,55 +715,43 @@ Dict{Symbol,Float64} with 2 entries: function mapdict!(f, d::Dict{K,V}) where K where V isempty(d) && return d f_return_type = typeof(f(first(d)[2])) - ret = _mapdict!(f_return_type,f,d) # This function will return a DataType if it isn't typestable + if f_return_type == V + new_d=d + else + L=length(d.vals) + new_vals = Vector{f_return_type}(undef,L) + new_d = Dict{K, f_return_type}(d.slots, d.keys, new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) + end + ret = _mapdict_apply!(f, new_d,d.vals) # This function will return a Tuple if it isn't typestable ret isa Dict && return ret# If ret is a Dict the function complete in a typesafe way # If it didn't we have to broaden the type ret= _mapdict_unioning!(f,d,ret) return ret end + """ mapdict(f, dict) -> dict Takes the function f(value) and returns a new Dict with the values transfor with new_value=f(value). """ -function mapdict(f, d::Dict) where K where V +function mapdict(f, d::Dict{K,V}) where K where V isempty(d) && return d - f_return_type = typeof(f(first(d)[2])) - ret = _mapdict(f_return_type,f,d) + f_return_type = typeof(f(first(d)[2])) + if f_return_type == V + new_d=Dict(d) + else + L=length(d.vals) + new_vals = Vector{f_return_type}(undef,L) + new_d = Dict{K, f_return_type}(copy(d.slots), copy(d.keys), new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) + end + ret = _mapdict_apply!(f, new_d,d.vals) ret isa Dict && return ret # If ret is a Dict the function complete in a typesafe way # If it didn't we have to broaden the type ret= _mapdict_unioning!(f,d,ret) return ret end -# This is the typesafe version -function _mapdict!(::Type{V}, f, d::Dict{K,V}) where V where K - return _mapdict_apply!(V,f, d, d.vals) -end -function _mapdict(::Type{V}, f, d::Dict{K,V}) where V where K - new_d=Dict(d) - return _mapdict_apply!(f, new_d, new_d.vals) -end - -# Mutating verion -function _mapdict!(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew - L = length(d.vals) - new_vals = Vector{Vnew}(undef,L) - - new_d = Dict{K, Vnew}(d.slots, d.keys, new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - return _mapdict_apply!(Vnew,f, new_d, d.vals) -end -function _mapdict(::Type{Vnew}, f, d::Dict{K,V}) where V where K where Vnew - L = length(d.vals) - new_vals = Vector{Vnew}(undef,L) - - new_d = Dict{K, Vnew}(copy(d.slots), copy(d.keys), new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - - _mapdict_apply!(Vnew,f, new_d, d.vals) - return new_d -end - @inline function _mapdict_apply!(f,d::Dict{K,V}, old_vals::Vector{Vold},i_start=d.idxfloor) where V where K where Vold L = length(d.vals) type_test(v)=false @@ -797,14 +785,14 @@ function _mapdict_unioning!(f, old_d::Dict{K,Vold},ret::Tuple{DataType,Dict{K,Vn new_type=ret[1] union_type=type_counter < num_types_before_any ? Union{union_type,new_type} : Any d = Dict{K, union_type}(d.slots, d.keys, convert(Vector{union_type}, d.vals), d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - ret=_mapdict_apply!(union_type,f, d, old_d.vals,ret[3]) + ret=_mapdict_apply!(f, d, old_d.vals,ret[3]) end if ret isa Dict return ret else + #Function should never get here because Any should catch everything error("mapdict could not converge on function output type") end - end From 5894be5ec257a79b8578e9acad0a66a143617663 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Fri, 1 Mar 2019 08:02:00 -0500 Subject: [PATCH 04/18] Export mapdict & mapdict --- base/exports.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/exports.jl b/base/exports.jl index 8a26eb8fdc73f..a351902d26e83 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -506,6 +506,8 @@ export length, map!, map, + mapdict!, + mapdict, mapfoldl, mapfoldr, mapreduce, From 140d3e7690955ebf6ff710cfe8ede7c78760a1d7 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Fri, 1 Mar 2019 08:26:42 -0500 Subject: [PATCH 05/18] Added example for mapdict --- base/dict.jl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/base/dict.jl b/base/dict.jl index 911e479799f0a..1c2e1ddad7843 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -733,6 +733,33 @@ end """ mapdict(f, dict) -> dict Takes the function f(value) and returns a new Dict with the values transfor with new_value=f(value). + +# Examples +```jldoctest +julia> D=Dict(:a=>1,:b=>2,:c=>3) +Dict{Symbol,Int64} with 3 entries: + :a => 1 + :b => 2 + :c => 3 + +julia> E=mapdict(v->v-1,D) +Dict{Symbol,Int64} with 3 entries: + :a => 0 + :b => 1 + :c => 2 + +julia> D +Dict{Symbol,Int64} with 3 entries: + :a => 1 + :b => 2 + :c => 3 + +julia> E=mapdict(v->float(v-1),D) +Dict{Symbol,Float64} with 3 entries: + :a => 0.0 + :b => 1.0 + :c => 2.0 + ``` """ function mapdict(f, d::Dict{K,V}) where K where V isempty(d) && return d From 0ee4a20ca55019185b6be747941998d7bba12e8b Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Wed, 6 Mar 2019 16:52:03 -0500 Subject: [PATCH 06/18] changed mapdict! to map!(f,values(dict)) --- base/dict.jl | 156 +++++++++++++++++++++------------------------------ 1 file changed, 64 insertions(+), 92 deletions(-) diff --git a/base/dict.jl b/base/dict.jl index 1c2e1ddad7843..3016a49481978 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -687,12 +687,15 @@ end filter!(f, d::Dict) = filter_in_one_pass!(f, d) + + + + + """ - mapdict!(f, dict) -> dict -Takes the function f(value) and transforms the stored values with essentially value=f(value). + map!(f, values(dict)) +Takes the function f(value) and transforms values stored in the dict with the transformation value=f(value). -If the value returned by f(value) is of a differnet type than the input function used -must take the for dict = map(f,dict) which will mutate dict in a memory efficent manner. # Examples ```jldoctest @@ -701,128 +704,97 @@ Dict{Symbol,Int64} with 2 entries: :a => 1 :b => 2 -julia> mapdict!(v->v-1,D) +julia> map!(v->v-1,values(D)) Dict{Symbol,Int64} with 2 entries: :a => 0 :b => 1 -julia> D=mapdict!(v->Float64(v),D) -Dict{Symbol,Float64} with 2 entries: - :a => 0.0 - :b => 1.0 ``` """ -function mapdict!(f, d::Dict{K,V}) where K where V - isempty(d) && return d - f_return_type = typeof(f(first(d)[2])) - if f_return_type == V - new_d=d - else - L=length(d.vals) - new_vals = Vector{f_return_type}(undef,L) - new_d = Dict{K, f_return_type}(d.slots, d.keys, new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - end - ret = _mapdict_apply!(f, new_d,d.vals) # This function will return a Tuple if it isn't typestable - ret isa Dict && return ret# If ret is a Dict the function complete in a typesafe way - # If it didn't we have to broaden the type - ret= _mapdict_unioning!(f,d,ret) - return ret -end +Base.map!(f, iter::Base.ValueIterator{Dict{K,V}}) where {K, V}= (_map_to!(Val(false), f, iter.dict.vals, iter, iter.dict.idxfloor); iter.dict) -""" - mapdict(f, dict) -> dict -Takes the function f(value) and returns a new Dict with the values transfor with new_value=f(value). +"""map(f, values(dict), create_new_dict=true, value_type_change::Bool = true) -> dict +Takes the function f(value) and creates a copy of the dict with the values transformed by value=f(value). +Setting value_type_change=false will ensure that the values type of the input and output dicts are the same. +create_new_dict=false will cause the map method to fallback to returning an array. # Examples ```jldoctest -julia> D=Dict(:a=>1,:b=>2,:c=>3) +julia> D=Dict(:a=>1, :b=>2, :c=>3) Dict{Symbol,Int64} with 3 entries: :a => 1 :b => 2 :c => 3 -julia> E=mapdict(v->v-1,D) -Dict{Symbol,Int64} with 3 entries: - :a => 0 - :b => 1 - :c => 2 +julia> E=map(v->isodd(v) ? Float64(v) : string(v), values(D), create_new_dict=true) +Dict{Symbol,Union{Float64, String}} with 3 entries: + :a => 1.0 + :b => "2" + :c => 3.0 + +julia> E=map(v->isodd(v) ? Int64(v) : UInt32(v), values(D), create_new_dict=true) +Dict{Symbol,Union{Int64, UInt32}} with 3 entries: + :a => 1 + :b => 0x00000002 + :c => 3 -julia> D +julia> E=map(v->isodd(v) ? Int64(v) : UInt32(v), values(D), create_new_dict=true, value_type_change = false) Dict{Symbol,Int64} with 3 entries: :a => 1 :b => 2 :c => 3 - -julia> E=mapdict(v->float(v-1),D) -Dict{Symbol,Float64} with 3 entries: - :a => 0.0 - :b => 1.0 - :c => 2.0 ``` """ -function mapdict(f, d::Dict{K,V}) where K where V - isempty(d) && return d +function Base.map(f,iter::Base.ValueIterator{Dict{K,V}}; create_new_dict::Bool, value_type_change::Bool=true) where {K, V} + if create_new_dict == false + return Base.map(f, iter::Base.ValueIterator{Dict{K,V}}) + end + d=iter.dict + if value_type_change + gen = (f(v) for v in iter) + et = Base.@default_eltype(gen) - f_return_type = typeof(f(first(d)[2])) - if f_return_type == V - new_d=Dict(d) else - L=length(d.vals) - new_vals = Vector{f_return_type}(undef,L) - new_d = Dict{K, f_return_type}(copy(d.slots), copy(d.keys), new_vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - end - ret = _mapdict_apply!(f, new_d,d.vals) - ret isa Dict && return ret # If ret is a Dict the function complete in a typesafe way - # If it didn't we have to broaden the type - ret= _mapdict_unioning!(f,d,ret) - return ret -end - -@inline function _mapdict_apply!(f,d::Dict{K,V}, old_vals::Vector{Vold},i_start=d.idxfloor) where V where K where Vold - L = length(d.vals) - type_test(v)=false - if isconcretetype(V) - @inline type_test(v)= typeof(v)===V - else - @inline type_test(v)= typeof(v) <: V + et = V end + dest=Vector{et}(undef,length(d.vals)) + # The reasignment is required to allow for type widening + dest = _map_to!(Val(value_type_change), f, dest, iter, d.idxfloor) + return Dict{K, eltype(dest)}(copy(d.slots), copy(d.keys), dest, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - i = i_start - vals = d.vals - @inbounds while i < L - (Base.isslotfilled(d, i) || (i+=1; continue )) && #This first line is to check the slot and iterate if it isn't used - (new_val = f(old_vals[i]); true) && type_test(new_val) && # This line is getting the new val checking the type - (vals[i] = new_val; true) && (i += 1; true) && continue #this line is finishing the iteration - # If anything fails above we return a tuple with the new type,dict, and the current index so we don't reapply the function - return (typeof(new_val),d,i) - end - return d end -# This function will keep broadening the new type for 5 iterations then assume the output should be any -function _mapdict_unioning!(f, old_d::Dict{K,Vold},ret::Tuple{DataType,Dict{K,Vnew},Int}) where K where Vnew where Vold - num_types_before_any = 5 - type_counter=0 - union_type=Vnew - d=ret[2] - while ret isa Tuple && type_counter <= num_types_before_any - type_counter+=1 - new_type=ret[1] - union_type=type_counter < num_types_before_any ? Union{union_type,new_type} : Any - d = Dict{K, union_type}(d.slots, d.keys, convert(Vector{union_type}, d.vals), d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - ret=_mapdict_apply!(f, d, old_d.vals,ret[3]) - end - if ret isa Dict - return ret - else - #Function should never get here because Any should catch everything - error("mapdict could not converge on function output type") + +function _map_to!(widen::Val{W}, f,dest::AbstractArray{T}, iter::Base.ValueIterator{Dict{K,V}}, ind) where {T, K, V, W} + # changing widen to a bool causes a 10x slowdown + vals=iter.dict.vals + inplace=dest===vals + @assert( !(W && inplace) ) # This asserts that you can widen or have inplace maping but not both + + len = length(vals) + i = ind + + @inbounds while i < len + if !(Base.isslotfilled(iter.dict, i)) + i += 1; continue + end + @inbounds el = f(vals[i]) + if !W || (el isa T || typeof(el) === T) + @inbounds dest[i] = el + i += 1; continue + else + new = setindex_widen_up_to(dest, el, i) + return _map_to(widen,f,new, iter, i+1) + end end + return dest end + + struct ImmutableDict{K,V} <: AbstractDict{K,V} parent::ImmutableDict{K,V} key::K From 90b5e7b9054fb081165752789e9ab7e21c0ff0e1 Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Thu, 7 Mar 2019 11:55:04 -0500 Subject: [PATCH 07/18] simplified to only include map!, create naive implimentation for AdstractDict, added implimentation for weakkeydict, added basic test --- base/abstractdict.jl | 26 ++++++++++ base/dict.jl | 111 ++++--------------------------------------- base/weakkeydict.jl | 1 + test/dict.jl | 25 ++++++++++ 4 files changed, 60 insertions(+), 103 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index a850a68d07b2b..f89f539e8a9ae 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -701,3 +701,29 @@ function iterate(s::IdSet, state...) ((k, _), i) = y return (k, i) end + +""" + map!(f, values(dict::AbstractDict)) +Takes the function f(value) and transforms values stored in `dict` with the transformation value=f(value). + +# Examples +```jldoctest +julia> D=Dict(:a=>1,:b=>2) +Dict{Symbol,Int64} with 2 entries: + :a => 1 + :b => 2 + +julia> map!(v->v-1,values(D)) +Dict{Symbol,Int64} with 2 entries: + :a => 0 + :b => 1 + ``` +""" +function map!(f, iter::Base.ValueIterator{D}) where D <:Base.AbstractDict + # This is the naive fallback which requires hash evaluations + # Contrary to the example Dict has an implementation which does not require hash evaluations + dict = iter.dict + for (key, val) in pairs(dict) + dict[key] = f(val) + end +end diff --git a/base/dict.jl b/base/dict.jl index 3016a49481978..57c6cfc4485bc 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -686,115 +686,20 @@ end filter!(f, d::Dict) = filter_in_one_pass!(f, d) - - - - - - -""" - map!(f, values(dict)) -Takes the function f(value) and transforms values stored in the dict with the transformation value=f(value). - - -# Examples -```jldoctest -julia> D=Dict(:a=>1,:b=>2) -Dict{Symbol,Int64} with 2 entries: - :a => 1 - :b => 2 - -julia> map!(v->v-1,values(D)) -Dict{Symbol,Int64} with 2 entries: - :a => 0 - :b => 1 - - ``` -""" -Base.map!(f, iter::Base.ValueIterator{Dict{K,V}}) where {K, V}= (_map_to!(Val(false), f, iter.dict.vals, iter, iter.dict.idxfloor); iter.dict) - - -"""map(f, values(dict), create_new_dict=true, value_type_change::Bool = true) -> dict -Takes the function f(value) and creates a copy of the dict with the values transformed by value=f(value). -Setting value_type_change=false will ensure that the values type of the input and output dicts are the same. -create_new_dict=false will cause the map method to fallback to returning an array. - -# Examples -```jldoctest -julia> D=Dict(:a=>1, :b=>2, :c=>3) -Dict{Symbol,Int64} with 3 entries: - :a => 1 - :b => 2 - :c => 3 - -julia> E=map(v->isodd(v) ? Float64(v) : string(v), values(D), create_new_dict=true) -Dict{Symbol,Union{Float64, String}} with 3 entries: - :a => 1.0 - :b => "2" - :c => 3.0 - -julia> E=map(v->isodd(v) ? Int64(v) : UInt32(v), values(D), create_new_dict=true) -Dict{Symbol,Union{Int64, UInt32}} with 3 entries: - :a => 1 - :b => 0x00000002 - :c => 3 - -julia> E=map(v->isodd(v) ? Int64(v) : UInt32(v), values(D), create_new_dict=true, value_type_change = false) -Dict{Symbol,Int64} with 3 entries: - :a => 1 - :b => 2 - :c => 3 - ``` -""" -function Base.map(f,iter::Base.ValueIterator{Dict{K,V}}; create_new_dict::Bool, value_type_change::Bool=true) where {K, V} - if create_new_dict == false - return Base.map(f, iter::Base.ValueIterator{Dict{K,V}}) - end - d=iter.dict - if value_type_change - gen = (f(v) for v in iter) - et = Base.@default_eltype(gen) - - else - et = V - end - dest=Vector{et}(undef,length(d.vals)) - # The reasignment is required to allow for type widening - dest = _map_to!(Val(value_type_change), f, dest, iter, d.idxfloor) - return Dict{K, eltype(dest)}(copy(d.slots), copy(d.keys), dest, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) - -end - - - -function _map_to!(widen::Val{W}, f,dest::AbstractArray{T}, iter::Base.ValueIterator{Dict{K,V}}, ind) where {T, K, V, W} - # changing widen to a bool causes a 10x slowdown - vals=iter.dict.vals - inplace=dest===vals - @assert( !(W && inplace) ) # This asserts that you can widen or have inplace maping but not both - - len = length(vals) - i = ind - +function map!(f, iter::ValueIterator{Dict{K,V}}) where {K, V} + dict = iter.dict + vals = dict.vals + i = dict.idxfloor + len=length(vals) @inbounds while i < len - if !(Base.isslotfilled(iter.dict, i)) + if !(isslotfilled(dict, i)) i += 1; continue end - @inbounds el = f(vals[i]) - if !W || (el isa T || typeof(el) === T) - @inbounds dest[i] = el - i += 1; continue - else - new = setindex_widen_up_to(dest, el, i) - return _map_to(widen,f,new, iter, i+1) - end + @inbounds vals[i] = f(vals[i]) + i += 1; continue end - return dest end - - - struct ImmutableDict{K,V} <: AbstractDict{K,V} parent::ImmutableDict{K,V} key::K diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 8aedbf3ce3a58..934a9f112fe0d 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -92,6 +92,7 @@ function getkey(wkh::WeakKeyDict{K}, kk, default) where K end end +Base.map!(f,iter::Base.ValueIterator{WeakKeyDict{K,V}}) where {K, V} = Base.map!(f,values(iter.dict.ht)) get(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get(wkh.ht, key, default), wkh) get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get(default, wkh.ht, key), wkh) function get!(wkh::WeakKeyDict{K}, key, default) where {K} diff --git a/test/dict.jl b/test/dict.jl index 9fc78fd1e4ddd..cf2604870a6e9 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1021,3 +1021,28 @@ end end end end + +@testset "map!(f,values(dict))" begin + @testset "AbstractDict & Fallback" begin + mutable struct TestDict{K, V} <: AbstractDict{K, V} + dict::Dict{K, V} + function TestDict(args...) + d=Dict(args...) + new{keytype(d), valtype(d)}(d) + end + end + Base.setindex!(td::TestDict, args...) = setindex!(td.dict, args...) + Base.getindex(td::TestDict, args...) = getindex(td.dict, args...) + Base.pairs(D::TestDict) = pairs(D.dict) + testdict = TestDict(:a=>1, :b=>2) + map!(v->v-1, values(testdict)) + @test testdict[:a] == 0 + @test testdict[:b] == 1 + end + @testset "Dict" begin + testdict = Dict(:a=>1, :b=>2) + map!(v->v-1, values(testdict)) + @test testdict[:a] == 0 + @test testdict[:b] == 1 + end +end From ada8654e791eb08fb7415f7f0af302060ef3f28a Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Thu, 7 Mar 2019 11:58:07 -0500 Subject: [PATCH 08/18] Comment fix --- base/abstractdict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index f89f539e8a9ae..ecd526f124df3 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -704,7 +704,7 @@ end """ map!(f, values(dict::AbstractDict)) -Takes the function f(value) and transforms values stored in `dict` with the transformation value=f(value). +Takes the function `f(value) and transforms values stored in `dict` with the transformation `value=f(value). # Examples ```jldoctest From a53f131638abb9fbfe959eea1278bf4162c2b48a Mon Sep 17 00:00:00 2001 From: Nicholas Dinsmore Date: Fri, 8 Mar 2019 12:20:51 -0500 Subject: [PATCH 09/18] Simplification & removed unneeded exports --- base/abstractdict.jl | 2 +- base/dict.jl | 14 ++++++-------- base/exports.jl | 2 -- base/weakkeydict.jl | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index ecd526f124df3..1d0fcbe009fa6 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -719,7 +719,7 @@ Dict{Symbol,Int64} with 2 entries: :b => 1 ``` """ -function map!(f, iter::Base.ValueIterator{D}) where D <:Base.AbstractDict +function map!(f, iter::ValueIterator) # This is the naive fallback which requires hash evaluations # Contrary to the example Dict has an implementation which does not require hash evaluations dict = iter.dict diff --git a/base/dict.jl b/base/dict.jl index 57c6cfc4485bc..84715c44672f9 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -686,18 +686,16 @@ end filter!(f, d::Dict) = filter_in_one_pass!(f, d) -function map!(f, iter::ValueIterator{Dict{K,V}}) where {K, V} +function map!(f, iter::ValueIterator{<:Dict}) dict = iter.dict vals = dict.vals - i = dict.idxfloor - len=length(vals) - @inbounds while i < len - if !(isslotfilled(dict, i)) - i += 1; continue + # @inbounds is here so the it gets propigated to isslotfiled + @inbounds for i = dict.idxfloor:lastindex(vals) + if isslotfilled(dict, i) + vals[i] = f(vals[i]) end - @inbounds vals[i] = f(vals[i]) - i += 1; continue end + return vals end struct ImmutableDict{K,V} <: AbstractDict{K,V} diff --git a/base/exports.jl b/base/exports.jl index a351902d26e83..8a26eb8fdc73f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -506,8 +506,6 @@ export length, map!, map, - mapdict!, - mapdict, mapfoldl, mapfoldr, mapreduce, diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 934a9f112fe0d..24390c2cf236a 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -92,7 +92,7 @@ function getkey(wkh::WeakKeyDict{K}, kk, default) where K end end -Base.map!(f,iter::Base.ValueIterator{WeakKeyDict{K,V}}) where {K, V} = Base.map!(f,values(iter.dict.ht)) +map!(f,iter::ValueIterator{<:WeakKeyDict})= map!(f,values(iter.dict.ht)) get(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get(wkh.ht, key, default), wkh) get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get(default, wkh.ht, key), wkh) function get!(wkh::WeakKeyDict{K}, key, default) where {K} From 80a3b8886462de4a9a9cc46f11f04a6786e1d7f7 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:16:14 -0400 Subject: [PATCH 10/18] Update base/abstractdict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- base/abstractdict.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 1d0fcbe009fa6..fd5fdd664e18d 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -704,6 +704,7 @@ end """ map!(f, values(dict::AbstractDict)) + Takes the function `f(value) and transforms values stored in `dict` with the transformation `value=f(value). # Examples From 49e705e048d5c057dac95f2a0ff65f46597e7863 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:16:27 -0400 Subject: [PATCH 11/18] Update base/abstractdict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- base/abstractdict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index fd5fdd664e18d..ea4fa129d3548 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -709,7 +709,7 @@ Takes the function `f(value) and transforms values stored in `dict` with the tra # Examples ```jldoctest -julia> D=Dict(:a=>1,:b=>2) +julia> d = Dict(:a => 1, :b => 2) Dict{Symbol,Int64} with 2 entries: :a => 1 :b => 2 From eb0ad4eae19b8d97a2f4587069c22a7a4525b31a Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:16:40 -0400 Subject: [PATCH 12/18] Update base/abstractdict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- base/abstractdict.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index ea4fa129d3548..9d8cfd0bf4591 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -705,7 +705,9 @@ end """ map!(f, values(dict::AbstractDict)) -Takes the function `f(value) and transforms values stored in `dict` with the transformation `value=f(value). +Modifies `dict` by transforming each value from `val` to `f(val)`. +Note that the type of `dict` cannot be changed: if `f(val)` is not an instance of the key type +of `dict` then it will be converted to the key type if possible and otherwise raise an error. # Examples ```jldoctest From abb113a8243d845e384785e1311e7b21c9762d1c Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:16:52 -0400 Subject: [PATCH 13/18] Update base/abstractdict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- base/abstractdict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 9d8cfd0bf4591..36202b36fbcae 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -716,7 +716,7 @@ Dict{Symbol,Int64} with 2 entries: :a => 1 :b => 2 -julia> map!(v->v-1,values(D)) +julia> map!(v -> v-1, values(d)) Dict{Symbol,Int64} with 2 entries: :a => 0 :b => 1 From aae11ee71f091b9db85f70a5705999fc3f33fa4b Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:17:11 -0400 Subject: [PATCH 14/18] Update base/weakkeydict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- base/weakkeydict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 24390c2cf236a..db1b60e27d771 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -92,7 +92,7 @@ function getkey(wkh::WeakKeyDict{K}, kk, default) where K end end -map!(f,iter::ValueIterator{<:WeakKeyDict})= map!(f,values(iter.dict.ht)) +map!(f,iter::ValueIterator{<:WeakKeyDict})= map!(f, values(iter.dict.ht)) get(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get(wkh.ht, key, default), wkh) get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get(default, wkh.ht, key), wkh) function get!(wkh::WeakKeyDict{K}, key, default) where {K} From 479da167f2c99107c3df5dc46195a70cd7a3452d Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:17:23 -0400 Subject: [PATCH 15/18] Update test/dict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- test/dict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dict.jl b/test/dict.jl index cf2604870a6e9..b7782dc638b1a 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1022,7 +1022,7 @@ end end end -@testset "map!(f,values(dict))" begin +@testset "map!(f, values(dict))" begin @testset "AbstractDict & Fallback" begin mutable struct TestDict{K, V} <: AbstractDict{K, V} dict::Dict{K, V} From 496fbd91cffd688f96fac68b59bc9dc8130d2888 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 13 Mar 2019 12:17:35 -0400 Subject: [PATCH 16/18] Update test/dict.jl Co-Authored-By: ndinsmore <45537276+ndinsmore@users.noreply.github.com> --- test/dict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dict.jl b/test/dict.jl index b7782dc638b1a..fd691ce8a62b8 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -1027,7 +1027,7 @@ end mutable struct TestDict{K, V} <: AbstractDict{K, V} dict::Dict{K, V} function TestDict(args...) - d=Dict(args...) + d = Dict(args...) new{keytype(d), valtype(d)}(d) end end From 2ca353841beafa2f46b86f21f9f10db2a05021af Mon Sep 17 00:00:00 2001 From: ndinsmore <45537276+ndinsmore@users.noreply.github.com> Date: Wed, 13 Mar 2019 14:56:14 -0400 Subject: [PATCH 17/18] Change return to iter --- base/abstractdict.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 36202b36fbcae..e0dc4d08c8c87 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -729,4 +729,5 @@ function map!(f, iter::ValueIterator) for (key, val) in pairs(dict) dict[key] = f(val) end + return iter end From 829ef9e05f14d7fcd4de196b0487ddfd841b28f2 Mon Sep 17 00:00:00 2001 From: ndinsmore <45537276+ndinsmore@users.noreply.github.com> Date: Wed, 13 Mar 2019 14:57:36 -0400 Subject: [PATCH 18/18] change return to iter --- base/dict.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/dict.jl b/base/dict.jl index 84715c44672f9..08032a929ea22 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -695,7 +695,7 @@ function map!(f, iter::ValueIterator{<:Dict}) vals[i] = f(vals[i]) end end - return vals + return iter end struct ImmutableDict{K,V} <: AbstractDict{K,V}