diff --git a/spec/std/named_tuple_spec.cr b/spec/std/named_tuple_spec.cr index 6dc6684be827..80e326fa253e 100644 --- a/spec/std/named_tuple_spec.cr +++ b/spec/std/named_tuple_spec.cr @@ -148,7 +148,7 @@ describe "NamedTuple" do typeof(val).should eq(Int32 | Char | Nil) end - describe "dig?" do + describe "#dig?" do it "gets the value at given path given splat" do h = {a: {b: {c: [10, 20]}}, x: {a: "b"}} @@ -165,7 +165,7 @@ describe "NamedTuple" do end end - describe "dig" do + describe "#dig" do it "gets the value at given path given splat" do h = {a: {b: {c: [10, 20]}}, x: {a: "b", c: nil}} @@ -290,9 +290,18 @@ describe "NamedTuple" do NamedTuple.new.empty?.should be_true end - it "does to_a" do - tup = {a: 1, b: 'a'} - tup.to_a.should eq([{:a, 1}, {:b, 'a'}]) + describe "#to_a" do + it "creates an array of key-value pairs" do + tup = {a: 1, b: 'a'} + tup.to_a.should eq([{:a, 1}, {:b, 'a'}]) + end + + it "preserves key type for empty named tuples" do + tup = NamedTuple.new + arr = tup.to_a + arr.should be_empty + arr.should be_a(Array({Symbol, NoReturn})) + end end it "does map" do @@ -327,10 +336,19 @@ describe "NamedTuple" do u.should_not eq(v) end - it "does to_h" do - tup1 = {a: 1, b: "hello"} - hash = tup1.to_h - hash.should eq({:a => 1, :b => "hello"}) + describe "#to_h" do + it "creates a hash" do + tup1 = {a: 1, b: "hello"} + hash = tup1.to_h + hash.should eq({:a => 1, :b => "hello"}) + end + + it "creates an empty hash from an empty named tuple" do + tup = NamedTuple.new + hash = tup.to_h + hash.should be_empty + hash.should be_a(Hash(Symbol, NoReturn)) + end end it "does to_s" do diff --git a/src/named_tuple.cr b/src/named_tuple.cr index 32d723e2ffd3..22a9a55e92a2 100644 --- a/src/named_tuple.cr +++ b/src/named_tuple.cr @@ -498,12 +498,18 @@ struct NamedTuple # tuple = {name: "Crystal", year: 2011} # tuple.to_a # => [{:name, "Crystal"}, {:year, 2011}] # ``` + # + # NOTE: `to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))` def to_a - ary = Array({typeof(first_key_internal), typeof(first_value_internal)}).new(size) - each do |key, value| - ary << {key.as(typeof(first_key_internal)), value.as(typeof(first_value_internal))} - end - ary + {% if T.size == 0 %} + [] of {Symbol, NoReturn} + {% else %} + [ + {% for key in T %} + { {{key.symbolize}}, self[{{key.symbolize}}] }, + {% end %} + ] + {% end %} end # Returns a `Hash` with the keys and values in this named tuple. @@ -512,9 +518,11 @@ struct NamedTuple # tuple = {name: "Crystal", year: 2011} # tuple.to_h # => {:name => "Crystal", :year => 2011} # ``` + # + # NOTE: `to_h` on an empty named tuple produces a `Hash(Symbol, NoReturn)` def to_h {% if T.size == 0 %} - {} of NoReturn => NoReturn + {} of Symbol => NoReturn {% else %} { {% for key in T %}