Skip to content

Commit

Permalink
Expand copy! to handle mixed nullability and views
Browse files Browse the repository at this point in the history
  • Loading branch information
cjprybol committed Nov 2, 2017
1 parent fe4fd47 commit c2fc9b0
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 7 deletions.
21 changes: 14 additions & 7 deletions src/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -389,17 +389,18 @@ end
# Methods preserving levels and more efficient than AbstractArray fallbacks
copy(A::CategoricalArray) = deepcopy(A)

function copy!(dest::CategoricalArray{T, N}, dstart::Integer,
src::CategoricalArray{T, N}, sstart::Integer,
n::Integer=length(src)-sstart+1) where {T, N}
function copy!(dest::CategoricalArray{<:Union{T,Null}, N}, dstart::Integer,
src::S, sstart::Integer, n::Integer=length(src)-sstart+1) where
{T,N,S<:Union{CategoricalArray{<:Union{T,Null},N},SubArray{A,B,C,D}} where {A,B,C<:CategoricalArray{<:Union{T,Null},N},D}}
destinds, srcinds = linearindices(dest), linearindices(src)
(dstart destinds && dstart+n-1 destinds) || throw(BoundsError(dest, dstart:dstart+n-1))
(sstart srcinds && sstart+n-1 srcinds) || throw(BoundsError(src, sstart:sstart+n-1))
n == 0 && return dest
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))

drefs = dest.refs
srefs = src.refs
srefs = isa(src, SubArray) ? [x.level for x in src] : src.refs
spool = isa(src, SubArray) ? src.parent.pool : src.pool

newlevels, ordered = mergelevels(isordered(dest), levels(dest), levels(src))
# Orderedness cannot be preserved if the source was unordered and new levels
Expand All @@ -412,7 +413,7 @@ function copy!(dest::CategoricalArray{T, N}, dstart::Integer,
if dstart == dstart == 1 && n == length(dest) == length(src)
# Set index to reflect refs
levels!(dest.pool, T[]) # Needed in case src and dest share some levels
levels!(dest.pool, index(src.pool))
levels!(dest.pool, index(spool))

# Set final levels in their visible order
levels!(dest.pool, newlevels)
Expand All @@ -421,7 +422,7 @@ function copy!(dest::CategoricalArray{T, N}, dstart::Integer,
else # More work to do: preserve some values (and therefore index)
levels!(dest.pool, newlevels)

indexmap = indexin(index(src.pool), index(dest.pool))
indexmap = indexin(index(spool), index(dest.pool))

@inbounds for i = 0:(n-1)
x = srefs[sstart+i]
Expand All @@ -433,9 +434,15 @@ function copy!(dest::CategoricalArray{T, N}, dstart::Integer,
dest
end

copy!(dest::CategoricalArray{T, N}, src::CategoricalArray{T, N}) where {T,N} =
CA_OR_CA_VIEW = Union{CategoricalArray, SubArray{A,B,C,D} where
{A,B,C<:CategoricalArray,D}}

copy!(dest::CategoricalArray, src::CA_OR_CA_VIEW) =
copy!(dest, 1, src, 1, length(src))

copy!(dest::CategoricalArray, dstart::Integer, src::CA_OR_CA_VIEW) =
copy!(dest, dstart, src, 1, length(src))

"""
similar(A::CategoricalArray, element_type=eltype(A), dims=size(A))
Expand Down
92 changes: 92 additions & 0 deletions test/13_arraycommon.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,98 @@ end
@test_throws BoundsError copy!(x, 1, y, 4, 2)
@test x a
end

@testset "copy non-nullable src into non-nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector(v), reverse(v))
dest = CategoricalVector{String}(3)
copy!(dest, src)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy nullable src into non-nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector{Union{Null, String}}(v), reverse(v))
dest = CategoricalVector{String}(3)
copy!(dest, src)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy non-nullable src into nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector(v), reverse(v))
dest = CategoricalVector{Union{String, Null}}(3)
copy!(dest, src)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy nullable src into nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector{Union{String, Null}}(v), reverse(v))
dest = CategoricalVector{Union{String, Null}}(3)
copy!(dest, src)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy non-nullable viewed src into non-nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector(v), reverse(v))
vsrc = view(src, 1:length(src))
dest = CategoricalVector{String}(3)
copy!(dest, vsrc)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy nullable viewed src into non-nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector{Union{String, Null}}(v), reverse(v))
vsrc = view(src, 1:length(src))
dest = CategoricalVector{String}(3)
copy!(dest, vsrc)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy non-nullable viewed src into nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector(v), reverse(v))
vsrc = view(src, 1:length(src))
dest = CategoricalVector{Union{String, Null}}(3)
copy!(dest, vsrc)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy nullable viewed src into nullable dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector{Union{String, Null}}(v), reverse(v))
vsrc = view(src, 1:length(src))
dest = CategoricalVector{Union{String, Null}}(3)
copy!(dest, vsrc)
@test dest == src
@test levels(dest) == levels(src) == reverse(v)
end

@testset "copy a viewed subset of src into dest" begin
v = ["a", "b", "c"]
src = levels!(CategoricalVector(v), reverse(v))
vsrc = view(src, 1:2)
dest = CategoricalVector{String}(3)
copy!(dest, vsrc)
@test dest[1:2] == src[1:2]
@test levels(dest) == levels(src)

vsrc = view(src, 1:2)
dest = CategoricalVector{String}(2)
copy!(dest, vsrc)
@test dest == src[1:2]
@test levels(dest) == levels(src)
end
end

@testset "resize!()" begin
Expand Down

0 comments on commit c2fc9b0

Please sign in to comment.