How do I best convert a nested named tuple to a nested struct?

The code below contains two examples: the first example works, the second one doesn’t. The first example creates a struct, then writes it to an arrow file (which converts it to a named tuple along the way). Reading it back works fine.

In the second example, there is a vector of structures structure nested inside the outside structure. This does not work, because the inside argument is still a tuple, not a structure. What would be the best way of getting this to work? Write new constructors? Thanks!

using Arrow, DataFrames


struct Mine
    x::Int  
    y::Vector{Int}
    z::String
end

m1 = Mine( 0, [1;2], "one" )
m2 = Mine( 1, [4;6], "two" )
df = DataFrame( m = [m1;m2] )
Arrow.write( "mine.arrow", df )
df2 = Arrow.Table( "mine.arrow" ) |> DataFrame

println( typeof( df2[1,:m] ) )
println( Mine( df2[1,:m]... ) )

# the above works


struct Another
    a::String
    b::Vector{Mine}
end

m3 = Mine( 0, [1;2;8], "three" )

a1 = Another( "first", [m1;m2] )
a2 = Another( "second", [m2;m3] )

dfa = DataFrame( a = [a1; a2] )
Arrow.write( "another.arrow", dfa)
dfa2 = Arrow.Table( "another.arrow" ) |> DataFrame

println( dfa2[1,:a] )
# how best to do what is intended below?
println( Another( dfa2[1,:a]... ) )
1 Like

Check out https://github.com/JeffreySarnoff/NamedTupleTools.jl#struct-construction-conversion

1 Like

You could also recreate nt2struct something like this

struct Mine
    x::Int  
    y::Vector{Int}
    z::String
end

m1 = Mine( 0, [1,2], "one" )

function nt2struct(nt, ::Type{T}) where T
    fns = fieldnames(T)
    fs = getfield.((nt, ), fns)
    T(fs...)
end

nt = (x=0, y=[1,2], z="one")
m2 = nt2struct(nt, Mine)
1 Like

Edit: This version is a bit faster than the one above, but still not completely type stable

function nt2struct(nt, ::Type{T}) where T
    fns = fieldnames(T)
    fs = ntuple(i->getfield(nt, fns[i]), fieldcount(T))
    T(fs...)
end

Edit2: This version is type stable and fast

function nt2struct(nt::NamedTuple{names, types}, ::Type{T}) where {names, types, T}
    fs = ntuple(i->getfield(nt, names[i]), fieldcount(T))
    T(fs...)
end
5 Likes

Thanks @baggepinnen !

I had managed to convert a named tuple to a string (top half); it is the nested part that I’m having trouble with (bottom half).

Thank you for the (to be credited) contribution to NamedTupleTools v2!

3 Likes