How to make a named tuple from a dictionary?

I don’t think it is a good idea, because, as I remarked above, Dicts are not ordered, so it can happen that two <:AbstractDict values are == but the resulting NamedTuples aren’t.

It may be the version – I do not have v1.0.3 available …
you have a way that works, so I am not looking further

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

julia> (; (Symbol(k) => v for (k,v) in adict)...)
(bstr = 'b', astr = 5)
4 Likes

There already is a much more general constructor which takes any iterator yielding Symbol => value pairs. People just don’t know about the syntax (hence add an example of programatically creating a named tuple by KristofferC · Pull Request #32330 · JuliaLang/julia · GitHub).

6 Likes

NamedTupleTools looks awesome! Is it possible to recursively turn all the dictionaries in the dictionary into named tuples?

Also what is your opinion on doing this in the first place? I am reading some MATLAB .mat files (which is read by MAT.jl to a dictionary) which have a nested matlab structs e.g EEG.event.epoch. And in order to keep my code more readable by the MATLAB savvy ones, I was thinking of using namedtuples since they resemble MATLAB structs. Are there any pros/cons do doing this? Order isn’t really neccessary.

1 Like

If you plan to go deep you may want to check whether Performance of nested named tuples · Issue #25296 · JuliaLang/julia · GitHub is still a problem.

1 Like

That problem has been resolved (not an issue with v1.3.1).

NamedTuples are a good choice anywhere they are a natural fit.
It sounds like NamedTuples would be appropriate for your use.

Okay great. So in this use case when I convert deeply nested MATLAB structs (eg. EEG.event.epoch) using MAT.jl the output will be an Dict named EEG containing a Dict called event. So if you need to access epoch it would be accessed by:

EEG["event"]["epoch"]

And so (finally) using NameTupleTools when I make a named tuple from EEG:

namedtuple(EEG)

EEG.event will contain event as a dict; requiring me to access epoch in the following way:

EEG.event["epoch"]

Is there way for the Dict constructor to convert all nested dictionaries into named tuples too? So that I can simply use:

EEG.event.epoch
1 Like
using NamedTupleTools
recursive_namedtuple(x::Any) = x
recursive_namedtuple(d::Dict) = namedtuple(Dict(k => recursive_namedtuple(v) for (k, v) in d))

You may want to restrict this to only apply to Dicts with the right type of keys.

9 Likes

Awesome, thanks a lot! I didn’t even know keys and values could be accessed like that.

The shortest universal solution seems to be this:

merge(NamedTuple(), Dict(:a => 5, :b => 6, :c => 7))

What…? @mauro3’s solution, which is also marked as the solution, seems shorter:

1 Like

Perhaps you missed this one?

1 Like

Many of the answers here do not work if Dict is created using string keys, just for Symbol keys, probably we should have some easier functions for conversions, i got myself sometimes struggling with type conversions too…

If the keys are symbols as in

d = Dict(:a => 5, :b => 6, :c => 7)

the best solution in Julia 1.6 is probably

NamedTuple(d)

Now if the keys are strings you must convert them to symbols. Here are some solutions:

d = Dict("a" => 5, "b" => 6, "c" => 7)


NamedTuple(Symbol(k) => v for (k,v) in d)

# or
NamedTuple(Symbol.(keys(d)) .=> values(d))

# or
(; (Symbol(k) => v for (k,v) in d)...)

# or
(; (Symbol.(keys(d)) .=> values(d))...)
5 Likes

If you have this a lot of times you can, of course, define

import Base.NamedTuple
NamedTuple(d::Dict{String, T} where T) = NamedTuple(Symbol(k) => v for (k,v) in d)

which allows you to do

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

Strictly speaking this is type piracy, although I guess it’s unlikely to break things in practice.

3 Likes

I agree. So the clean way would be

nt(d::Dict{AbstractString, T} where T) = NamedTuple(Symbol(k) => v for (k,v) in d)

with

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

But I think many people would like to see the above implementation as standard, perhaps with String replaced by AbstractString as here.

1 Like

I might as well post my solution, which is also recursive and doesn’t use any external library:

namedtuple(d::Dict) = (; map(k -> Symbol(first(k)) => namedtuple(last(k)), collect(d))...)
namedtuple(x) = x
2 Likes