NamedTuple(**T) - Crystal 1.20.1

# struct NamedTuple(**T)

- NamedTuple(**T)
- Value
- Object

##
Overview

A named tuple is a fixed-size, immutable, stack-allocated mapping of a fixed set of keys to values.

You can think of a `NamedTuple` as an immutable `Hash` whose keys (which are of type `Symbol`), and the types for each key, are known at compile time.

A named tuple can be created with a named tuple literal:

    language = {name: "Crystal", year: 2011} # NamedTuple(name: String, year: Int32)

    language[:name]  # => "Crystal"
    language[:year]  # => 2011
    language[:other] # compile time error

See `NamedTuple` literals in the language reference.

The compiler knows what types are in each key, so when indexing a named tuple with a symbol or string literal the compiler will return the value for that key and with the expected type, like in the above snippet. Indexing with a symbol or string literal for which there's no key will give a compile-time error.

Indexing with a symbol or string that is only known at runtime will return a value whose type is the union of all the types in the named tuple, and might raise `KeyError`.

Indexing with `#[]?` does not make the return value nilable if the key is known to exist:

    language = {name: "Crystal", year: 2011}
    language[:name]?         # => "Crystal"
    typeof(language[:name]?) # => String

`NamedTuple`'s own instance classes may also be indexed in a similar manner, returning their value types instead:

    tuple = NamedTuple(name: String, year: Int32)
    tuple[:name]   # => String
    tuple["year"]  # => Int32
    tuple[:other]? # => nil

##
Defined in:

json/to_json.cr
named_tuple.cr
yaml/to_yaml.cr

##
Constructors

- .from(hash : Hash) : self

  Creates a named tuple from the given hash, with elements casted to the given types.

- .new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
- .new(pull : JSON::PullParser)
- .new(**options : **T)

  Creates a named tuple that will contain the given arguments.

##
Class Method Summary

- .[](key : Symbol | String)

  Returns the value type for the given key if there is such a key, otherwise raises `KeyError`.

- .[]?(key : Symbol | String)

  Returns the value type for the given key if there is such a key, otherwise returns `nil`.

- .types

  Returns the types of this named tuple type.

##
Instance Method Summary

- #==(other : self)

  Returns `true` if this tuple has the same keys as other, and values for each key are the same in `self` and other.

- #==(other : NamedTuple)

  Returns `true` if this tuple has the same keys as other, and values for each key are the same in `self` and other.

- #[](key : Symbol | String)

  Returns the value for the given key if there is such a key, otherwise raises `KeyError`.

- #[]?(key : Symbol | String)

  Returns the value for the given key if there is such a key, otherwise returns `nil`.

- #clone

  Returns a named tuple with the same keys but with cloned values, using the `#clone` method.

- #dig(key : Symbol | String, *subkeys)

  Traverses the depth of a structure and returns the value, otherwise raises `KeyError`.

- #dig?(key : Symbol | String, *subkeys)

  Traverses the depth of a structure and returns the value.

- #each(&) : Nil

  Yields each key and value in this named tuple.

- #each_key(&) : Nil

  Yields each key in this named tuple.

- #each_value(&) : Nil

  Yields each value in this named tuple.

- #each_with_index(offset = 0, &)

  Yields each key and value, together with an index starting at offset, in this named tuple.

- #empty?

  Returns `true` if this named tuple is empty.

- #fetch(key : Symbol | String, default_value)

  Returns the value for the given key, if there's such key, otherwise returns default_value.

- #fetch(key : Symbol, &)

  Returns the value for the given key, if there's such key, otherwise the value returned by the block.

- #fetch(key : String, &)

  Returns the value for the given key, if there's such key, otherwise the value returned by the block.

- #from(hash : Hash)

  Expects to be called on a named tuple whose values are types, creates a tuple from the given hash, with types casted appropriately.

- #has_key?(key : Symbol) : Bool

  Returns `true` if this named tuple has the given key, `false` otherwise.

- #has_key?(key : String) : Bool

  Returns `true` if this named tuple has the given key, `false` otherwise.

- #hash(hasher)

  Returns a hash value based on this name tuple's size, keys and values.

- #inspect : String

  Same as `#to_s`.

- #keys

  Returns a `Tuple` of symbols with the keys in this named tuple.

- #map(&)

  Returns an `Array` populated with the results of each iteration in the given block, which is given each key and value in this named tuple.

- #merge(other : NamedTuple)

  Merges two named tuples into one, returning a new named tuple.

- #merge(**other : **U) forall U

  Merges two named tuples into one, returning a new named tuple.

- #pretty_print(pp)
- #reverse_merge(other : NamedTuple)

  Merges two named tuples into one, returning a new named tuple.

