Skip to content

Commit 6d08e4b

Browse files
author
Pietro Vertechi
authored
coverage and clean up (#79)
* Tests and cleanup * Move things in correct places * Switch to _promote_typejoin * typo
1 parent 1dc78e9 commit 6d08e4b

File tree

7 files changed

+55
-41
lines changed

7 files changed

+55
-41
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ julia> map(t -> t.b ^ t.a, LazyRows(t))
182182

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

185-
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 `staticscheme` 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):
185+
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):
186186

187187
```julia
188188
using StructArrays

src/collect.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,6 @@ function grow_to_structarray!(dest::AbstractArray, itr, elem = iterate(itr))
109109
return dest
110110
end
111111

112-
function to_structarray(::Type{T}, nt::C) where {T, C<:Tup}
113-
S = add_params(T, eltypes(astuple(C)))
114-
return StructArray{S}(nt)
115-
end
116-
117112
widenstructarray(dest::AbstractArray, i, el::S) where {S} = widenstructarray(dest, i, S)
118113

119114
function widenstructarray(dest::StructArray{T}, i, ::Type{S}) where {T, S}
@@ -123,7 +118,8 @@ function widenstructarray(dest::StructArray{T}, i, ::Type{S}) where {T, S}
123118
cols = fieldarrays(dest)
124119
if names == propertynames(cols)
125120
nt = map((a, b) -> widenstructarray(a, i, b), cols, strip_params(sch)(types))
126-
return to_structarray(T, nt)
121+
ST = _promote_typejoin(S, T)
122+
return StructArray{ST}(nt)
127123
else
128124
return widenarray(dest, i, S)
129125
end

src/interface.jl

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,5 @@ end
99

1010
staticschema(::Type{T}) where {T<:Tup} = T
1111

12-
astuple(::Type{NamedTuple{names, types}}) where {names, types} = types
13-
astuple(::Type{T}) where {T<:Tuple} = T
14-
15-
function fields(::Type{T}) where {T}
16-
fieldnames(staticschema(T))
17-
end
18-
19-
strip_params(::Type{<:Tuple}) = Tuple
20-
strip_params(::Type{<:NamedTuple{names}}) where {names} = NamedTuple{names}
12+
createinstance(::Type{T}, args...) where {T} = T(args...)
13+
createinstance(::Type{T}, args...) where {T<:Union{Tuple, NamedTuple}} = T(args)

src/lazy.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ end
2424
staticschema(::Type{<:LazyRow{T}}) where {T} = staticschema(T)
2525
buildfromschema(f, ::Type{<:LazyRow{T}}) where {T} = buildfromschema(f, T)
2626

27-
iscompatible(::Type{<:LazyRow{S}}, ::Type{StructArray{T, N, C}}) where {S, T, N, C} =
28-
iscompatible(S, StructArray{T, N, C})
27+
iscompatible(::Type{<:LazyRow{R}}, ::Type{S}) where {R, S<:StructArray} = iscompatible(R, S)
2928

3029
(s::ArrayInitializer)(::Type{<:LazyRow{T}}, d) where {T} = buildfromschema(typ -> s(typ, d), T)
3130

src/structarray.jl

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Base.convert(::Type{StructArray}, v::StructArray) = v
8181
Base.convert(::Type{StructVector}, v::AbstractVector) = StructVector(v)
8282
Base.convert(::Type{StructVector}, v::StructVector) = v
8383

84-
function Base.similar(::Type{StructArray{T, N, C}}, sz::Dims) where {T, N, C}
84+
function Base.similar(::Type{<:StructArray{T, <:Any, C}}, sz::Dims) where {T, C}
8585
buildfromschema(typ -> similar(typ, sz), T, C)
8686
end
8787

@@ -171,12 +171,6 @@ function Base.copyto!(I::StructArray, doffs::Integer, J::StructArray, soffs::Int
171171
return I
172172
end
173173

174-
function Base.cat(args::StructArray...; dims)
175-
f = key -> cat((getproperty(t, key) for t in args)...; dims=dims)
176-
T = mapreduce(eltype, promote_type, args)
177-
StructArray{T}(map(f, fields(eltype(args[1]))))
178-
end
179-
180174
function Base.resize!(s::StructArray, i::Integer)
181175
for a in fieldarrays(s)
182176
resize!(a, i)
@@ -188,12 +182,12 @@ function Base.empty!(s::StructArray)
188182
foreachfield(empty!, s)
189183
end
190184

191-
for op in [:hcat, :vcat]
185+
for op in [:cat, :hcat, :vcat]
192186
@eval begin
193-
function Base.$op(args::StructArray...)
194-
f = key -> $op((getproperty(t, key) for t in args)...)
187+
function Base.$op(args::StructArray...; kwargs...)
188+
f = key -> $op((getproperty(t, key) for t in args)...; kwargs...)
195189
T = mapreduce(eltype, promote_type, args)
196-
StructArray{T}(map(f, fields(eltype(args[1]))))
190+
StructArray{T}(map(f, propertynames(args[1])))
197191
end
198192
end
199193
end

src/utils.jl

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,13 @@ end
4646

4747
foreachfield(f, x::T, xs...) where {T} = foreachfield(staticschema(T), f, x, xs...)
4848

49-
createinstance(::Type{T}, args...) where {T} = T(args...)
50-
createinstance(::Type{T}, args...) where {T<:Union{Tuple, NamedTuple}} = T(args)
51-
52-
add_params(::Type{T}, ::Type{C}) where {T, C<:Tuple} = T
53-
add_params(::Type{T}, ::Type{C}) where {T<:Tuple, C<:Tuple} = C
54-
add_params(::Type{<:NamedTuple{names}}, ::Type{C}) where {names, C<:Tuple} = NamedTuple{names, C}
55-
add_params(::Type{<:Pair}, ::Type{Tuple{S, T}}) where {S, T} = Pair{S, T}
56-
5749
"""
5850
`iscompatible(::Type{S}, ::Type{V}) where {S, V<:AbstractArray}`
5951
6052
Check whether element type `S` can be pushed to a container of type `V`.
6153
"""
6254
iscompatible(::Type{S}, ::Type{<:AbstractArray{T}}) where {S, T} = S<:T
63-
iscompatible(::Type{S}, ::Type{StructArray{T, N, C}}) where {S, T, N, C} = iscompatible(astuple(staticschema(S)), astuple(C))
55+
iscompatible(::Type{S}, ::Type{<:StructArray{<:Any, <:Any, C}}) where {S, C} = iscompatible(astuple(staticschema(S)), astuple(C))
6456

6557
iscompatible(::Type{Tuple{}}, ::Type{T}) where {T<:Tuple} = false
6658
iscompatible(::Type{T}, ::Type{Tuple{}}) where {T<:Tuple} = false
@@ -72,6 +64,26 @@ end
7264

7365
iscompatible(::S, ::T) where {S, T<:AbstractArray} = iscompatible(S, T)
7466

67+
function _promote_typejoin(::Type{S}, ::Type{T}) where {S<:NTuple{N, Any}, T<:NTuple{N, Any}} where N
68+
head = _promote_typejoin(Base.tuple_type_head(S), Base.tuple_type_head(T))
69+
tail = _promote_typejoin(Base.tuple_type_tail(S), Base.tuple_type_tail(T))
70+
return Base.tuple_type_cons(head, tail)
71+
end
72+
73+
_promote_typejoin(::Type{Tuple{}}, ::Type{Tuple{}}) = Tuple{}
74+
function _promote_typejoin(::Type{NamedTuple{names, types}}, ::Type{NamedTuple{names, types′}}) where {names, types, types′}
75+
T = _promote_typejoin(types, types′)
76+
return NamedTuple{names, T}
77+
end
78+
79+
_promote_typejoin(::Type{S}, ::Type{T}) where {S, T} = Base.promote_typejoin(S, T)
80+
81+
function _promote_typejoin(::Type{Pair{A, B}}, ::Type{Pair{A′, B′}}) where {A, A′, B, B′}
82+
C = _promote_typejoin(A, A′)
83+
D = _promote_typejoin(B, B′)
84+
return Pair{C, D}
85+
end
86+
7587
function replace_storage(f, v::AbstractArray{T, N})::AbstractArray{T, N} where {T, N}
7688
f(v)
7789
end
@@ -106,9 +118,15 @@ function replace_storage(f, s::StructArray{T}) where T
106118
StructArray{T}(newcols)
107119
end
108120

109-
to_tup(c::T) where {T} = to_tup(c, fields(T))
121+
to_tup(c::T) where {T} = to_tup(c, fieldnames(staticschema(T)))
110122
function to_tup(c, fields::NTuple{N, Symbol}) where N
111123
t = ntuple(i -> getproperty(c, fields[i]), N)
112124
return NamedTuple{fields}(t)
113125
end
114126
to_tup(c, fields::NTuple{N, Int}) where {N} = ntuple(i -> _getproperty(c, fields[i]), N)
127+
128+
astuple(::Type{NamedTuple{names, types}}) where {names, types} = types
129+
astuple(::Type{T}) where {T<:Tuple} = T
130+
131+
strip_params(::Type{<:Tuple}) = Tuple
132+
strip_params(::Type{<:NamedTuple{names}}) where {names} = NamedTuple{names}

test/runtests.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,25 @@ end
2020

2121
@testset "indexstyle" begin
2222
@inferred IndexStyle(StructArray(a=rand(10,10), b=view(rand(100,100), 1:10, 1:10)))
23-
T = typeof(StructArray(a=rand(10,10), b=view(rand(100,100), 1:10, 1:10)))
23+
s = StructArray(a=rand(10,10), b=view(rand(100,100), 1:10, 1:10))
24+
T = typeof(s)
2425
@test IndexStyle(T) === IndexCartesian()
26+
@test StructArrays._best_index(T) == CartesianIndex{2}
27+
@test s[100] == s[10, 10] == (a=s.a[10,10], b=s.b[10,10])
28+
s[100] = (a=1, b=1)
29+
@test s[100] == s[10, 10] == (a=1, b=1)
30+
s[10, 10] = (a=0, b=0)
31+
@test s[100] == s[10, 10] == (a=0, b=0)
2532
@inferred IndexStyle(StructArray(a=rand(10,10), b=rand(10,10)))
26-
T = typeof(StructArray(a=rand(10,10), b=rand(10,10)))
33+
s = StructArray(a=rand(10,10), b=rand(10,10))
34+
T = typeof(s)
2735
@test IndexStyle(T) === IndexLinear()
36+
@test StructArrays._best_index(T) == Int
37+
@test s[100] == s[10, 10] == (a=s.a[10,10], b=s.b[10,10])
38+
s[100] = (a=1, b=1)
39+
@test s[100] == s[10, 10] == (a=1, b=1)
40+
s[10, 10] = (a=0, b=0)
41+
@test s[100] == s[10, 10] == (a=0, b=0)
2842
end
2943

3044
@testset "replace_storage" begin

0 commit comments

Comments
 (0)