Skip to content

Commit 031b1bc

Browse files
author
Pietro Vertechi
authored
switch to component interface (#167)
* switch to _getfield interface * fix tests * fix pooledarray doctest * _getfield cleanup * update docstrings * avoid fieldnames and fieldtypes * rename to component * rename fieldarrays to componens * test edge case of lazyrows collection * add fieldarrays deprecation * test interoperability with typed tables * typo * update news * unexport components * only run ci on linux
1 parent 0b82933 commit 031b1bc

File tree

15 files changed

+128
-71
lines changed

15 files changed

+128
-71
lines changed

.github/workflows/CI.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ jobs:
2020
- 'nightly'
2121
os:
2222
- ubuntu-latest
23-
- windows-latest
24-
- macos-latest
2523
arch:
2624
- x64
2725
steps:

NEWS.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
# NEWS
22

3+
## Version 0.6.0
4+
5+
### Breaking
6+
7+
- Renamed `fieldarrays` to `StructArrays.components` [#167](https://github.com/JuliaArrays/StructArrays.jl/pull/167)
8+
- `getproperty` is no longer used to access fields of a struct. It is replaced by `StructArrays.component(x, i)` [#167](https://github.com/JuliaArrays/StructArrays.jl/pull/167). This is only relevant for structs with custom layout.
9+
- Inner constructors are bypassed on `getindex` [#145](https://github.com/JuliaArrays/StructArrays.jl/pull/136)
10+
- Broadcast on `StructArray`s now returns a `StructArray` [#136](https://github.com/JuliaArrays/StructArrays.jl/pull/136)
11+
312
## Version 0.4.0
413

514
### Breaking
615

716
- `fieldarrays` now returns a tuple of arrays for a `StructArray{<:Tuple}`
817
- `push!` now only works if the `StructArray` and the element have the same propertynames
9-
- the special constructor `StructArray(first_col => last_col)` is no longer supported
18+
- The special constructor `StructArray(first_col => last_col)` is no longer supported
1019

1120
## Version 0.2.0
1221

@@ -19,4 +28,3 @@
1928

2029
- Added `collect_structarray` function to collect an iterable of structs into a `StructArray` without having to allocate an array of structs
2130
- `StructArray{T}(undef, dims)` and `StructArray(v::AbstractArray)` now support an `unwrap` keyword argument to specify on which types to do recursive unnesting of array of structs to struct of arrays
22-

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1818
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
1919
PooledArrays = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
2020
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
21+
TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9"
2122
WeakRefStrings = "ea10d353-3f73-51f8-a26c-33c1cb351aa5"
2223

2324
[targets]
24-
test = ["Test", "OffsetArrays", "PooledArrays", "WeakRefStrings", "Documenter"]
25+
test = ["Test", "OffsetArrays", "PooledArrays", "TypedTables", "WeakRefStrings", "Documenter"]

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ julia> s.re
3030
0.680079 0.92407
3131
0.874437 0.929336
3232

33-
julia> fieldarrays(s) # obtain all field arrays as a named tuple
33+
julia> StructArrays.components(s) # obtain all field arrays as a named tuple
3434
(re = [0.680079 0.92407; 0.874437 0.929336], im = [0.625239 0.267358; 0.737254 0.804478])
3535
```
3636

@@ -183,7 +183,9 @@ julia> map(t -> t.b ^ t.a, LazyRows(t))
183183

184184
## Advanced: structures with non-standard data layout
185185

186-
StructArrays support structures with non-standard data layout (where `getproperty` has been overloaded or where the constructors are non-standard). The user is required to provide an overloaded `staticschema` for their type (to give the names and types of the properties of a given type) as well as a `createinstance` method. Here is an example of a type `MyType` that has as properties either its field `data` or properties of its field `rest` (which is a named tuple):
186+
StructArrays support structures with custom data layout. The user is required to overload `staticschema` in order to define the custom layout, `component` to access fields of the custom layout, and `createinstance(T, fields...)` to create an instance of type `T` from its custom fields `fields`. In other word, given `x::T`, `createinstance(T, (component(x, f) for f in fieldnames(staticschema(T)))...)` should successfully return an instance of type `T`.
187+
188+
Here is an example of a type `MyType` that has as custom fields either its field `data` or fields of its field `rest` (which is a named tuple):
187189

188190
```julia
189191
using StructArrays
@@ -195,17 +197,17 @@ end
195197

196198
MyType(x; kwargs...) = MyType(x, values(kwargs))
197199

198-
Base.getproperty(b::MyType, s::Symbol) = s == :data ? getfield(b, 1) : getproperty(getfield(b, 2), s)
199-
Base.propertynames(b::MyType) = (:data, propertynames(getfield(b, 2))...)
200-
201-
# explicitly give the "schema" of the object to StructArrays
202200
function StructArrays.staticschema(::Type{MyType{T, NamedTuple{names, types}}}) where {T, names, types}
203-
NamedTuple{(:data, names...), Base.tuple_type_cons(T, types)}
201+
return NamedTuple{(:data, names...), Base.tuple_type_cons(T, types)}
202+
end
203+
204+
function StructArrays.component(m::MyType, key::Symbol)
205+
return key === :data ? getfield(m, 1) : getfield(getfield(m, 2), key)
204206
end
205207

206208
# generate an instance of MyType type
207209
function StructArrays.createinstance(::Type{MyType{T, NT}}, x, args...) where {T, NT}
208-
MyType(x, NT(args))
210+
return MyType(x, NT(args))
209211
end
210212

211213
s = [MyType(rand(), a=1, b=2) for i in 1:10]

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
33
PooledArrays = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
4+
5+
[compat]
6+
PooledArrays = "1"

docs/src/index.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ collect_structarray
2323
# Accessors
2424

2525
```@docs
26-
fieldarrays
26+
StructArrays.components
2727
```
2828

2929
# Lazy iteration
@@ -38,7 +38,13 @@ LazyRows
3838
```@docs
3939
StructArrays.append!!
4040
StructArrays.replace_storage
41+
```
42+
43+
# Interface
44+
45+
```@docs
4146
StructArrays.staticschema
47+
StructArrays.component
4248
StructArrays.createinstance
4349
```
4450

src/StructArrays.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module StructArrays
33
using Base: tuple_type_cons, tuple_type_head, tuple_type_tail, tail
44

55
export StructArray, StructVector, LazyRow, LazyRows
6-
export collect_structarray, fieldarrays
6+
export collect_structarray
77
export replace_storage
88

99
include("interface.jl")
@@ -18,10 +18,10 @@ include("tables.jl")
1818
import DataAPI: refarray, refvalue
1919
using DataAPI: defaultarray
2020

21-
refarray(s::StructArray) = StructArray(map(refarray, fieldarrays(s)))
21+
refarray(s::StructArray) = StructArray(map(refarray, components(s)))
2222

2323
function refvalue(s::StructArray{T}, v::Tup) where {T}
24-
createinstance(T, map(refvalue, fieldarrays(s), v)...)
24+
createinstance(T, map(refvalue, components(s), v)...)
2525
end
2626

2727
# Use Adapt allows for automatic conversion of CPU to GPU StructArrays

src/collect.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function _widenstructarray(dest::StructArray, i, ::Type{T}) where {T}
113113
sch = hasfields(T) ? staticschema(T) : nothing
114114
sch !== nothing && fieldnames(sch) == propertynames(dest) || return _widenarray(dest, i, T)
115115
types = ntuple(x -> fieldtype(sch, x), fieldcount(sch))
116-
cols = Tuple(fieldarrays(dest))
116+
cols = Tuple(components(dest))
117117
newcols = map((a, b) -> _widenstructarray(a, i, b), cols, types)
118118
return StructArray{T}(newcols)
119119
end

src/interface.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
const Tup = Union{Tuple, NamedTuple}
22
const EmptyTup = Union{Tuple{}, NamedTuple{(), Tuple{}}}
33

4+
"""
5+
StructArrayys.component(x, i)
6+
7+
Default to `getfield`. It should be overloaded for custom types with a custom
8+
schema. See [`StructArrays.staticschema`](@ref).
9+
"""
10+
component(x, i) = getfield(x, i)
11+
412
"""
513
StructArrays.staticschema(T)
614
@@ -9,8 +17,9 @@ The default schema for an element type `T`. A schema is a `Tuple` or
917
this will have fields with the same names and types as `T`.
1018
1119
This can be overloaded for custom types if required, in which case
12-
[`StructArrays.createinstance`](@ref) should also be defined.
13-
20+
[`StructArrays.component`](@ref) and [`StructArrays.createinstance`](@ref)
21+
should also be defined.
22+
1423
```julia-repl
1524
julia> StructArrays.staticschema(Complex{Float64})
1625
NamedTuple{(:re, :im),Tuple{Float64,Float64}}

src/lazy.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ Base.propertynames(c::LazyRow) = propertynames(getfield(c, 1))
4545
function Base.show(io::IO, c::LazyRow)
4646
print(io, "LazyRow")
4747
columns, index = getfield(c, 1), getfield(c, 2)
48-
tup = StructArray(fieldarrays(columns))[index]
48+
tup = StructArray(components(columns))[index]
4949
show(io, tup)
5050
end
5151

52+
@inline Base.@propagate_inbounds component(l::LazyRow, key) = getproperty(l, key)
53+
5254
staticschema(::Type{<:LazyRow{T}}) where {T} = staticschema(T)
5355
buildfromschema(f, ::Type{<:LazyRow{T}}) where {T} = buildfromschema(f, T)
54-
5556
iscompatible(::Type{<:LazyRow{R}}, ::Type{S}) where {R, S<:StructArray} = iscompatible(R, S)
5657

5758
(s::ArrayInitializer)(::Type{<:LazyRow{T}}, d) where {T} = buildfromschema(typ -> s(typ, d), T)
@@ -74,11 +75,16 @@ struct LazyRows{T, N, C, I} <: AbstractArray{LazyRow{T, N, C, I}, N}
7475
columns::StructArray{T, N, C, I}
7576
end
7677
Base.parent(v::LazyRows) = getfield(v, 1)
77-
fieldarrays(v::LazyRows) = fieldarrays(parent(v))
78+
components(v::LazyRows) = components(parent(v))
79+
80+
component(v::LazyRows, key) = component(parent(v), key)
81+
82+
staticschema(::Type{LazyRows{T, N, C, I}}) where {T, N, C, I} = staticschema(C)
83+
createinstance(::Type{<:LazyRows{T}}, args...) where {T} = LazyRows(StructArray{T}(args))
7884

79-
Base.getproperty(s::LazyRows, key::Symbol) = getproperty(parent(s), key)
80-
Base.getproperty(s::LazyRows, key::Int) = getproperty(parent(s), key)
81-
Base.propertynames(c::LazyRows) = propertynames(parent(c))
85+
Base.getproperty(v::LazyRows, key::Symbol) = component(v, key)
86+
Base.getproperty(v::LazyRows, key::Int) = component(v, key)
87+
Base.propertynames(v::LazyRows) = propertynames(parent(v))
8288

8389
Base.size(v::LazyRows) = size(parent(v))
8490
Base.getindex(v::LazyRows{<:Any, <:Any, <:Any, Int}, i::Int) = LazyRow(parent(v), i)
@@ -91,6 +97,6 @@ end
9197

9298
function Base.showarg(io::IO, s::LazyRows{T}, toplevel) where T
9399
print(io, "LazyRows")
94-
showfields(io, Tuple(fieldarrays(s)))
100+
showfields(io, Tuple(components(s)))
95101
toplevel && print(io, " with eltype LazyRow{", T, "}")
96102
end

0 commit comments

Comments
 (0)