miha priimek
miha priimek

Reputation: 979

How to flatten named tuples in a tuple in julia?

I have a Tuple of elements, some of which are NamedTuples. I would like to flatten the NamedTuples like so:

julia> nt = (a="a", b="b")
(a = "a", b = "b")

julia> t = (1, 2, 3, nt)
(1, 2, 3, (a = "a", b = "b"))

julia> res = tuple(1, 2, 3, nt...)
(1, 2, 3, "a", "b")

How to do this programmatically? I tried the following:

julia> exprs = [x isa NamedTuple ? Meta.parse("$x...") : x for x in t]
4-element Array{Any,1}:
 1                        
 2                        
 3                        
  :((a = "a", b = "b")...)

julia> res = tuple(eval(ex) for ex in exprs)
(Base.Generator{Array{Any,1},typeof(eval)}(Base.MainInclude.eval, Any[1, 2, 3, :((a = "a", b = "b")...)]),)

But it doesn't quite give what I would like: (1, 2, 3, "a", "b")

Upvotes: 3

Views: 652

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69839

The simplest way to do it would be to write:

julia> Tuple(Iterators.flatten((1,2,3, (a="a",b="b"))))
(1, 2, 3, "a", "b")

this has a downside that it is type unstable and that it will flatten all iterables (not only NamedTuples).

If you want only NamedTuples flattened then something like this could be used:

julia> Tuple(reduce((a,b) -> (b isa NamedTuple ? append! : push!)(a, b), (1,2,3, (a="a",b="b")), init=[]))
(1, 2, 3, "a", "b")

(still it will be type unstable)

If you want something type stable you can use recursion e.g. like this:

flatten() = ()
flatten(a::NamedTuple) = Tuple(a)
flatten(a) = (a,)
flatten(a::NamedTuple, b...) = tuple(a..., flatten(b...)...)
flatten(a, b...) = tuple(a, flatten(b...)...)
flatten_tuple(x::Tuple) = flatten(x...)

and now you have:

julia> flatten_tuple((1,2,3, (a="a",b="b")))
(1, 2, 3, "a", "b")

julia> @code_warntype flatten_tuple((1,2,3, (a="a",b="b")))
Variables
  #self#::Core.Compiler.Const(flatten_tuple, false)
  x::Tuple{Int64,Int64,Int64,NamedTuple{(:a, :b),Tuple{String,String}}}

Body::Tuple{Int64,Int64,Int64,String,String}
1 ─ %1 = Core._apply_iterate(Base.iterate, Main.flatten, x)::Tuple{Int64,Int64,Int64,String,String}
└──      return %1

Upvotes: 7

Related Questions