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
2 Likes

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
1 Like

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