diff --git a/spec/std/named_tuple_spec.cr b/spec/std/named_tuple_spec.cr index 080dd7ae927f..f8de731e154c 100644 --- a/spec/std/named_tuple_spec.cr +++ b/spec/std/named_tuple_spec.cr @@ -297,6 +297,12 @@ describe "NamedTuple" do tup.values.should eq({1, 'a'}) end + it "merges with other named tuple" do + a = {one: 1, two: 2, three: 3, four: 4, five: 5, "im \"string": "works"} + b = {two: "Two", three: true, "new one": "ok"} + c = a.merge(b).merge(four: "Four").should eq({one: 1, two: "Two", three: true, four: "Four", five: 5, "new one": "ok", "im \"string": "works"}) + end + it "does types" do tuple = {a: 1, b: 'a', c: "hello"} tuple.class.types.to_s.should eq("{a: Int32, b: Char, c: String}") diff --git a/src/named_tuple.cr b/src/named_tuple.cr index ceef6642e373..69d5e71c572f 100644 --- a/src/named_tuple.cr +++ b/src/named_tuple.cr @@ -159,6 +159,31 @@ struct NamedTuple yield end + # Merges two named tuples into one, returning a new named tuple. + # If a key is defined in both tuples, the value and its type is used from *other*. + # + # ``` + # a = {foo: "Hello", bar: "Old"} + # b = {bar: "New", baz: "Bye"} + # a.merge(b) # => {foo: "Hello", bar: "New", baz: "Bye"} + # ``` + def merge(other : NamedTuple) + merge(**other) + end + + # ditto + def merge(**other : **U) forall U + {% begin %} + { + {% for k in T %} {% unless U.keys.includes?(k) %} {{k.stringify}}: self[{{k.symbolize}}],{% end %} {% end %} + {% for k in U %} {{k.stringify}}: other[{{k.symbolize}}], {% end %} + } + {% end %} + end + + # Returns a hash value based on this name tuple's size, keys and values. + # + # See also: `Object#hash`. # See `Object#hash(hasher)` def hash(hasher) {% for key in T.keys.sort %}