Get value in nested data structure of arbitrary depth

Hi,

I would like to acess values of nested data structure with a Tuple representing the fields location in the data structure. The data structure can be of an arbitrary type and depth. Here is a MWE illustrating a few operations I would like to perform.

data = (a = 1, b = (a = [4,2], b = 3))
k1 = (:a,)
k2 = (:b, :a, 1)
function get_value(data, k)
    # get value at key k 
end
# returns 1
get_value(data, k1)
# returns 4
get_value(data, k2)

Is there a way to achieve this? Is recursion the right approach?

Thanks!

A combination of dispatch and recursion would work:

get_value(data::NamedTuple, k::Symbol) = getproperty(data, k)
get_value(data::AbstractVector, k::Int) = getindex(data, k)
function get_value(data, k::Tuple)
    if length(k) == 0
        data
    else
        get_value(get_value(data, k[1]), k[2:end])
    end
end

Alternatively, you could just use a loop:

function get_value2(data, ks)
    for k in ks
        data = get(data, k, missing)  # get works for many types and handles missing keys
    end
    data
end

Can you give some context? What is the application, and how is the tuple of fields being generated?

Thank you for your replies.

@bertschi, thank you. I think your second approach will work.

@stevengj, the context is a bit difficult to describe without going into a rabbit hole. Basically, I am trying to compare values in two data structures. I know the “keys” of each, but not the values at those keys.

It’s not quite as bullet-proof as I had hoped for. A more rigorous solution might be using lenses from Accessors.jl:

julia> l1 = PropertyLens(:a)  # describes path into data, i.e., like k1 = (:a,)
(@o _.a)

julia> l1(data)
1

julia> l2 = Accessors.opticcompose(PropertyLens(:b), PropertyLens(:a), IndexLens(1))
(@o _.b.a[1])

julia> l2(data)
4

Thanks for the update! Accessors.jl looks like a relevant package.