Reputation: 19132
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
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
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