Chris Rackauckas
Chris Rackauckas

Reputation: 19132

Setindex for named tuple

I am trying to write a non-mutating setindex for named tuple, where given a value type for the name var, it creates a new named tuple where x.var = y. What I have right now is:

function setindex(nt::T,x,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return x
  else
    typeof(x)(s == var ? y : w for (s,w) in nt)
  end
end

but my main issue is that I'm not sure of a good way to iterate the named tuple to get the (name,value) pairs.

Upvotes: 3

Views: 427

Answers (2)

张实唯
张实唯

Reputation: 2862

This is the @generated version. The generated code is dead simple and type stable.

julia> @generated function setindex(x::NamedTuple,y,v::Val)
         k = first(v.parameters)
         k ∉ x.names ? :x : :( (x..., $k=y) )
       end

julia> @code_warntype setindex((a=2, b=3), 4, Val(:b))
Body::NamedTuple{(:a, :b),Tuple{Int64,Int64}}
2 1 ─ %1 = (Base.getfield)(x, :a)::Int64                                                               │╻╷╷   macro expansion
  │   %2 = %new(NamedTuple{(:a, :b),Tuple{Int64,Int64}}, %1, y)::NamedTuple{(:a, :b),Tuple{Int64,Int64}}│┃││╷  merge
  └──      return %2                                                                                   ││    

julia> @code_warntype setindex((a=2, b=3), 4, Val(:c))
Body::NamedTuple{(:a, :b),Tuple{Int64,Int64}}
2 1 ─     return x

Upvotes: 7

hckr
hckr

Reputation: 5583

You can use pairs which gives an iterator for key-value pairs, or use fieldnames with type T and access the value with nt[name] or (use keys with the named tuple variable the same way). I think you prefer pairs.

function setindex(nt::T,y,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return nt
  else
    T(s == var ? y : w for (s,w) in pairs(nt))
  end
end

or

function setindex(nt::T,y,v::Val{var}) where {T,var}
  if var ∉ fieldnames(T)
    return nt
  else
    T(k == var ? y : nt[k] for k in fieldnames(T))
  end
end

Upvotes: 1

Related Questions