- #reverse_merge(**other)

  Merges two named tuples into one, returning a new named tuple.

- #size

  Returns the number of elements in this named tuple.

- #sorted_keys

  Returns a `Tuple` of symbols with the keys in this named tuple, sorted by name.

- #to_a

  Returns a new `Array` of tuples populated with each key-value pair.

- #to_a(&)

  Returns an `Array` with the results of running block against tuples with key and values belonging to this `NamedTuple`.

- #to_h

  Returns a `Hash` with the keys and values in this named tuple.

- #to_json(json : JSON::Builder)
- #to_s(io : IO) : Nil

  Appends a string representation of this named tuple to the given `IO`.

- #to_yaml(yaml : YAML::Nodes::Builder)
- #values

  Returns a `Tuple` with the values in this named tuple.

### Instance methods inherited from struct `Value`

==(other : Log::Metadata::Value)
==(other : JSON::Any)
==(other : YAML::Any)
==(other) ==, dup dup

### Instance methods inherited from class `Object`

! : Bool !, !=(other) !=, !~(other) !~, ==(other) ==, ===(other : JSON::Any)
===(other : YAML::Any)
===(other) ===, =~(other) =~, as(type : Class) as, as?(type : Class) as?, class class, dup dup, hash(hasher)
hash hash, in?(collection : Object) : Bool
in?(*values : Object) : Bool in?, inspect(io : IO) : Nil
inspect : String inspect, is_a?(type : Class) : Bool is_a?, itself itself, nil? : Bool nil?, not_nil!(message)
not_nil! not_nil!, pretty_inspect(width = 79, newline = "\n", indent = 0) : String pretty_inspect, pretty_print(pp : PrettyPrint) : Nil pretty_print, responds_to?(name : Symbol) : Bool responds_to?, tap(&) tap, to_json(io : IO) : Nil
to_json : String to_json, to_pretty_json(indent : String = " ") : String
to_pretty_json(io : IO, indent : String = " ") : Nil to_pretty_json, to_s(io : IO) : Nil
to_s : String to_s, to_yaml(io : IO) : Nil
to_yaml : String to_yaml, try(&) try, unsafe_as(type : T.class) forall T unsafe_as

### Class methods inherited from class `Object`

from_json(string_or_io : String | IO, root : String)
from_json(string_or_io : String | IO) from_json, from_yaml(string_or_io : String | IO) from_yaml

### Macros inherited from class `Object`

class_getter(*names, &block) class_getter, class_getter!(*names) class_getter!, class_getter?(*names, &block) class_getter?, class_property(*names, &block) class_property, class_property!(*names) class_property!, class_property?(*names, &block) class_property?, class_setter(*names) class_setter, def_clone def_clone, def_equals(*fields) def_equals, def_equals_and_hash(*fields) def_equals_and_hash, def_hash(*fields) def_hash, delegate(*methods, to object) delegate, forward_missing_to(delegate) forward_missing_to, getter(*names, &block) getter, getter!(*names) getter!, getter?(*names, &block) getter?, property(*names, &block) property, property!(*names) property!, property?(*names, &block) property?, setter(*names) setter

##
Constructor Detail

def self.from(hash : Hash) : self #

Creates a named tuple from the given hash, with elements casted to the given types. Here the Int32 | String union is cast to Int32.

    num_or_str = 42.as(Int32 | String)
    NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # => {name: "number", val: 42}

    num_or_str = "a string".as(Int32 | String)
    NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # raises TypeCastError (Cast from String to Int32 failed)

See also: `#from`.

def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) #
def self.new(pull : JSON::PullParser) #
def self.new(**options : **T) #

Creates a named tuple that will contain the given arguments.

With a named tuple literal you cannot create an empty named tuple. This method doesn't have this limitation, which makes it especially useful in macros and generic code.

    NamedTuple.new(name: "Crystal", year: 2011) #=> {name: "Crystal", year: 2011}
    NamedTuple.new # => {}
    {}             # syntax error

##
Class Method Detail

def self.[](key : Symbol | String) #

Returns the value type for the given key if there is such a key, otherwise raises `KeyError`. Read the type docs to understand the difference between indexing with a literal or a variable.

    alias Foo = NamedTuple(name: String, year: Int32)

    Foo[:name]       # => String
    Foo["year"]      # => Int32
    Foo["year"].zero # => 0
    Foo[:other]      # => Error: missing key 'other' for named tuple NamedTuple(name: String, year: Int32).class

    key = :year
    Foo[key]      # => Int32
    Foo[key].zero # Error: undefined method 'zero' for String.class (compile-time type is (Int32.class | String.class))

    key = "other"
    Foo[key] # raises KeyError

def self.[]?(key : Symbol | String) #

