Custom Data Layouts
Working with nested data-layouts
Sometimes you have nested data but want to treat it as a flattened struct, e.g. adapting an example from the StructArrays.jl docs
struct MyType{T, NT<:NamedTuple}
x::T
rest::NT
end
MyType(x; kwargs...) = MyType(x, values(kwargs))
function Base.getproperty(s::MyType, prop::Symbol)
if prop == :x
getfield(s, prop)
else
getfield(getfield(s, :rest), prop)
end
end
Base.propertynames(s::MyType) = (:data, propertynames(getfield(x, :rest))) julia> mt = MyType(1.0; a=1, b=2)
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(1.0, (a = 1, b = 2))
julia> mt.a
1
julia> mt.b
2We can support this 'flattened' structure in FieldViews by defining a custom method on fieldmap that tells FieldViews how to traverse the nested fields.
To teach FieldViews how to handle MyType, we'd do
function FieldViews.fieldmap(::Type{MyType{T, NamedTuple{rest_names, rest_types}}}) where {T, rest_names, rest_types}
(:x, map(name -> :rest => name, rest_names)...)
endjulia> FieldViews.fieldmap(typeof(mt))
(:x, :rest => :a, :rest => :b)This says that there is one field :x which is not redirected, and two inner fields :a and :b which are redirected from :rest. Now our nested struct is compatible with FieldViews.jl
julia> s = FieldViewable([MyType(i/5, a=6-i, b=2) for i in 1:5])
5-element FieldViewable{MyType{Float64, @NamedTuple{a::Int64, b::Int64}}, 1, Vector{MyType{Float64, @NamedTuple{a::Int64, b::Int64}}}}:
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(0.2, (a = 5, b = 2))
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(0.4, (a = 4, b = 2))
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(0.6, (a = 3, b = 2))
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(0.8, (a = 2, b = 2))
MyType{Float64, @NamedTuple{a::Int64, b::Int64}}(1.0, (a = 1, b = 2))
julia> s.x[1]
0.2
julia> s.a[2]
4
julia> s.b[3]
2Renaming fields
In addition to flattening out nested field structures, FieldViews.jl is also able to support "renamed" fields, e.g.
struct Foo
a::Int
data::@NamedTuple{_b::Int, _c::Int}
end
function FieldViews.fieldmap(::Type{Foo})
(:a, :data => Renamed(:_b, :b), :data => Renamed(:_c, :c))
endjulia> v = FieldViewable([Foo(1, (_b=1, _c=2))]);
julia> v.c
1-element FieldView{:c, Int64, 1, Foo, Vector{Foo}}:
2This is used for e.g. StaticArray support to rename Tuple fields to :x, :y, :z, :w.