How to initialize/construct deep nested structs in Julia?

Viewed 1266

I want to create some deeper nested structs. It seems the Setfield.jl package is sufficient to fill them. But I'm struggling with the initialization of the (empty) structs to fill them later iteratively. These are the structs looks like:

struct Bands
   B0::String
   B1::String
   B2::String
   B3::String
   B4::String
   B5::String
   .
   .
   .
   B25::String
end 


struct Masks
   mask1::Bands
   mask2::Bands
   mask3::Bands
   mask4::Bands
   mask5::Bands
   mask6::String
end

I've tried inner constructors and as described here https://docs.julialang.org/en/v1/manual/constructors/#Incomplete-Initialization-1. It seems that I have to set all values before like described at https://jw3126.github.io/Setfield.jl/stable/intro/. How can I initialize the nested struct, in that case 131 values at once?

2 Answers

You can create default constructors that just put empty objects for all fields which you can update later, for example

julia> struct Bands
           B0::String
           B1::String

           # Create a constructor that puts an empty String ("") for each field
           Bands() = new(("" for _ in 1:length(fieldnames(Bands)))...)

           # Define a vararg constructor
           Bands(args...) = new(args...)
       end

julia> struct Masks
           mask1::Bands
           mask2::Bands

           # Create a constructor that puts an empty Bands (Bands()) for each field
           Masks() = new((Bands() for _ in 1:length(fieldnames(Masks)))...)

           # Define a vararg constructor
           Masks(args...) = new(args...)
       end

julia> masks = Masks()
Masks(Bands("", ""), Bands("", ""))

julia> using Setfield

julia> masks = @set masks.mask1 = Bands("hello", "world") # Set a field of masks
Masks(Bands("hello", "world"), Bands("", ""))

julia> masks = @set masks.mask2.B0 = "hello" # Set a field of masks.mask2
Masks(Bands("hello", "world"), Bands("hello", ""))

For your scenario using Parameters.jl will result with a much cleaner code (additionally since String is immutable your Bands struct needs to be mutable.

using Parameters

@with_kw mutable struct Bands
    B0::String = ""
    B1::String = ""
end

@with_kw struct Masks
    mask1::Bands = Bands()
    mask2::Bands = Bands()
end

In this way your code is clean, readable and you do not need to define a huge load of constructors.

Related