Returns the value type for the given key if there is such a key, otherwise returns `nil`. Read the type docs to understand the difference between indexing with a literal or a variable.

    alias Foo = NamedTuple(name: String, year: Int32)

    Foo[:name]?          # => String
    Foo["year"]?         # => Int32
    Foo["year"]?.zero    # => 0
    Foo[:other]?         # => nil
    typeof(Foo[:other]?) # => Nil

    key = :year
    Foo[key]?      # => Int32
    Foo[key]?.zero # Error: undefined method 'zero' for String.class (compile-time type is (Int32.class | String.class | Nil))

    key = "other"
    Foo[key]? # => nil

def self.types #

Returns the types of this named tuple type.

    tuple = {a: 1, b: "hello", c: 'x'}
    tuple.class.types # => {a: Int32, b: String, c: Char}

##
Instance Method Detail

def ==(other : self) #

Returns `true` if this tuple has the same keys as other, and values for each key are the same in `self` and other.

    tuple1 = {name: "Crystal", year: 2011}
    tuple2 = {year: 2011, name: "Crystal"}
    tuple3 = {name: "Crystal", year: 2012}
    tuple4 = {name: "Crystal", year: 2011.0}

    tuple1 == tuple2 # => true
    tuple1 == tuple3 # => false
    tuple1 == tuple4 # => true

def ==(other : NamedTuple) #

Returns `true` if this tuple has the same keys as other, and values for each key are the same in `self` and other.

    tuple1 = {name: "Crystal", year: 2011}
    tuple2 = {year: 2011, name: "Crystal"}
    tuple3 = {name: "Crystal", year: 2012}
    tuple4 = {name: "Crystal", year: 2011.0}

    tuple1 == tuple2 # => true
    tuple1 == tuple3 # => false
    tuple1 == tuple4 # => true

def [](key : Symbol | String) #

Returns the value for the given key if there is such a key, otherwise raises `KeyError`. Read the type docs to understand the difference between indexing with a literal or a variable.

    tuple = {name: "Crystal", year: 2011}

    tuple[:name]          # => "Crystal"
    typeof(tuple[:name])  # => String
    tuple["year"]         # => 2011
    typeof(tuple["year"]) # => Int32
    tuple[:other]         # Error: missing key 'other' for named tuple NamedTuple(name: String, year: Int32)

    key = :name
    tuple[key]         # => "Crystal"
    typeof(tuple[key]) # => (Int32 | String)

    key = "year"
    tuple[key] # => 2011

    key = :other
    tuple[key] # raises KeyError

def []?(key : Symbol | String) #

Returns the value for the given key if there is such a key, otherwise returns `nil`. Read the type docs to understand the difference between indexing with a literal or a variable.

    tuple = {name: "Crystal", year: 2011}

    tuple[:name]?          # => "Crystal"
    typeof(tuple[:name]?)  # => String
    tuple["year"]?         # => 2011
    typeof(tuple["year"]?) # => Int32
    tuple[:other]?         # => nil
    typeof(tuple[:other]?) # => Nil

    key = :name
    tuple[key]?         # => "Crystal"
    typeof(tuple[key]?) # => (Int32 | String | Nil)

    key = "year"
    tuple[key]? # => 2011

    key = :other
    tuple[key]? # => nil

def clone #

Returns a named tuple with the same keys but with cloned values, using the `#clone` method.

def dig(key : Symbol | String, *subkeys) #

Traverses the depth of a structure and returns the value, otherwise raises `KeyError`.

    h = {a: {b: {c: [10, 20]}}, x: {a: "b"}}
    h.dig :a, :b, :c # => [10, 20]
    h.dig "a", "x"   # raises KeyError

def dig?(key : Symbol | String, *subkeys) #

Traverses the depth of a structure and returns the value. Returns `nil` if not found.

    h = {a: {b: {c: [10, 20]}}, x: {a: "b"}}
    h.dig? :a, :b, :c # => [10, 20]
    h.dig? "a", "x"   # => nil

def each(&) : Nil #

Yields each key and value in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.each do |key, value|
      puts "#{key} = #{value}"
    end

Output:

    name = Crystal
    year = 2011

def each_key(&) : Nil #

Yields each key in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.each_key do |key|
      puts key
    end

Output:

    name
    year

def each_value(&) : Nil #

Yields each value in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.each_value do |value|
      puts value
    end

Output:

    Crystal
    2011

def each_with_index(offset = 0, &) #

Yields each key and value, together with an index starting at offset, in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.each_with_index do |key, value, i|
      puts "#{i + 1}) #{key} = #{value}"
    end

Output:

    1) name = Crystal
    2) year = 2011

def empty? #

