4

Aside that Dictionaries are mutable and NamedTuple not, that NamedTuple can be retrieved by position and a bit of different notation, are there other significant differences between Dictionaries and NamedTuples in Julia ? When to use one or the other ?

They seem pretty similar:

# Definition
d  = Dict("k1"=>"v1", "k2"=>"v2")
nt = (k1="v1", k2="v2")

# Selection by specific key
d["k1"]
nt.k1

# Keys
keys(d)
keys(nt)

# Values
values(d)
values(nt)

# Selection by position
d[1] # error
nt[1]
Antonello
  • 6,092
  • 3
  • 31
  • 56
  • One big difference is that the order of keys/values in a `NamedTuple` matters. `(a=1,b=2) != (b=2,a=1)` even though `Dict(:a => 1, :b => 2) == Dict(:b => 2, :a => 1)` – BallpointBen May 29 '21 at 20:30

3 Answers3

11

A significant difference in Julia is that a NamedTuple is its own type, and therefore the compiler can specialize on that particular named tuple signature, while the Dictionary approach has to look up the value from the key. In addition, each value of a NamedTuple can be a different type itself, allowing for further optimization and type stability beyond what can be achieved in a dictionary. If we change it a bit more so that the type of the dictionary is Heterogeneous so that it is of type Dict{Symbol,Any}, you can see how it can still be type stable.

d=Dict(:k1=>"v1",:k2=>2.0)
nt=(k1="v1",k2=2.0)

foo(x) = x[:k2]

Now if a function uses this dictionary or named tuple directly, we can see for the dictionary type, the result is not type stable, as the value in the Dictionary can only be guaranteed to by the type of Any.

@code_warntype foo(d)

Body::Any
4 1 ─ %1 = invoke Base.ht_keyindex(_2::Dict{Symbol,Any}, :k2::Symbol)::Int64                                                                                                                                               │╻  getindex
  │   %2 = (Base.slt_int)(%1, 0)::Bool                                                                                                                                                                                     ││╻  <
  └──      goto #3 if not %2                                                                                                                                                                                               ││
  2 ─ %4 = %new(Base.KeyError, :k2)::KeyError                                                                                                                                                                              ││╻  Type
  │        (Base.throw)(%4)                                                                                                                                                                                                ││
  └──      $(Expr(:unreachable))                                                                                                                                                                                           ││
  3 ─ %7 = (Base.getfield)(x, :vals)::Array{Any,1}                                                                                                                                                                         ││╻  getproperty
  │   %8 = (Base.arrayref)(false, %7, %1)::Any                                                                                                                                                                             ││╻  getindex
  └──      goto #5                                                                                                                                                                                                         ││
  4 ─      $(Expr(:unreachable))                                                                                                                                                                                           ││
  5 ┄      return %8

On the other hand, a NamedTuple can be type stable. Because the field was known by the function, the type is also known to be a Float64.

@code_warntype foo(nt)

Body::Float64
4 1 ─ %1 = (Base.getfield)(x, :k2)::Float64                                                                                                                                                                                     │╻ getindex
  └──      return %1
dberge
  • 311
  • 1
  • 5
7

Think of NamedTuple as an anonymous struct in Julia not a Dict. In particular storing heterogeneous types in NamedTuple is type stable.

Note that this is also a major difference in thinking between Python and Julia. In Julia if you want your code to be fast you usually care about type inference.

Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
-5

According to similar questions about Python seems.. not. The main differences seems to be the mutable/immutable property and that x.a is more readable and less verbose than x[a]...:

Antonello
  • 6,092
  • 3
  • 31
  • 56
  • 1
    So, this "answer" is definitely wrong. I keep it alive, as it is a good reminder that there are differences between Python and Julia ;-) – Antonello Sep 11 '18 at 13:24