Skip to content

Commit cd4f44d

Browse files
authored
Use similar in empty (JuliaLang#49958)
After this, ```julia julia> S = StructArray{Complex{Int}}(([1,2], [3,4])) 2-element StructArray(::Vector{Int64}, ::Vector{Int64}) with eltype Complex{Int64}: 1 + 3im 2 + 4im julia> empty(S) 0-element StructArray(::Vector{Int64}, ::Vector{Int64}) with eltype Complex{Int64} ``` instead of ```julia julia> empty(S) Complex{Int64}[] ``` This behavior matches the docstring now: "Create an empty vector similar to `v`". Often, `similar` will fall back to creating a `Vector`, so the current behavior will be preserved. ~I'm unsure about what test to add for this, so suggestions are welcome.~ Test added for a `StructArray` through a new test helper
1 parent c3836e1 commit cd4f44d

File tree

3 files changed

+53
-1
lines changed

3 files changed

+53
-1
lines changed

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ julia> empty([1.0, 2.0, 3.0], String)
881881
String[]
882882
```
883883
"""
884-
empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}()
884+
empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = similar(a, U, 0)
885885

886886
# like empty, but should return a mutable collection, a Vector by default
887887
emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}()

test/abstractarray.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ using Random, LinearAlgebra
55
isdefined(Main, :InfiniteArrays) || @eval Main include("testhelpers/InfiniteArrays.jl")
66
using .Main.InfiniteArrays
77

8+
isdefined(Main, :StructArrays) || @eval Main include("testhelpers/StructArrays.jl")
9+
using .Main.StructArrays
10+
811
A = rand(5,4,3)
912
@testset "Bounds checking" begin
1013
@test checkbounds(Bool, A, 1, 1, 1) == true
@@ -1019,6 +1022,16 @@ end
10191022
@test isempty(v)
10201023
@test isempty(v2::Vector{Int})
10211024
@test isempty(v3::Vector{Float64})
1025+
1026+
S = StructArrays.StructArray{Complex{Int}}((v, v))
1027+
for T in (Complex{Int}, ComplexF64)
1028+
S0 = empty(S, T)
1029+
@test S0 isa StructArrays.StructArray{T}
1030+
@test length(S0) == 0
1031+
end
1032+
S0 = empty(S, String)
1033+
@test S0 isa Vector{String}
1034+
@test length(S0) == 0
10221035
end
10231036

10241037
@testset "CartesianIndices" begin

test/testhelpers/StructArrays.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module StructArrays
2+
3+
struct StructArray{T,N,C <: Tuple{Vararg{AbstractArray{<:Any,N}}}} <: AbstractArray{T,N}
4+
components :: C
5+
6+
function StructArray{T,N,C}(components::C) where {T,N,C}
7+
fieldcount(T) == length(components) || throw(ArgumentError("number of components incompatible with eltype"))
8+
allequal(axes.(components)) || throw(ArgumentError("component arrays must have the same axes"))
9+
new{T,N,C}(components)
10+
end
11+
end
12+
13+
function StructArray{T}(components::Tuple{Vararg{AbstractArray{<:Any,N}}}) where {T,N}
14+
StructArray{T,N,typeof(components)}(components)
15+
end
16+
17+
Base.size(S::StructArray) = size(S.components[1])
18+
Base.axes(S::StructArray) = axes(S.components[1])
19+
function Base.getindex(S::StructArray{T,N}, inds::Vararg{Int,N}) where {T,N}
20+
vals = map(x -> x[inds...], S.components)
21+
T(vals...)
22+
end
23+
function Base.setindex!(S::StructArray{T,N}, val, inds::Vararg{Int,N}) where {T,N}
24+
vals = getfield.(Ref(convert(T, val)), fieldnames(T))
25+
for (A,v) in zip(S.components, vals)
26+
A[inds...] = v
27+
end
28+
S
29+
end
30+
31+
isnonemptystructtype(::Type{T}) where {T} = isstructtype(T) && fieldcount(T) != 0
32+
33+
function Base.similar(S::StructArray, ::Type{T}, dims::Tuple{Int, Vararg{Int}}) where {T}
34+
isnonemptystructtype(T) || return similar(S.components[1], T, dims)
35+
arrs = similar.(S.components, fieldtypes(T), Ref(dims))
36+
StructArray{T}(arrs)
37+
end
38+
39+
end

0 commit comments

Comments
 (0)