Skip to content

Commit 97a9cb6

Browse files
tkfKristofferC
authored andcommitted
Add mergewith[!](combine, dicts...) (#34296)
1 parent dce58b3 commit 97a9cb6

File tree

5 files changed

+72
-15
lines changed

5 files changed

+72
-15
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ Build system changes
4343
New library functions
4444
---------------------
4545

46+
* New functions `mergewith` and `mergewith!` supersede `merge` and `merge!` with `combine`
47+
argument. They don't have the restriction for `combine` to be a `Function` and also
48+
provide one-argument method that returns a closure. The old methods of `merge` and
49+
`merge!` are still available for backward compatibility ([#34296]).
4650
* The new `isdisjoint` function indicates whether two collections are disjoint ([#34427]).
4751

4852
New library features

base/abstractdict.jl

+43-8
Original file line numberDiff line numberDiff line change
@@ -184,36 +184,52 @@ function merge!(d::AbstractDict, others::AbstractDict...)
184184
end
185185

186186
"""
187-
merge!(combine, d::AbstractDict, others::AbstractDict...)
187+
mergewith!(combine, d::AbstractDict, others::AbstractDict...) -> d
188+
mergewith!(combine)
189+
merge!(combine, d::AbstractDict, others::AbstractDict...) -> d
188190
189191
Update collection with pairs from the other collections.
190192
Values with the same key will be combined using the
191-
combiner function.
193+
combiner function. The curried form `mergewith!(combine)` returns the
194+
function `(args...) -> mergewith!(combine, args...)`.
195+
196+
Method `merge!(combine::Union{Function,Type}, args...)` as an alias of
197+
`mergewith!(combine, args...)` is still available for backward
198+
compatibility.
199+
200+
!!! compat "Julia 1.5"
201+
`mergewith!` requires Julia 1.5 or later.
192202
193203
# Examples
194204
```jldoctest
195205
julia> d1 = Dict(1 => 2, 3 => 4);
196206
197207
julia> d2 = Dict(1 => 4, 4 => 5);
198208
199-
julia> merge!(+, d1, d2);
209+
julia> mergewith!(+, d1, d2);
200210
201211
julia> d1
202212
Dict{Int64,Int64} with 3 entries:
203213
4 => 5
204214
3 => 4
205215
1 => 6
206216
207-
julia> merge!(-, d1, d1);
217+
julia> mergewith!(-, d1, d1);
208218
209219
julia> d1
210220
Dict{Int64,Int64} with 3 entries:
211221
4 => 0
212222
3 => 0
213223
1 => 0
224+
225+
julia> foldl(mergewith!(+), [d1, d2]; init=Dict{Int64,Int64}())
226+
Dict{Int64,Int64} with 3 entries:
227+
4 => 5
228+
3 => 0
229+
1 => 4
214230
```
215231
"""
216-
function merge!(combine::Function, d::AbstractDict, others::AbstractDict...)
232+
function mergewith!(combine, d::AbstractDict, others::AbstractDict...)
217233
for other in others
218234
for (k,v) in other
219235
d[k] = haskey(d, k) ? combine(d[k], v) : v
@@ -222,6 +238,10 @@ function merge!(combine::Function, d::AbstractDict, others::AbstractDict...)
222238
return d
223239
end
224240

241+
mergewith!(combine) = (args...) -> mergewith!(combine, args...)
242+
243+
merge!(combine::Callable, args...) = mergewith!(combine, args...)
244+
225245
"""
226246
keytype(type)
227247
@@ -287,12 +307,21 @@ merge(d::AbstractDict, others::AbstractDict...) =
287307
merge!(_typeddict(d, others...), others...)
288308

289309
"""
310+
mergewith(combine, d::AbstractDict, others::AbstractDict...)
311+
mergewith(combine)
290312
merge(combine, d::AbstractDict, others::AbstractDict...)
291313
292314
Construct a merged collection from the given collections. If necessary, the
293315
types of the resulting collection will be promoted to accommodate the types of
294316
the merged collections. Values with the same key will be combined using the
295-
combiner function.
317+
combiner function. The curried form `mergewith(combine)` returns the function
318+
`(args...) -> mergewith(combine, args...)`.
319+
320+
Method `merge(combine::Union{Function,Type}, args...)` as an alias of
321+
`mergewith(combine, args...)` is still available for backward compatibility.
322+
323+
!!! compat "Julia 1.5"
324+
`mergewith` requires Julia 1.5 or later.
296325
297326
# Examples
298327
```jldoctest
@@ -306,14 +335,20 @@ Dict{String,Int64} with 2 entries:
306335
"bar" => 4711
307336
"baz" => 17
308337
309-
julia> merge(+, a, b)
338+
julia> mergewith(+, a, b)
310339
Dict{String,Float64} with 3 entries:
311340
"bar" => 4753.0
312341
"baz" => 17.0
313342
"foo" => 0.0
343+
344+
julia> ans == mergewith(+)(a, b)
345+
true
314346
```
315347
"""
316-
merge(combine::Function, d::AbstractDict, others::AbstractDict...) =
348+
mergewith(combine, d::AbstractDict, others::AbstractDict...) =
349+
mergewith!(combine, _typeddict(d, others...), others...)
350+
mergewith(combine) = (args...) -> mergewith(combine, args...)
351+
merge(combine::Callable, d::AbstractDict, others::AbstractDict...) =
317352
merge!(combine, _typeddict(d, others...), others...)
318353

319354
promoteK(K) = K

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,9 @@ export
515515
mapfoldr,
516516
mapreduce,
517517
merge!,
518+
mergewith!,
518519
merge,
520+
mergewith,
519521
pairs,
520522
reduce,
521523
setdiff!,

doc/src/base/collections.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ Base.keys
210210
Base.values
211211
Base.pairs
212212
Base.merge
213-
Base.merge!(::AbstractDict, ::AbstractDict...)
214-
Base.merge!(::Function, ::AbstractDict, ::AbstractDict...)
213+
Base.mergewith
214+
Base.merge!
215+
Base.mergewith!
215216
Base.sizehint!
216217
Base.keytype
217218
Base.valtype

test/dict.jl

+20-5
Original file line numberDiff line numberDiff line change
@@ -924,14 +924,22 @@ let
924924
end
925925
end
926926

927+
struct NonFunctionCallable end
928+
(::NonFunctionCallable)(args...) = +(args...)
929+
927930
@testset "Dict merge" begin
928931
d1 = Dict("A" => 1, "B" => 2)
929932
d2 = Dict("B" => 3.0, "C" => 4.0)
930933
@test @inferred merge(d1, d2) == Dict("A" => 1, "B" => 3, "C" => 4)
931934
# merge with combiner function
935+
@test @inferred mergewith(+, d1, d2) == Dict("A" => 1, "B" => 5, "C" => 4)
936+
@test @inferred mergewith(*, d1, d2) == Dict("A" => 1, "B" => 6, "C" => 4)
937+
@test @inferred mergewith(-, d1, d2) == Dict("A" => 1, "B" => -1, "C" => 4)
938+
@test @inferred mergewith(NonFunctionCallable(), d1, d2) == Dict("A" => 1, "B" => 5, "C" => 4)
939+
@test foldl(mergewith(+), [d1, d2]; init=Dict{Union{},Union{}}()) ==
940+
Dict("A" => 1, "B" => 5, "C" => 4)
941+
# backward compatibility
932942
@test @inferred merge(+, d1, d2) == Dict("A" => 1, "B" => 5, "C" => 4)
933-
@test @inferred merge(*, d1, d2) == Dict("A" => 1, "B" => 6, "C" => 4)
934-
@test @inferred merge(-, d1, d2) == Dict("A" => 1, "B" => -1, "C" => 4)
935943
end
936944

937945
@testset "Dict merge!" begin
@@ -940,12 +948,19 @@ end
940948
@inferred merge!(d1, d2)
941949
@test d1 == Dict("A" => 1, "B" => 3, "C" => 4)
942950
# merge! with combiner function
943-
@inferred merge!(+, d1, d2)
951+
@inferred mergewith!(+, d1, d2)
944952
@test d1 == Dict("A" => 1, "B" => 6, "C" => 8)
945-
@inferred merge!(*, d1, d2)
953+
@inferred mergewith!(*, d1, d2)
946954
@test d1 == Dict("A" => 1, "B" => 18, "C" => 32)
947-
@inferred merge!(-, d1, d2)
955+
@inferred mergewith!(-, d1, d2)
948956
@test d1 == Dict("A" => 1, "B" => 15, "C" => 28)
957+
@inferred mergewith!(NonFunctionCallable(), d1, d2)
958+
@test d1 == Dict("A" => 1, "B" => 18, "C" => 32)
959+
@test foldl(mergewith!(+), [d1, d2]; init=empty(d1)) ==
960+
Dict("A" => 1, "B" => 21, "C" => 36)
961+
# backward compatibility
962+
merge!(+, d1, d2)
963+
@test d1 == Dict("A" => 1, "B" => 21, "C" => 36)
949964
end
950965

951966
@testset "Dict reduce merge" begin

0 commit comments

Comments
 (0)