Skip to content

Commit 827907e

Browse files
author
Michael Abbott
committed
re-write indexing of key vectors
1 parent 77ec916 commit 827907e

File tree

2 files changed

+54
-27
lines changed

2 files changed

+54
-27
lines changed

src/struct.jl

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,53 +58,78 @@ Base.eachindex(A::KeyedArray) = eachindex(parent(A))
5858

5959
Base.keys(A::KeyedArray) = error("Base.keys(::KeyedArray) not defined, please open an issue if this happens unexpectedly.")
6060

61-
for (bget, rget, cpy) in [(:getindex, :keys_getindex, :copy), (:view, :keys_view, :identity)]
61+
for (get_or_view, key_get, maybe_copy) in [
62+
(:getindex, :keys_getindex, :copy),
63+
(:view, :keys_view, :identity)
64+
]
6265
@eval begin
6366

64-
@inline function Base.$bget(A::KeyedArray, I::Integer...)
65-
@boundscheck checkbounds(parent(A), I...)
66-
@inbounds Base.$bget(parent(A), I...)
67-
end
67+
@inline function Base.$get_or_view(A::KeyedArray, raw_inds...)
68+
inds = to_indices(A, raw_inds)
69+
@boundscheck checkbounds(parent(A), inds...)
70+
data = @inbounds $get_or_view(parent(A), inds...)
71+
data isa AbstractArray || return data # scalar output
72+
73+
raw_keys = $key_get(axiskeys(A), inds)
74+
raw_keys === () && return data # things like A[A .> 0]
6875

69-
@inline function Base.$bget(A::KeyedArray, I::Union{Colon, CartesianIndex, BitArray})
70-
@boundscheck checkbounds(parent(A), I)
71-
@inbounds Base.$bget(parent(A), I)
76+
new_keys = ntuple(ndims(data)) do d
77+
isnothing(raw_keys) && return axes(data, d)
78+
raw_keys[d]
79+
end
80+
KeyedArray(data, new_keys)
7281
end
7382

74-
@inline @propagate_inbounds function Base.$bget(A::KeyedArray, Iraw...)
75-
I = Base.to_indices(A, Iraw) # allows InvertedIndices.jl
83+
# drop all, for A[:] and A[A .> 0] with ndims>=2
84+
@inline $key_get(keys::Tuple{Any, Any, Vararg{Any}}, inds::Tuple{Base.LogicalIndex}) = ()
85+
@inline $key_get(keys::Tuple{Any, Any, Vararg{Any}}, inds::Tuple{Base.Slice}) = ()
7686

77-
@boundscheck checkbounds(parent(A), I...)
78-
data = @inbounds Base.$bget(parent(A), I...)
87+
# drop one, for integer index
88+
@inline $key_get(keys::Tuple, inds::Tuple{Integer, Vararg{Any}}) =
89+
$key_get(tail(keys), tail(inds))
7990

80-
@boundscheck map(checkbounds, axiskeys(A), I)
81-
new_keys = $rget(axiskeys(A), I)
91+
# from a Colon, k[:] would copy too, but this avoids view([1,2,3], :)
92+
@inline $key_get(keys::Tuple, inds::Tuple{Base.Slice, Vararg{Any}}) =
93+
($maybe_copy(first(keys)), $key_get(tail(keys), tail(inds))...)
8294

83-
new_keys isa Tuple{} ? data : KeyedArray(data, new_keys)
95+
# this avoids making views of 1:10 etc, they are immutable anyway
96+
@inline function $key_get(keys::Tuple, inds::Tuple{AbstractVector, Vararg{Any}})
97+
got = if first(keys) isa AbstractRange
98+
@inbounds getindex(first(keys), first(inds))
99+
else
100+
@inbounds $get_or_view(first(keys), first(inds))
101+
end
102+
(got, $key_get(tail(keys), tail(inds))...)
84103
end
85104

86-
@inline function $rget(keys, inds)
87-
got = map(keys, inds) do r,i
88-
i isa Integer && return nothing
89-
i isa Colon && return $cpy(r) # avoids view([1,2,3], :)
90-
r isa AbstractRange && return getindex(r,i) # don't make views of 1:10
91-
return @inbounds $bget(r,i)
92-
end
93-
filter(r -> r isa AbstractArray, got)
105+
# newindex=[CartesianIndex{0}()], uses up one ind, sets N keys to default i.e. axes
106+
@inline function $key_get(keys::Tuple, inds::Tuple{AbstractVector{CartesianIndex{0}}, Vararg{Any}})
107+
(OneTo(1), $key_get(keys, tail(inds))...)
94108
end
109+
@inline function $key_get(keys::Tuple, inds::Tuple{AbstractVector{CartesianIndex{N}}, Vararg{Any}}) where {N}
110+
_, keys_left = Base.IteratorsMD.split(keys, Val(N))
111+
(ntuple(_->nothing, N)..., $key_get(keys_left, tail(inds))...)
112+
end
113+
114+
# terminating case, trailing 1s (already checked) could be left over
115+
@inline $key_get(keys::Tuple{}, inds::Tuple{}) = ()
116+
@inline $key_get(keys::Tuple{}, inds::Tuple{Integer, Vararg{Any}}) = ()
95117

96118
end
97119
end
98120

99-
@inline function Base.setindex!(A::KeyedArray, val, I...)
121+
@inline function Base.setindex!(A::KeyedArray, val, raw_inds...)
122+
I = Base.to_indices(A, raw_inds)
100123
@boundscheck checkbounds(A, I...)
101124
@inbounds setindex!(parent(A), val, I...)
102125
val
103126
end
104127

105-
@inline function Base.Broadcast.dotview(A::KeyedArray, I...)
128+
@inline function Base.dotview(A::KeyedArray, raw_inds...)
129+
I = Base.to_indices(A, raw_inds)
106130
@boundscheck checkbounds(A, I...)
107-
@inbounds Base.dotview(parent(A), I...)
131+
@inbounds setindex!(parent(A), val, I...)
132+
val
108133
end
109134

110135
"""

test/_basic.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ using Test, AxisKeys
2020
@test R[1,1] == 321
2121

2222
@test R[:] == vec(R.data)
23-
@test_broken R[1:2, 1, 1] == R.data[1:2, 1, 1] # trailing 1s are broken
23+
@test R[1:2, 1, 1] == R.data[1:2, 1, 1]
2424
@test axiskeys(R[:, [0.9,0.1,0.9,0.1] .> 0.5],2) == [10,30]
2525
@test ndims(R[R.data .> 0.5]) == 1
26+
newaxis = [CartesianIndex{0}()]
27+
@test axiskeys(R[[1,3],newaxis,:]) == (['a', 'c'], Base.OneTo(1), 10:10:40)
2628

2729
@test_throws Exception R(:nope) # ideally ArgumentError
2830
@test_throws Exception R('z') # ideally BoundsError?

0 commit comments

Comments
 (0)