How to append multiple key-value pairs to an existing Dict

We can create a Dict from an iteratable of key-value pairs, e.g.,

julia> x = ['A', 'B', 'C']; y = [1, 2, 3];

julia> d = Dict(x .=> y)
Dict{Char, Int64} with 3 entries:
  'C' => 3
  'A' => 1
  'B' => 2

Now supposing I have another two collections of keys and values x2 and y2, how can I insert them into the above d using a similar syntax?

julia> x2 = ['D', 'E', 'F']; y2 = [4, 5, 6];

julia> append!(d, x2 .=> y2)
ERROR: MethodError: no method matching append!(::Dict{Char, Int64}, ::Vector{Pair{Char, Int64}})
Closest candidates are:

julia> push!(d, x2 .=> y2)
ERROR: MethodError: no method matching push!(::Dict{Char, Int64}, ::Vector{Pair{Char, Int64}})

Do I have to use a loop to insert them one by one? :joy:

There are maybe better ways, but until then you can merge() multiple Dicts.

julia> x = ['A', 'B', 'C']; y = [1, 2, 3];

julia> d = Dict(x .=> y)
Dict{Char, Int64} with 3 entries:
  'C' => 3
  'A' => 1
  'B' => 2

julia> x2 = ['D', 'E', 'F']; y2 = [4, 5, 6];

julia> merge(d, Dict(x2 .=> y2))
Dict{Char, Int64} with 6 entries:
  'C' => 3
  'D' => 4
  'A' => 1
  'E' => 5
  'F' => 6
  'B' => 2
3 Likes

Thanks. It is indeed a workaround. However, I am expecting something without creating a temporary Dict.

See related issue here and Dictionaries.jl

1 Like

You can just broadcast setindex!:

julia> d = Dict(x .=> y)
Dict{Char, Int64} with 3 entries:
  'C' => 3
  'A' => 1
  'B' => 2

julia> setindex!.(Ref(d), y2, x2);

julia> d
Dict{Char, Int64} with 6 entries:
  'C' => 3
  'D' => 4
  'A' => 1
  'E' => 5
  'F' => 6
  'B' => 2

or even push!.(Ref(d), x2 .=> y2).

6 Likes

Loops can be short

foreach(((k, v), ) -> d[k] = v, zip(x2, y2))

foreach is good in this case, since it doesn’t produce extra allocations (like broadcasting does).

3 Likes

It is interesting. setindex!. applies broadcasting and returns a vector of 3 Dicts instead, though the three are essentially the same object.

julia> res = setindex!.(Ref(d), y2, x2)
3-element Vector{Dict{Char, Int64}}:
 Dict('C' => 3, 'D' => 4, 'A' => 1, 'E' => 5, 'F' => 6, 'B' => 2)
 Dict('C' => 3, 'D' => 4, 'A' => 1, 'E' => 5, 'F' => 6, 'B' => 2)
 Dict('C' => 3, 'D' => 4, 'A' => 1, 'E' => 5, 'F' => 6, 'B' => 2)

julia> res[1] === res[2] === res[3] === d
true
1 Like

Comprehension seems to be even shorter:

[d[x]=y for (x,y) in zip(x2,y2)]

But allocates, which the foreach solution doesn’t.

4 Likes

Sometimes I worry the answers we the community give in response to questions like this are a bit hard to read. E.g. there’s something surgically efficient about foreach(((k, v), ) -> d[k] = v, zip(x2, y2)), but it’s pretty hard to parse: nested tuples in an anonymous function, etc.

If I were a new-ish user and asked how to do something that seems like it should be simple and got advice about fancy higher order functions and avoiding allocation, I would probably be put off. Even as an intermediate user, it feels somehow wrong that the right way to add multiple items to a dictionary requires broadcasting over a Ref or an anonymous function in a foreach or something like that.

Anyway, my not-high-powered answer to the original question is to not fear the loop: loops are typically fast in Julia, and it’s not lot of characters. You can even do it in one line if you want:

for (k,v) in zip(x2, y2) d[k] = v end

(though in my opinion it’s more readable with line breaks)
Shuhua, is there a particular reason you don’t want to loop?

11 Likes

Certainly no. :smiley: I just want to know whether there are more convenient (built-in) functions like push! and append!.

Anyway, I have learned some advanced techniques in all these answers. Thanks.
For now, I think the for loop is the best solution: intuitive and fast. (Also, no need to put the loop in one line just to make it look shorter :stuck_out_tongue_closed_eyes:)

2 Likes

I think it is great to see multiple solutions to a problem.

If they contain unfamiliar building blocks, this may be utilized as an opportunity to learn about them.

3 Likes