Copy tuple with modification

Viewed 377

I have a tuple:

my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)

I would like to modify a value in this tuple. Because tuples are immutable, the obvious route doesn't work:

my_tup[:a]=50 #Raises an error, as expected

So to perform the modification, I'd like to copy the tuple and its elements while changing the target element.

My current solution is as follows:

my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)
args = keys(my_tup)
NamedTuple{args}(i!=:a ? getfield(my_tup, i) : 50 for i in args)

But this seems verbose or as though there should already be a function for it in the standard library.

Is there a better way?

3 Answers

You can merge two named tuples:

julia> xs = (a = 1, b = 2, c = 3)
(a = 1, b = 2, c = 3)

julia> ys = merge(xs, (; a = 50))
(a = 50, b = 2, c = 3)

David Varela has the correct answer, "use merge". It is an important technique.

  • To make this solution more easily available to others, here are some examples.

on creating plain Tuples (unnamed Tuples) and NamedTuples

Both Tuples and NamedTuples are constructed using parentheses:

julia> a_tuple = (1, 2)
(1, 2)

julia> a_namedtuple = (a = 1, b = 2)
(a = 1, b = 2)

If we try construct a Tuple or a NamedTuple with exactly one entry that same way, it does not work; instead the values are assigned named variables directly.

julia> not_a_tuple = ("xyz");
julia> not_a_tuple, typeof(not_a_tuple)
("xyz", String)

julia> not_a_namedtuple = (abc = "xyz");
julia> not_a_namedtuple, typeof(not_a_namedtuple)
("xyz", String)

To construct Tuples and NamedTuples that have a single value, we let Julia know that we are working with [Named]Tuples. An easy way to do this, and one that works for both kinds of Tuples, is to add a comma ',' before the closing parenthesis. For visual emphasis, I separate the commas from the values; it is not necessary to do that in your source code.

julia> a_tuple = (0.5 ,)
(0.5,)

julia> a_namedtuple = (onehalf = 0.5 ,)
(onehalf = 0.5,)

on substituting values in NamedTuples

julia> namedtuple = (a = 1, b = 2, c = 3);  # the initial NamedTuple
julia> changes_to_make = (b = 0 ,);         # the modifications intended

julia> changed_namedtuple = merge(namedtuple, changes_to_make)
(a = 1, b = 0, c = 3)
julia> namedtuple = (a = 1, b = 2, c = 3);  # the initial NamedTuple
julia> changes_to_make = (b = 0, c = 7);    # the modifications intended

julia> changed_namedtuple = merge(namedtuple, changes_to_make)
(a = 1, b = 0, c = 7)
julia> my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)
(a = 1, b = 2, c = 3,d = 4, e = 5, f = 6, g = 7, h = 8, i = 9)

julia> new_one = (my_tup..., a=50)
(a = 50, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7, h = 8, i = 9)
Related