dictionaryjulia

How to merge a dictionary in Julia?


I want to write a function in Julia which merges two dictionaries.

function merge(left::Dict, right::Dict)::Dict

The semantics are as follows:

Here'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.


Solution

  • 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!.)

    Example

    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