Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,13 @@ Base.getproperty(b::MyType, s::Symbol) = s == :data ? getfield(b, 1) : getproper
getnamestypes(::Type{NamedTuple{names, types}}) where {names, types} = (names, types)
getnamestypes(::Type{MyType{NT}}) where NT = getnamestypes(NT)

# explicitly give the "schema" of the object to StructArrays
function StructArrays.staticschema(::Type{T}) where {T<:MyType}
names, types = getnamestypes(T)
NamedTuple{(:data, names...), Base.tuple_type_cons(Float64, types)}
end

# generate an instance of MyType type
function StructArrays.createinstance(::Type{T}, x, args...) where {T<:MyType}
names, types = getnamestypes(T)
MyType(x, NamedTuple{names, types}(args))
Expand Down Expand Up @@ -303,3 +305,66 @@ julia> s
Foo(44, "d")
Foo(55, "e")
```
In the above example "for structures with non-standard data layout" our `MyType` was composed of `data` of type `Float64` and `rest` of type `NamedTuple`. In many practical cases where there are custom types involved it's hard for StructArrays to automatically widen the types incase the input we are receiving is heterogeneous in it's type nature. The following example demonstrates a widening method incase the types received are different.
```julia
struct MyType1{T, Names, Types}
data::T
rest::NamedTuple{Names, Types}
end

MyType1(x; kwargs...) = MyType1(x, values(kwargs))

# and a source of custom type data
struct location{U}
x::U
y::U
end
struct region{G<:Array{location}}
area::G
end

function Base.getproperty(x::MyType1, field::Symbol)
if field == :data
getfield(x, :data)
elseif field == :rest
getfield(x, :rest)
else
getproperty(getfield(x, :rest), field)
end
end

getnamestypes(::Type{MyType1{T, Names, Types}}) where {T, Names, Types} = (T, Names, Types)

# explicitly give the "schema" of the object to StructArrays
function StructArrays.staticschema(::Type{T}) where {T<:MyType1}
K, names, types = getnamestypes(T)
NamedTuple{(:data, names...), Base.tuple_type_cons(K, types)}
end

# generate an instance of MyType type
function StructArrays.createinstance(::Type{T}, x, args...) where {T<:MyType1}
K, names, types = getnamestypes(T)
MyType1(x, NamedTuple{names, types}(args))
end

s1 = MyType1(location(1, 0), (place = "Delhi"))
s2 = MyType1(location(2.5, 1.9), (place = "Mumbai"))
s3 = MyType1(region([location(1, 0), location(2.5, 1.9)]), (place = "North India"))

s = [s1, s2, s3]
# Now if we try to do StructArray(s)
# we will get an error

function meta_table(iter)
cols = Tables.columntable(iter)
meta_table(first(cols), Base.tail(cols))
end

function meta_table(data, rest::NamedTuple{names, types}) where {names, types}
F = MyType1{eltype(data), names, StructArrays.eltypes(types)}
return StructArray{F}(; data=data, rest...)
end

meta_table(s)
```
The above example has been tested and implemented in [GeometryBasics.jl](https://github.com/JuliaGeometry/GeometryBasics.jl).