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 add an example of programatically creating a named tuple by KristofferC · Pull Request #32330 · JuliaLang/julia · GitHub).
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))
What…? @mauro3’s solution, which is also marked as the solution, seems shorter:
Perhaps you missed this one?
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