JeffreySarnoff / NamedTupleTools.jl

some utilities for working with NamedTuples
MIT License
80 stars 11 forks source link

Add `flattened` #30

Closed charleskawczynski closed 3 years ago

charleskawczynski commented 4 years ago

This PR adds a method flattened, which flattens a nested named tuple and preserves access to each value in the nested hierarchy. This method behaves as follows and passes the following test:

    nt = (x = 1, a = (y = 2, z = 3, b = ((a = 1,), (a = 2,), (a = 3,))))
    fnt = flattened(nt, :_)

    @test getproperty(fnt, :x)   == 1
    @test getproperty(fnt, :a)   == (y = 2, z = 3, b = ((a = 1,), (a = 2,), (a = 3,)))
    @test getproperty(fnt, :a_y) == 2
    @test getproperty(fnt, :a_z) == 3
    @test getproperty(fnt, :a_b) == ((a = 1,), (a = 2,), (a = 3,))
    @test getproperty(fnt, :a_b_1) == (a = 1,)
    @test getproperty(fnt, :a_b_2) == (a = 2,)
    @test getproperty(fnt, :a_b_3) == (a = 3,)
    @test getproperty(fnt, :a_b_1_a) == 1
    @test getproperty(fnt, :a_b_2_a) == 2
    @test getproperty(fnt, :a_b_3_a) == 3
JeffreySarnoff commented 4 years ago

Thank you. I am looking through it.

JeffreySarnoff commented 4 years ago

is there a reason not to call it flattened ?

charleskawczynski commented 4 years ago

Not at all. I’m fine with flattened

JeffreySarnoff commented 4 years ago

OK, please change the name to flattened and add a doc string .. we can work on that together, just give me a good starting point.

charleskawczynski commented 4 years ago

Updated. As I'm now using this in my particular application, I'm seeing that I need to be able to manipulate / filter the leaves of the nested named tuples. I suppose that this could easily be done after the fact, but we might be able to add some extra input arguments (functions) to easily do this in one go.

JeffreySarnoff commented 4 years ago

I agree. One go.

charleskawczynski commented 4 years ago

Updated. I think this should do the job for what I need. I'm open to other modifications, not sure what else might be useful, though.

charleskawczynski commented 4 years ago

Actually, I might need one more thing

JeffreySarnoff commented 4 years ago

"Actually, I might need one more thing" What are the specific things (capabilities) that you added after you first proposed the PR? [that info, in your words will make the docs easier to do properly]

JeffreySarnoff commented 4 years ago

Also, give me (describe) one or two of your use case[s].

charleskawczynski commented 4 years ago

My use case has gotten a bit too complicated ☹️

JeffreySarnoff commented 4 years ago

Do you want to discuss it?

charleskawczynski commented 4 years ago

The final form that I ended up with looks like this:

function flattened!(
        k_all,
        v_all,
        nt::Union{NamedTuple,Tuple},
        tup_chain,
        separator,
        filter_leaves,
        process_leaves,
        stop_at_branch
    )
    if stop_at_branch(nt)
        push!(k_all, join_tup_chain(tup_chain, separator))
        k_leaf = []
        v_leaf = []
        for pn in propertynames(nt)
            p = getproperty(nt, pn)
            if filter_leaves(pn, p)
                push!(k_leaf, pn)
                push!(v_leaf, process_leaves(pn, p))
            end
        end
        push!(v_all, (; zip(k_leaf, v_leaf)...))
    else
        for pn in propertynames(nt)
            p = getproperty(nt, pn)
            tup_chain_new = (tup_chain..., pn)
            if filter_leaves(pn, p)
                push!(k_all, join_tup_chain(tup_chain_new, separator))
                push!(v_all, process_leaves(pn, p))
            end
            flattened!(k_all, v_all, p, tup_chain_new, separator,
                filter_leaves, process_leaves, stop_at_branch)
        end
    end
end

It basically also allows users to flatten until stop_at_branch(nt) == true so that, for example, one can flatten until they reach the second to last "leaf" in a nested named tuple "tree". The default is to not stop until the leaves of the tree (what's seen in the test).

The method itself may still be useful and I don't mind updating the PR but, my use-case is for a package that I'm actually likely to destroy (https://github.com/charleskawczynski/VariableTemplates.jl).

JeffreySarnoff commented 4 years ago

ok well, let's not promulgate that which we destroy :) I will take another look at the prior push.

JeffreySarnoff commented 3 years ago

no movement so ...