hutou
December 7, 2022, 5:35pm
1
Say I have the following code:
opts = {title: "abc", header: "test", value: 42}
popts = {title: "xyz", width: 10}
new_opts = opts.merge(popts)
p! new_opts # => {header: "test", value: 42, title: "xyz", width: 10}
How can I merge opts
and popts
while excluding popts
keys missing in opts
?
Expected result :
# => {header: "test", value: 42, title: "xyz"}
EDIT: Solved by creating an update_from
method in struct NamedTuple
, adapted from the merge
method, but as monkey-patching is not recommanded, is there another solution ?
Define your own struct to represent this data and define a custom #merge
method on it.
2 Likes
You could define it as a non-member method:
def update_from(dst : NamedTuple, from src : NamedTuple)
update_from_impl(dst, src)
end
def update_from_impl(dst : T, from src : U) forall T, U
{% begin %}
{% u_keys = U.keys %}
NamedTuple(
{% for k in T.keys %}
{{ k.stringify }}: typeof(
dst[{{ k.stringify }}],
{% if u_keys.includes?(k) %}
src[{{ k.stringify }}],
{% end %}
),
{% end %}
).new(
{% for k in T.keys %}
{% receiver = u_keys.includes?(k) ? "src" : "dst" %}
{{ k.stringify }}: {{ receiver.id }}[{{ k.stringify }}],
{% end %}
)
{% end %}
end
p update_from(
{title: "abc", header: "test", value: 42},
{title: "xyz", width: 10},
)
Unfortunately you cannot infer the NamedTuple
instantiation in one step via NamedTuple(**T) forall T
, in contrast to the working Tuple(*T) forall T
.
hutou
December 8, 2022, 11:17am
4
Thanks a lot, @HertzDevil , that’s exactly what I was looking for (and besides, I now have some non-trivial code to study )!
zw963
December 8, 2022, 5:26pm
5
If use ruby, you can get expected result use
opts = {title: "abc", header: "test", value: 42}
popts = {title: "xyz", width: 10}
result = opts.merge(popts.slice(*opts.keys))
Unfortunately, Crystal no NamedTuple#slice equivalent, you can create a feature request for this.
struct NamedTuple
def slice(*keys : *U) forall U
h = self.to_h.select { |x| keys.map(&.to_s).includes? x.to_s }
h
end
end
x = {x: 100, y: 200, z: 300}
p x.slice("x", "y")
# => {:x => 100, :y => 200}
Above method return a Hash, i thought it is hard to convert above h back to a tuple, i don’t know how to do that.