# Search nested namedtuples by key

**URL:** <https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352>\
**Category:** General Usage\
**Tags:** namedtuple, functors, fmap\
**Created:** [July 17, 2022, 11:04am UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352 "2022-07-17T11:04:28Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [July 17, 2022, 11:04am UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/1 "2022-07-17T11:04:28Z")

</div>

using the Functor package I quickly found how to search by value type, but not by key.  
Is it possible to do this (ie search by key) in some way or with the help of another package?

```julia
julia> nt=(gr='a',f1=1, f2=(gr='b', f21=21))
(gr = 'a', f1 = 1, f2 = (gr = 'b', f21 = 21))

julia> using Functors

julia> fmap(x->[99],nt, exclude= v -> v isa Char)
(gr = [99], f1 = 1, f2 = (gr = [99], f21 = 21))

julia> fmap(uppercase,nt, exclude= v -> v isa Char)
(gr = 'A', f1 = 1, f2 = (gr = 'B', f21 = 21))

```

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [July 17, 2022, 9:10pm UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/2 "2022-07-17T21:10:58Z")

</div>

First of all, it might be that `NamedTuple` is not the right type for your application.  
If you want to do a lot of alternation of your data at runtime, then a `Dict` might be better suited, since `NamedTuple`s lead to a bit of compilation each time.

However, the function `propertynames` gives you access to the keys and `values` gives you the values. With these, you can probably do whatever you want with pure Julia. Following your example,  
let’s say you want to recursively replace all occurrences of `gr` with `g`, then you could do

```julia
function replace_gr(nt::NamedTuple)
    new_keys = replace(propertynames(nt), :gr => :g )
    new_values = replace_gr.(values(nt))
    return NamedTuple{new_keys}(new_values)
end
replace_gr(nt) = nt # use multiple dispatch to ignore all non NamedTypes

replace_gr(nt) # -> (g = 'a', f1 = 1, f2 = (g = 'b', f21 = 21))

```

By the way, if you just want to replace a value, you could do `nt_replace_f1 = (; nt..., f1 = 10)`.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [July 17, 2022, 10:16pm UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/3 "2022-07-17T22:16:02Z")

</div>

Thanks for the tips on using dictionaries instead of namedtuples.  
the request was put in a discussion on Slack and specifically asked to act on the values of specific namedtuple keys nested at multiple levels.  
in the examples I have proposed, the result is obtained by checking the type of the value (Char in this case which is the type of value corresponding to the key: gr).  
What needs to be changed is the value not the key.  
PS  
In the meantime I have found my own solution based on a schema used in the rmap function of the NestedTuples.jl package.  
the technique seems to me similar to the one you propose: multiple dispatching and recursion are used to find the keys in all internal levels.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [July 18, 2022, 8:59am UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/4 "2022-07-18T08:59:11Z")

</div>

Here are some methods I have thought of to manage some cases.

```julia

rlmap(x, K, V) = x

function rlmap(nt::NamedTuple{N,T},K::Symbol, f::Function) where {N,T}
    NamedTuple{N}(map(((k,v),) -> k==K ? f(v) : rlmap(v,K, f), zip(keys(nt),values(nt))))
end

function rlmap(nt::NamedTuple{N,T},K::Symbol, V) where {N,T}
    NamedTuple{N}(map(((k,v),) -> k==K ? V : rlmap(v,K, V), zip(keys(nt),values(nt))))
end

function rlmap(nt::NamedTuple{N,T}, TTS::DataType, f:: Function) where {N,T}
  NamedTuple{N}(map(v -> v isa TTS ? f(v) : rlmap(v, TTS, f), values(nt)))
end

nt=(gr='a',f1=1, f2=(gr='b', f21=21), f3=(f31=31, f32=(f321=321, gr='c')))

rlmap(nt, :gr, 999)
rlmap(nt, :gr, uppercase)

rlmap(nt, Char, x->999)
rlmap(nt, Char, (_)->999)
rlmap(nt, Char, uppercase)

```

However, I would like to understand better the mechanism (forgive the improper use of the following terms) for which the intensive use of namedtuple slows down the execution because the compiler has to intervene.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [July 18, 2022, 10:01am UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/5 "2022-07-18T10:01:10Z")

</div>

Actually, I misunderstood your initial goal. I thought you wanted to change the keys dynamically.  
(But in your example it is more that you want to change the values depending on the key).

My point was just that `Dict{Symbol, Float64}` is one data type, independent of the value of the keys. Whereas, `NamedTuple{(:x,), Tuple{Float64}}` is another dataype as `NamedTuple{(:y,), Tuple{Float64}}`. Therefore, if you change the keys too often, you might generate a lot of different data types.

However, if your named tuples are constant most of the time then there is not much to worry about 😉  
And there are even packages to change the values of a named tuple, such as [Home · Accessors.jl](https://juliaobjects.github.io/Accessors.jl/stable/)

Below is a _very_ constructed example which shows bad use of namedtuples. (It’s more for fun and to demonstrate the effect.)

```julia
function test_dict(n, letters = (:A, :B, :C, :D)) 
    
    x = 0.0
    for i in 1:n 
        new_key = Symbol( rand(letters), rand(letters), rand(letters) )
        nt = Dict( new_key => rand() )
        x += sum(values(nt))
    end
    return x
end

function test_nt(n, letters = (:A, :B, :C, :D)) 
    x = 0.0
    for i in 1:n 
        new_key = Symbol( rand(letters), rand(letters), rand(letters) )
        nt = NamedTuple{(new_key,)}( (rand(),) ) # don't do this at home ;)
        x += sum(values(nt))
    end
    return x
end

@time test_dict(10) # 0.000033 seconds (70 allocations: 6.875 KiB)
@time test_nt(10) # 0.070186 seconds (22.57 k allocations: 1.354 MiB, 73.19% compilation time)

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [July 18, 2022, 10:15am UTC](https://discourse.julialang.org/t/search-nested-namedtuples-by-key/84352/6 "2022-07-18T10:15:09Z")

</div>

> [@SteffenPL](#):
>
> My point was just that `Dict{Symbol, Float64}` is one data type, independent of the value of the keys. Whereas, `NamedTuple{(:x,), Tuple{Float64}}` is another dataype as `NamedTuple{(:y,), Tuple{Float64}}`. Therefore, if you change the keys too often, you might generate a lot of different data types.

👌

> [@SteffenPL](#):
>
> However, if your named tuples are constant most of the time then there is not much to worry about 😉  
> And there are even packages to change the values of a named tuple, such as [Home · Accessors.jl](https://juliaobjects.github.io/Accessors.jl/stable/)

The problem was originally posed[here](https://app.slack.com/client/T68168MUP/C6A044SQH/thread/C6A044SQH-1657755882.288999) in the terms I have said.

Among the various proposals there were those to use fmap of the Functors package or to use some feature of Accessors.  
I have not been able to find functions of these packages that can be applied directly to the general case (i.e. change the values of a certain key at all levels of a namedtuple nested)
