Skip to content

Commit 16f433b

Browse files
matthias314vtjnash
andauthored
faster mergewith! (#40893)
Co-authored-by: matthias314 <matthias314@posteo.net> Co-authored-by: Jameson Nash <vtjnash@gmail.com>
1 parent 2cf5f98 commit 16f433b

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

base/abstractdict.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,14 @@ Dict{Int64, Int64} with 3 entries:
234234
```
235235
"""
236236
function mergewith!(combine, d::AbstractDict, others::AbstractDict...)
237-
for other in others
238-
for (k,v) in other
239-
d[k] = haskey(d, k) ? combine(d[k], v) : v
240-
end
237+
foldl(mergewith!(combine), others; init = d)
238+
end
239+
240+
function mergewith!(combine, d1::AbstractDict, d2::AbstractDict)
241+
for (k, v) in d2
242+
d1[k] = haskey(d1, k) ? combine(d1[k], v) : v
241243
end
242-
return d
244+
return d1
243245
end
244246

245247
mergewith!(combine) = (args...) -> mergewith!(combine, args...)

base/dict.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,21 @@ function map!(f, iter::ValueIterator{<:Dict})
729729
return iter
730730
end
731731

732+
function mergewith!(combine, d1::Dict{K, V}, d2::AbstractDict) where {K, V}
733+
for (k, v) in d2
734+
i = ht_keyindex2!(d1, k)
735+
if i > 0
736+
d1.vals[i] = combine(d1.vals[i], v)
737+
else
738+
if !isequal(k, convert(K, k))
739+
throw(ArgumentError("$(limitrepr(k)) is not a valid key for type $K"))
740+
end
741+
@inbounds _setindex!(d1, convert(V, v), k, -i)
742+
end
743+
end
744+
return d1
745+
end
746+
732747
struct ImmutableDict{K,V} <: AbstractDict{K,V}
733748
parent::ImmutableDict{K,V}
734749
key::K

test/dict.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,26 @@ end
10701070
check_merge([Dict(3=>4), Dict(:a=>5)], Dict(:a => 5, 3 => 4))
10711071
end
10721072

1073+
@testset "AbstractDict mergewith!" begin
1074+
# we use IdDict to test the mergewith! implementation for AbstractDict
1075+
d1 = IdDict(1 => 1, 2 => 2)
1076+
d2 = IdDict(2 => 3, 3 => 4)
1077+
d3 = IdDict{Int, Float64}(1 => 5, 3 => 6)
1078+
d = copy(d1)
1079+
@inferred mergewith!(-, d, d2)
1080+
@test d == IdDict(1 => 1, 2 => -1, 3 => 4)
1081+
d = copy(d1)
1082+
@inferred mergewith!(-, d, d3)
1083+
@test d == IdDict(1 => -4, 2 => 2, 3 => 6)
1084+
d = copy(d1)
1085+
@inferred mergewith!(+, d, d2, d3)
1086+
@test d == IdDict(1 => 6, 2 => 5, 3 => 10)
1087+
@inferred mergewith(+, d1, d2, d3)
1088+
d = mergewith(+, d1, d2, d3)
1089+
@test d isa Dict{Int, Float64}
1090+
@test d == Dict(1 => 6, 2 => 5, 3 => 10)
1091+
end
1092+
10731093
@testset "misc error/io" begin
10741094
d = Dict('a'=>1, 'b'=>1, 'c'=> 3)
10751095
@test_throws ErrorException 'a' in d

0 commit comments

Comments
 (0)