Remove a field from a `NamedTuple`

Is there an easy way in base Julia to create a NamedTuple with all the fields except some particular ones? Currently I have the following, but I’m wondering if I’m missing something obvious. I’m calling it delete as it seems to do what delete! does with Dicts, but not in-place.

"""
    delete(nt::NamedTuple, fieldnames)

Remove the given fields from `nt`, returning a new `NamedTuple`
"""
function delete(nt::NamedTuple, fieldnames...)
    (; filter(p->first(p) ∉ fieldnames, collect(pairs(nt)))...)
end

The collect is necessary because apparently filter on pairs returns a Dict, which loses ordering information.

1 Like

Maybe the easiest implementation is something like

delete(nt::NamedTuple{names}, keys) where names =
    NamedTuple{filter(x -> x ∉ keys, names)}(nt)

This needs Julia 1.4 or Compat.jl imported for filter on a tuple.

There is also BangBang.delete!! that does something like this and here is how it’s implemented: BangBang.jl/base.jl at 10a6a7aefe90d4e7948aab977858a3fa34a017c2 · JuliaFolds/BangBang.jl · GitHub

4 Likes

You are looking for Base.structdiff.

3 Likes

That’s a good example where I wouldn’t know if I can use it or if it might change without warning.

1 Like

Good point, I opened an issue:

https://github.com/JuliaLang/julia/issues/34772

1 Like

So, what is the solution today (2024-08-12)?

Is Base.structdiff still the recommended way?


I naïvely thought that deleting a key-value pair from a NamedTuple is so analogous to doing so from a Dict that delete() is the most appropriate name for this operation. But apparently, it’s not. Why?

I’ve been also surprised that there is no delete() for Dict whereas there is delete!(). I thought it would be nt2 = delete(nt1, keytodelete) for a NamedTuple. . . .

For NamedTuples, the fields are part of the type, so they have to be provided in a way that can be inferred.

delete could work with constant folding though I guess.

1 Like
3 Likes

I think I understand the issue. delete!(dict, . . .) works by modifying the first object, but delete!(nt, . . .) cannot, because deleting an element would change the type of the object, which cannot be an in-place modification.

Along these lines?

delete(nt, sym) = Base.structdiff(nt, NamedTuple{(sym,)}((nothing,)))

(I don’t know well how to construct a NamedTuple using the constructor.)

If this delete() is a good idea, I’d also like to see it for Dict:

newdict = delete(dict, keytodelete)

Sometime ago, I had to first copy a Dict before deleting some of its elements.

1 Like

Great!

1 Like

See the PR linked by @nsajko, especially the aggressive constant propagation.

1 Like