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 NamedTuple
s 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
julia> adict = Dict("astr" => 5, "bstr" => 'b');
julia> (; (Symbol(k) => v for (k,v) in adict)...)
(bstr = 'b', astr = 5)
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 https://github.com/JuliaLang/julia/pull/32330).
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.
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.
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
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 Dict
s with the right type of keys.
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))
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))...)
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.
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.
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