Skip to content

Commit

Permalink
extend KeyPath to allow CartesianIndex keys
Browse files Browse the repository at this point in the history
  • Loading branch information
jondeuce committed Nov 27, 2024
1 parent 09bf849 commit 1b8a810
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 3 deletions.
9 changes: 6 additions & 3 deletions src/keypath.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
using Base: tail

KeyT = Union{Symbol, AbstractString, Integer}
KeyT = Union{Symbol, AbstractString, Integer, CartesianIndex}

"""
KeyPath(keys...)
A type for representing a path of keys to a value in a nested structure.
Can be constructed with a sequence of keys, or by concatenating other `KeyPath`s.
Keys can be of type `Symbol`, `String`, or `Int`.
Keys can be of type `Symbol`, `String`, `Int`, or `CartesianIndex`.
For custom types, access through symbol keys is assumed to be done with `getproperty`.
For consistency, the method `Base.propertynames` is used to get the viable property names.
For string and integer keys instead, the access is done with `getindex`.
For string, integer, and cartesian index keys, the access is done with `getindex` instead.
See also [`getkeypath`](@ref), [`haskeypath`](@ref).
Expand Down Expand Up @@ -85,18 +85,21 @@ end
keypathstr(kp::KeyPath) = join(kp.keys, ".")

_getkey(x, k::Integer) = x[k]
_getkey(x::AbstractArray, k::CartesianIndex) = x[k]
_getkey(x, k::Symbol) = getproperty(x, k)
_getkey(x::AbstractDict, k::Symbol) = x[k]
_getkey(x, k::AbstractString) = x[k]

_setkey!(x, k::Integer, v) = (x[k] = v)
_setkey!(x::AbstractArray, k::CartesianIndex, v) = (x[k] = v)
_setkey!(x, k::Symbol, v) = setproperty!(x, k, v)
_setkey!(x::AbstractDict, k::Symbol, v) = (x[k] = v)
_setkey!(x, k::AbstractString, v) = (x[k] = v)

_haskey(x, k::Integer) = haskey(x, k)
_haskey(x::Tuple, k::Integer) = 1 <= k <= length(x)
_haskey(x::AbstractArray, k::Integer) = 1 <= k <= length(x) # TODO: extend to generic indexing
_haskey(x::AbstractArray, k::CartesianIndex) = checkbounds(Bool, x, k)
_haskey(x, k::Symbol) = k in propertynames(x)
_haskey(x::AbstractDict, k::Symbol) = haskey(x, k)
_haskey(x, k::AbstractString) = haskey(x, k)
Expand Down
13 changes: 13 additions & 0 deletions test/keypath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
kp = KeyPath(:b, :c, 2)
@test getkeypath(x, kp) == 7

x = [(a=1,) (b=2,)]
@test getkeypath(x, KeyPath(CartesianIndex(1, 1), :a)) == 1
@test getkeypath(x, KeyPath(CartesianIndex(1, 2), :b)) == 2

@testset "access through getproperty" begin
x = Tkp(3, Dict(:c => 4, :d => 5), 6);

Expand All @@ -65,6 +69,10 @@
kp = KeyPath(:b, :c, 2)
setkeypath!(x, kp, 17)
@test x.b.c[2] == 17

x = [(a=1,) (b=2,)]
setkeypath!(x, KeyPath(CartesianIndex(1, 2)), (c=3,))
@test x[2] == (c=3,)
end

@testset "haskeypath" begin
Expand All @@ -75,6 +83,11 @@
@test !haskeypath(x, KeyPath(:b, "d", 4))
@test !haskeypath(x, KeyPath(:b, "e"))

x = [(a=1,) (b=2,)]
@test haskeypath(x, KeyPath(CartesianIndex(1, 1)))
@test haskeypath(x, KeyPath(CartesianIndex(1, 2)))
@test !haskeypath(x, KeyPath(CartesianIndex(1, 3)))

@testset "access through getproperty" begin
x = Tkp(3, Dict(:c => 4, :d => 5), 6);

Expand Down

0 comments on commit 1b8a810

Please sign in to comment.