How to make a named tuple from a dictionary?

Is there a simple way to make a named tuple from a Dict{Symbol, T}?

5 Likes

There should be an easier way, but this works:

julia> NamedTuple{tuple(keys(di)...), Tuple{typeof.(collect(values(di)))...,}}((values(di)...,)) 
(a = 1, b = 5.6)```
3 Likes
dictkeys(d::Dict) = (collect(keys(d))...,)
dictvalues(d::Dict) = (collect(values(d))...,)

namedtuple(d::Dict{Symbol,T}) where {T} =
    NamedTuple{dictkeys(d)}(dictvalues(d))

dictionary = Dict(:a => 5, :b => 6, :c => 7)
Dict{Symbol,Int64} with 3 entries:
  :a => 5
  :b => 6
  :c => 7

namedtuple(dictionary)
(a = 5, b = 6, c = 7)

dictionary = Dict(:a => 5, :b => 6.0, :c => "7")
Dict{Symbol,Any} with 3 entries:
  :a => 5
  :b => 6.0
  :c => "7"

namedtuple(dictionary)
(a = 5, b = 6.0, c = "7")
4 Likes

Thanks @mauro3 and @JeffreySarnoff for the replies, it would be nice of you to make a PR!

Is there nowadays a more convenient way to turn a dict into a named tuple?

julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol,Int64} with 2 entries:
  :a => 1                        
  :b => 2

julia> NamedTuple{Tuple(keys(d))}(values(d))
(a = 1, b = 2)
8 Likes

Probably touching internal detail, but…

((; kw...) -> kw.data)(; d...)

also works.

3 Likes

AFAIU values(kw) is the public accessor for kw.data (to turn the keyword iterator into a NamedTuple).

2 Likes

Simply unpack the dict into a tuple.


(a = 1, dict...)


7 Likes

Tada:

julia> (; Dict(:a => 5, :b => 6, :c => 7)...)
(a = 5, b = 6, c = 7)

(Edit: note, that this already worked in Julia 0.7 when NamedTuples first appeared. Sometimes it just takes a bit longer to find the best solution…)

41 Likes

Ran into the inverse of this problem, so thought it might be helpful (at least not harmful) to post a solution for that here:

julia> nt = (a = 5, b = 6, c = 7)
(a = 5, b = 6, c = 7)

julia> Dict(pairs(nt))
Dict{Symbol,Int64} with 3 entries:
  :a => 5
  :b => 6
  :c => 7
23 Likes

I think it would be nice if @mauro3’s solution were to appear explicitly in the documentation somewhere, and it might also be nice if there were some sort of namedtuple function, analogous to tuple which would probably make this functionality much easier to find.

6 Likes

NamedTupleTools.jl exports namedtuple. I’d welcome issues that request additional features and be happier still with targeted PRs.

7 Likes

Ran into this again and just wanted to point out that the solution here seems incredibly slow

julia> @btime (;d...)
  967.857 ns (13 allocations: 928 bytes)
(a = 1, b = 2)

compare that to the inverse

julia> @btime Dict(pairs(nt))
  208.054 ns (7 allocations: 704 bytes)
Dict{Symbol,Int64} with 2 entries:
  :a => 1
  :b => 2

Anyone know of any way of speeding this up?

2 Likes

@Tamas_Papp solution seems twice as fast than (; d...). But given that this is type-unstable code, it’s maybe not so suprising that it’s slow?

1 Like

And how to make a named tuple from a dictionary, when dictionary keys are of type String?
The proposed methods do not work in such a case.

if using NamedTupleTools.jl is ok:

julia> using NamedTupleTools

julia> adict = Dict("astr" => 5, "bstr" => 'b');

julia> namedtuple(Symbol.(keys(adict)), values(adict))
(bstr = 'b', astr = 5)

julia> typeof(ans)
NamedTuple{(:bstr, :astr),Tuple{Char,Int64}}
1 Like
julia> d = Dict(["a" => 1, "b" => 2])
Dict{String,Int64} with 2 entries:
  "b" => 2
  "a" => 1

julia> NamedTuple{Tuple(Symbol.(keys(d)))}(values(d))
(b = 2, a = 1)

Note that of course since a Dict not sorted, ordering is not guaranteed.

3 Likes

Thank you @Tamas_Papp. It worked.

This produces me an error:

MethodError: no method matching namedtuple(::Array{Symbol,1}, ::Base.ValueIterator{Dict{String,Any}})

(I am using Julia 1.0.3)

Perhaps there should be a NamedTuple constructor taking a Dict and vice versa as this seems to pop up regularly. What do you think?

2 Likes