Creating a struct from a Named Tuple

Is there any function like similar which creates a struct inhering the names and types of a NamedTuple?

MEW:

nt = (a = 1, b ="Hello")

#some function like 
MyStruct = similar(nt)

#`similar` would create this:
struct MyStruct
    a::Int64
    b::String
end

My question is because you can easily convert a SubDataFrame into a NamedTuple. So, retrieving some data and putting it into a model can be easily done through a NamedTuple. However, there doesn’t seem to be an easy way to move from a SubDataFrame to a struct, unless you repeat the name of each variable and the type of each variable (which is long and prone to error if you have 20 variables).

Many thanks

  1. Don’t overload similar on types you don’t own. Define a constructor instead

  2. One thing you can do is use the @with_kw macro

@with_kw struct MyStruct
    a::Int64
    b::String
end
MyStruct(; nt...)
1 Like

The name similar was just an example. But, in any case, I’m actually asking for the other way of the conversion: going from a NamedTuple to a struct.

Yes, my example gives you that.

Thanks, my point is that I want to avoid defining the struct. So, I was wondering if there’s a function foo where you can do

nt = (a = 1, b ="Hello")

MyStruct = foo(nt) 

where MyStruct gives you a struct identifying the names and types from nt.

Put differently, I’m trying to avoid the need to write:

Base.@kwdef struct MyStruct
    a::Int64
    b::String
end

by using the functino foo that figures out the names and types.

The main purpose I would imagine for making a struct defined based on a NamedTuple would be in order to have your own type for dispatch without pirating functions on NamedTuple.

For this, I’ll instead propose you make a thin wrapper over NamedTuple. For an immutable structure, this is all you need for the wrapper approach:

struct MyStruct{NT<:NamedTuple}
	nt::NT
end

Base.getproperty(x::MyStruct,y::Symbol) = Base.getproperty(getfield(x,:nt),y)

# now test it
x = MyStruct((;a=1,b="two"))
x.b # can access via the fieldnames of the NamedTuple

Note that (with this definition of getproperty) the only way to access the nt field of MyStruct is via an explicit call to getfield (ie, x.nt will look for a nt field within the NamedTuple instead).

There will be all sorts of stumbling blocks if you want to define a standalone struct based on an instance of a NamedTuple. It’s literally possible, but will be based on eval and will have horrible performance even once you do get it “working”. It will also likely cause lots of world age errors when you try to use it.

4 Likes

Thanks!! this is what I was looking for

You can also just dispatch on NamedTuple (no need for a Struct) if you will be defining all your own functions. Here are two methods I use.

# Define the function to accept keyword arguments.
# Splat a named tuple to call it.
# Keyword argument order doesn't matter since they are named.
# `c...` will slurp up any unused elements of the named tuple.
function f(; b, a, c...)
    println(a+1)
    println(reverse(b))
end

# Define the function to accept normal positional arguments.
# Add a method which deconstructs the named tuple and calls the main method.
function g(a, b)
    println(a+2)
    println(uppercase(b))
end
g(nt::NamedTuple) = g(nt.a, nt.b)
julia> nt = (a=1, b="Hello", c=:extra1, d=:extra2)
(a = 1, b = "Hello", c = :extra1, d = :extra2)

julia> f(; nt...)
2
olleH

julia> g(nt)
3
HELLO
1 Like