Returns `true` if this named tuple is empty.

    tuple = {name: "Crystal", year: 2011}
    tuple.empty? # => false

def fetch(key : Symbol | String, default_value) #

Returns the value for the given key, if there's such key, otherwise returns default_value.

    tuple = {name: "Crystal", year: 2011}
    tuple.fetch(:name, "Unknown") # => "Crystal"
    tuple.fetch("year", 0)        # => 2011
    tuple.fetch(:other, 0)        # => 0

def fetch(key : Symbol, &) #

Returns the value for the given key, if there's such key, otherwise the value returned by the block.

    tuple = {name: "Crystal", year: 2011}
    tuple.fetch(:name) { "Unknown" } # => "Crystal"
    tuple.fetch(:other) { 0 }        # => 0

def fetch(key : String, &) #

Returns the value for the given key, if there's such key, otherwise the value returned by the block.

    tuple = {name: "Crystal", year: 2011}
    tuple.fetch("name") { "Unknown" } # => "Crystal"
    tuple.fetch("other") { 0 }        # => 0

def from(hash : Hash) #

Expects to be called on a named tuple whose values are types, creates a tuple from the given hash, with types casted appropriately. The hash keys must be either symbols or strings.

This allows you to easily pass a hash as individual named arguments to a method.

    require "json"

    def speak_about(thing : String, n : Int64)
      "I see #{n} #{thing}s"
    end

    hash = JSON.parse(%({"thing": "world", "n": 2})).as_h # hash : Hash(String, JSON::Any)
    hash = hash.transform_values(&.raw)                   # hash : Hash(String, JSON::Any::Type)

    speak_about(**{thing: String, n: Int64}.from(hash)) # => "I see 2 worlds"

def has_key?(key : Symbol) : Bool #

Returns `true` if this named tuple has the given key, `false` otherwise.

    tuple = {name: "Crystal", year: 2011}
    tuple.has_key?(:name)  # => true
    tuple.has_key?(:other) # => false

def has_key?(key : String) : Bool #

Returns `true` if this named tuple has the given key, `false` otherwise.

    tuple = {name: "Crystal", year: 2011}
    tuple.has_key?(:name)  # => true
    tuple.has_key?(:other) # => false

def hash(hasher) #

Returns a hash value based on this name tuple's size, keys and values.

See also: `Object#hash`. See `Object#hash(hasher)`

def inspect : String #

Same as `#to_s`.

def keys #

Returns a `Tuple` of symbols with the keys in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.keys # => {:name, :year}

def map(&) #

Returns an `Array` populated with the results of each iteration in the given block, which is given each key and value in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.map { |k, v| "#{k}: #{v}" } # => ["name: Crystal", "year: 2011"]

def merge(other : NamedTuple) #

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 : **U) forall U #

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 pretty_print(pp) #
def reverse_merge(other : NamedTuple) #

Merges two named tuples into one, returning a new named tuple. If a key is defined in both tuples, the value and its type are used from self (original tuple)

    a = {foo: "Hello", bar: "Old"}
    b = {bar: "New", baz: "Bye"}
    a.reverse_merge(b) # => {foo: "Hello", bar: "Old", baz: "Bye"}

def reverse_merge(**other) #

Merges two named tuples into one, returning a new named tuple. If a key is defined in both tuples, the value and its type are used from self (original tuple)

    a = {foo: "Hello", bar: "Old"}
    b = {bar: "New", baz: "Bye"}
    a.reverse_merge(b) # => {foo: "Hello", bar: "Old", baz: "Bye"}

def size #

Returns the number of elements in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.size # => 2

def sorted_keys #

Returns a `Tuple` of symbols with the keys in this named tuple, sorted by name.

    tuple = {foo: 1, bar: 2, baz: 3}
    tuple.sorted_keys # => {:bar, :baz, :foo}

def to_a #

Returns a new `Array` of tuples populated with each key-value pair.

    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(&) #

Returns an `Array` with the results of running block against tuples with key and values belonging to this `NamedTuple`.

    tuple = {first_name: "foo", last_name: "bar"}
    tuple.to_a(&.last.capitalize) # => ["Foo", "Bar"]

NOTE `#to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))`

def to_h #

Returns a `Hash` with the keys and values in this named tuple.

    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_json(json : JSON::Builder) #
def to_s(io : IO) : Nil #

Appends a string representation of this named tuple to the given `IO`.

    tuple = {name: "Crystal", year: 2011}
    tuple.to_s # => %({name: "Crystal", year: 2011})

def to_yaml(yaml : YAML::Nodes::Builder) #
def values #

Returns a `Tuple` with the values in this named tuple.

    tuple = {name: "Crystal", year: 2011}
    tuple.values # => {"Crystal", 2011}