I want to write a function in Julia which merges two dictionaries.
function merge(left::Dict, right::Dict)::Dict
The semantics are as follows:
left
and right
left
and right
taken ownership of their data, meaning that they will be modified after the function call and no guarantee about the data they contain should be madeleft
and right
the value from left
is maintainedHere's some initial idea about how to approach this problem. (This is pseudocode with notes, not something which will actually compile.)
function mergeDict(left::Dict, right::Dict)::Dict
# create a new dictionary from `left`
return_value = left
# move data from `right` to `left`, "no-clobber"
for k, v in pop_next!(right)
# the function `pop_next!` does not exist, no iterator-like `pop!`
for k in keys(right)
v = pop!(right, k)
# does this work as expected? destructive operation while reading keys?
# `keys()` returns an *iterator*, not a collection! (?)
if !haskey(left, k)
push!(left, k, v) # no `push!` function
left[k] = v # this works instead
end
end
# `left` and `right` are not pointers, but "copy-by-value references"
# just as in Python, so this doesn't work
left = nothing
right = nothing
# we want to invalidate the data, how to do that?
# this also won't work because `left` references the same data
# structure as `return_value`
clear(left)
clear(right)
end
You can see I have attempted to write a manual implementation. I am fairly sure Julia will have some useful functions as part of the standard library for implementing this, however being new to Julia I do not know what those might be.
I found the functions merge
, mergewith
, merge!
and mergewith!
however none of these appear to have the above described semantics.
The differences between your desired function and merge
appear to be
(1) in case of conflicts, the merged dictionary should contain the value from the left
dictionary instead of the value from the right
dictionary, and
(2) it should remove the transferred pairs from the input dictionaries.
If the same key appears in both dictionaries, then there is a decision to make. Your function could either (a) remove the pairs from both dictionaries, or (b) remove only the pair transferred from the left
dictionary.
For part 1, your function can call the Julia functions. Either:
1a: Swap the first and second arguments to merge
, as in merge(right, left)
. Or
1b: Provide mergewith
a combine
function that selects its first arg, as in mergewith( (a,b)->a, left, right)
.
For part 2, your function can either
2a. use empty!
on both inputs as in empty!(left)
and empty!(right)
(if inputs both should become empty despite conflicts). Or
2b. use filter!(pair -> pair.first in keys(left), right)
and empty!(left)
(keep conflicting keys in right
if right
pairs not transferred to the merged dictionary should remain in right
)
(Tip: methodswith(Dict, supertypes=true)
lists signatures of methods that accept a Dict
, including empty!
and filter!
.)
function mergeAndEmpty!(left::Dict, right::Dict)::Dict
tmp = merge(right, left)
empty!(left)
empty!(right)
return tmp
end
d1 = Dict("a"=>1, "b"=>2, "c"=>3, "d"=>4)
d2 = Dict("y"=>10, "z"=>20)
d_out = mergeAndEmpty!(d1, d2)
println(d1) # empty
println(d2) # empty
println(d_out) # contains all 6 elements