From a289138f5599024a07e12728fd9b0c4552964b52 Mon Sep 17 00:00:00 2001 From: SamW Date: Thu, 16 Nov 2023 13:58:46 -0800 Subject: [PATCH] Update Struct --- core/hash.rbs | 3528 ++++++++++++++++++------------------ core/struct.rbs | 478 ++++- test/stdlib/Struct_test.rb | 217 ++- 3 files changed, 2418 insertions(+), 1805 deletions(-) diff --git a/core/hash.rbs b/core/hash.rbs index c5741fd304..3da69eb939 100644 --- a/core/hash.rbs +++ b/core/hash.rbs @@ -1,1906 +1,1912 @@ -# -# A Hash maps each of its unique keys to a specific value. -# -# A Hash has certain similarities to an Array, but: -# * An Array index is always an Integer. -# * A Hash key can be (almost) any object. -# -# -# ### Hash Data Syntax -# -# The older syntax for Hash data uses the "hash rocket," `=>`: -# -# h = {:foo => 0, :bar => 1, :baz => 2} -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# Alternatively, but only for a Hash key that's a Symbol, you can use a newer -# JSON-style syntax, where each bareword becomes a Symbol: -# -# h = {foo: 0, bar: 1, baz: 2} -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# You can also use a String in place of a bareword: -# -# h = {'foo': 0, 'bar': 1, 'baz': 2} -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# And you can mix the styles: -# -# h = {foo: 0, :bar => 1, 'baz': 2} -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# But it's an error to try the JSON-style syntax for a key that's not a bareword -# or a String: -# -# # Raises SyntaxError (syntax error, unexpected ':', expecting =>): -# h = {0: 'zero'} -# -# Hash value can be omitted, meaning that value will be fetched from the context -# by the name of the key: -# -# x = 0 -# y = 100 -# h = {x:, y:} -# h # => {:x=>0, :y=>100} -# -# ### Common Uses -# -# You can use a Hash to give names to objects: -# -# person = {name: 'Matz', language: 'Ruby'} -# person # => {:name=>"Matz", :language=>"Ruby"} -# -# You can use a Hash to give names to method arguments: -# -# def some_method(hash) -# p hash -# end -# some_method({foo: 0, bar: 1, baz: 2}) # => {:foo=>0, :bar=>1, :baz=>2} -# -# Note: when the last argument in a method call is a Hash, the curly braces may -# be omitted: -# -# some_method(foo: 0, bar: 1, baz: 2) # => {:foo=>0, :bar=>1, :baz=>2} -# -# You can use a Hash to initialize an object: -# -# class Dev -# attr_accessor :name, :language -# def initialize(hash) -# self.name = hash[:name] -# self.language = hash[:language] -# end -# end -# matz = Dev.new(name: 'Matz', language: 'Ruby') -# matz # => # -# -# ### Creating a Hash -# -# You can create a Hash object explicitly with: -# -# * A [hash literal](rdoc-ref:syntax/literals.rdoc@Hash+Literals). -# -# -# You can convert certain objects to Hashes with: -# -# * Method #Hash. -# -# -# You can create a Hash by calling method Hash.new. -# -# Create an empty Hash: -# -# h = Hash.new -# h # => {} -# h.class # => Hash -# -# You can create a Hash by calling method Hash.[]. -# -# Create an empty Hash: -# -# h = Hash[] -# h # => {} -# -# Create a Hash with initial entries: -# -# h = Hash[foo: 0, bar: 1, baz: 2] -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# You can create a Hash by using its literal form (curly braces). -# -# Create an empty Hash: -# -# h = {} -# h # => {} -# -# Create a Hash with initial entries: -# -# h = {foo: 0, bar: 1, baz: 2} -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# ### Hash Value Basics -# -# The simplest way to retrieve a Hash value (instance method #[]): -# -# h = {foo: 0, bar: 1, baz: 2} -# h[:foo] # => 0 -# -# The simplest way to create or update a Hash value (instance method #[]=): -# -# h = {foo: 0, bar: 1, baz: 2} -# h[:bat] = 3 # => 3 -# h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} -# h[:foo] = 4 # => 4 -# h # => {:foo=>4, :bar=>1, :baz=>2, :bat=>3} -# -# The simplest way to delete a Hash entry (instance method #delete): -# -# h = {foo: 0, bar: 1, baz: 2} -# h.delete(:bar) # => 1 -# h # => {:foo=>0, :baz=>2} -# -# ### Entry Order -# -# A Hash object presents its entries in the order of their creation. This is -# seen in: -# -# * Iterative methods such as `each`, `each_key`, `each_pair`, `each_value`. -# * Other order-sensitive methods such as `shift`, `keys`, `values`. -# * The String returned by method `inspect`. -# -# -# A new Hash has its initial ordering per the given entries: -# -# h = Hash[foo: 0, bar: 1] -# h # => {:foo=>0, :bar=>1} -# -# New entries are added at the end: -# -# h[:baz] = 2 -# h # => {:foo=>0, :bar=>1, :baz=>2} -# -# Updating a value does not affect the order: -# -# h[:baz] = 3 -# h # => {:foo=>0, :bar=>1, :baz=>3} -# -# But re-creating a deleted entry can affect the order: -# -# h.delete(:foo) -# h[:foo] = 5 -# h # => {:bar=>1, :baz=>3, :foo=>5} -# -# ### Hash Keys -# -# #### Hash Key Equivalence -# -# Two objects are treated as the same hash key when their `hash` value is -# identical and the two objects are `eql?` to each other. -# -# #### Modifying an Active Hash Key -# -# Modifying a Hash key while it is in use damages the hash's index. -# -# This Hash has keys that are Arrays: -# -# a0 = [ :foo, :bar ] -# a1 = [ :baz, :bat ] -# h = {a0 => 0, a1 => 1} -# h.include?(a0) # => true -# h[a0] # => 0 -# a0.hash # => 110002110 -# -# Modifying array element `a0[0]` changes its hash value: -# -# a0[0] = :bam -# a0.hash # => 1069447059 -# -# And damages the Hash index: -# -# h.include?(a0) # => false -# h[a0] # => nil -# -# You can repair the hash index using method `rehash`: -# -# h.rehash # => {[:bam, :bar]=>0, [:baz, :bat]=>1} -# h.include?(a0) # => true -# h[a0] # => 0 -# -# A String key is always safe. That's because an unfrozen String passed as a key -# will be replaced by a duplicated and frozen String: -# -# s = 'foo' -# s.frozen? # => false -# h = {s => 0} -# first_key = h.keys.first -# first_key.frozen? # => true -# -# #### User-Defined Hash Keys -# -# To be useable as a Hash key, objects must implement the methods `hash` and -# `eql?`. Note: this requirement does not apply if the Hash uses -# #compare_by_identity since comparison will then rely on the keys' object id -# instead of `hash` and `eql?`. -# -# Object defines basic implementation for `hash` and `eq?` that makes each -# object a distinct key. Typically, user-defined classes will want to override -# these methods to provide meaningful behavior, or for example inherit Struct -# that has useful definitions for these. -# -# A typical implementation of `hash` is based on the object's data while `eql?` -# is usually aliased to the overridden `==` method: -# -# class Book -# attr_reader :author, :title -# -# def initialize(author, title) -# @author = author -# @title = title -# end -# -# def ==(other) -# self.class === other && -# other.author == @author && -# other.title == @title -# end -# -# alias eql? == -# -# def hash -# @author.hash ^ @title.hash # XOR -# end -# end -# -# book1 = Book.new 'matz', 'Ruby in a Nutshell' -# book2 = Book.new 'matz', 'Ruby in a Nutshell' -# -# reviews = {} -# -# reviews[book1] = 'Great reference!' -# reviews[book2] = 'Nice and compact!' -# -# reviews.length #=> 1 -# -# ### Default Values -# -# The methods #[], #values_at and #dig need to return the value associated to a -# certain key. When that key is not found, that value will be determined by its -# default proc (if any) or else its default (initially `nil`). -# -# You can retrieve the default value with method #default: -# -# h = Hash.new -# h.default # => nil -# -# You can set the default value by passing an argument to method Hash.new or -# with method #default= -# -# h = Hash.new(-1) -# h.default # => -1 -# h.default = 0 -# h.default # => 0 -# -# This default value is returned for #[], #values_at and #dig when a key is not -# found: -# -# counts = {foo: 42} -# counts.default # => nil (default) -# counts[:foo] = 42 -# counts[:bar] # => nil -# counts.default = 0 -# counts[:bar] # => 0 -# counts.values_at(:foo, :bar, :baz) # => [42, 0, 0] -# counts.dig(:bar) # => 0 -# -# Note that the default value is used without being duplicated. It is not -# advised to set the default value to a mutable object: -# -# synonyms = Hash.new([]) -# synonyms[:hello] # => [] -# synonyms[:hello] << :hi # => [:hi], but this mutates the default! -# synonyms.default # => [:hi] -# synonyms[:world] << :universe -# synonyms[:world] # => [:hi, :universe], oops -# synonyms.keys # => [], oops -# -# To use a mutable object as default, it is recommended to use a default proc -# -# #### Default Proc -# -# When the default proc for a Hash is set (i.e., not `nil`), the default value -# returned by method #[] is determined by the default proc alone. -# -# You can retrieve the default proc with method #default_proc: -# -# h = Hash.new -# h.default_proc # => nil -# -# You can set the default proc by calling Hash.new with a block or calling the -# method #default_proc= -# -# h = Hash.new { |hash, key| "Default value for #{key}" } -# h.default_proc.class # => Proc -# h.default_proc = proc { |hash, key| "Default value for #{key.inspect}" } -# h.default_proc.class # => Proc -# -# When the default proc is set (i.e., not `nil`) and method #[] is called with -# with a non-existent key, #[] calls the default proc with both the Hash object -# itself and the missing key, then returns the proc's return value: -# -# h = Hash.new { |hash, key| "Default value for #{key}" } -# h[:nosuch] # => "Default value for nosuch" -# -# Note that in the example above no entry for key `:nosuch` is created: -# -# h.include?(:nosuch) # => false -# -# However, the proc itself can add a new entry: -# -# synonyms = Hash.new { |hash, key| hash[key] = [] } -# synonyms.include?(:hello) # => false -# synonyms[:hello] << :hi # => [:hi] -# synonyms[:world] << :universe # => [:universe] -# synonyms.keys # => [:hello, :world] -# -# Note that setting the default proc will clear the default value and vice -# versa. -# -# ### What's Here -# -# First, what's elsewhere. Class Hash: -# -# * Inherits from [class Object](rdoc-ref:Object@What-27s+Here). -# * Includes [module Enumerable](rdoc-ref:Enumerable@What-27s+Here), which -# provides dozens of additional methods. -# -# -# Here, class Hash provides methods that are useful for: -# -# * [Creating a Hash](rdoc-ref:Hash@Methods+for+Creating+a+Hash) -# * [Setting Hash State](rdoc-ref:Hash@Methods+for+Setting+Hash+State) -# * [Querying](rdoc-ref:Hash@Methods+for+Querying) -# * [Comparing](rdoc-ref:Hash@Methods+for+Comparing) -# * [Fetching](rdoc-ref:Hash@Methods+for+Fetching) -# * [Assigning](rdoc-ref:Hash@Methods+for+Assigning) -# * [Deleting](rdoc-ref:Hash@Methods+for+Deleting) -# * [Iterating](rdoc-ref:Hash@Methods+for+Iterating) -# * [Converting](rdoc-ref:Hash@Methods+for+Converting) -# * [Transforming Keys and -# Values](rdoc-ref:Hash@Methods+for+Transforming+Keys+and+Values) -# * [And more....](rdoc-ref:Hash@Other+Methods) -# -# -# Class Hash also includes methods from module Enumerable. -# -# #### Methods for Creating a Hash -# -# * ::[]: Returns a new hash populated with given objects. -# * ::new: Returns a new empty hash. -# * ::try_convert: Returns a new hash created from a given object. -# -# -# #### Methods for Setting Hash State -# -# * #compare_by_identity: Sets `self` to consider only identity in comparing -# keys. -# * #default=: Sets the default to a given value. -# * #default_proc=: Sets the default proc to a given proc. -# * #rehash: Rebuilds the hash table by recomputing the hash index for each -# key. -# -# -# #### Methods for Querying -# -# * #any?: Returns whether any element satisfies a given criterion. -# * #compare_by_identity?: Returns whether the hash considers only identity -# when comparing keys. -# * #default: Returns the default value, or the default value for a given key. -# * #default_proc: Returns the default proc. -# * #empty?: Returns whether there are no entries. -# * #eql?: Returns whether a given object is equal to `self`. -# * #hash: Returns the integer hash code. -# * #has_value?: Returns whether a given object is a value in `self`. -# * #include?, #has_key?, #member?, #key?: Returns whether a given object is a -# key in `self`. -# * #length, #size: Returns the count of entries. -# * #value?: Returns whether a given object is a value in `self`. -# -# -# #### Methods for Comparing -# -# * #<: Returns whether `self` is a proper subset of a given object. -# * #<=: Returns whether `self` is a subset of a given object. -# * #==: Returns whether a given object is equal to `self`. -# * #>: Returns whether `self` is a proper superset of a given object -# * #>=: Returns whether `self` is a proper superset of a given object. -# -# -# #### Methods for Fetching -# -# * #[]: Returns the value associated with a given key. -# * #assoc: Returns a 2-element array containing a given key and its value. -# * #dig: Returns the object in nested objects that is specified by a given -# key and additional arguments. -# * #fetch: Returns the value for a given key. -# * #fetch_values: Returns array containing the values associated with given -# keys. -# * #key: Returns the key for the first-found entry with a given value. -# * #keys: Returns an array containing all keys in `self`. -# * #rassoc: Returns a 2-element array consisting of the key and value of the -# first-found entry having a given value. -# * #values: Returns an array containing all values in `self`/ -# * #values_at: Returns an array containing values for given keys. -# -# -# #### Methods for Assigning -# -# * #[]=, #store: Associates a given key with a given value. -# * #merge: Returns the hash formed by merging each given hash into a copy of -# `self`. -# * #merge!, #update: Merges each given hash into `self`. -# * #replace: Replaces the entire contents of `self` with the contents of a -# given hash. -# -# -# #### Methods for Deleting -# -# These methods remove entries from `self`: -# -# * #clear: Removes all entries from `self`. -# * #compact!: Removes all `nil`-valued entries from `self`. -# * #delete: Removes the entry for a given key. -# * #delete_if: Removes entries selected by a given block. -# * #filter!, #select!: Keep only those entries selected by a given block. -# * #keep_if: Keep only those entries selected by a given block. -# * #reject!: Removes entries selected by a given block. -# * #shift: Removes and returns the first entry. -# -# -# These methods return a copy of `self` with some entries removed: -# -# * #compact: Returns a copy of `self` with all `nil`-valued entries removed. -# * #except: Returns a copy of `self` with entries removed for specified keys. -# * #filter, #select: Returns a copy of `self` with only those entries -# selected by a given block. -# * #reject: Returns a copy of `self` with entries removed as specified by a -# given block. -# * #slice: Returns a hash containing the entries for given keys. -# -# -# #### Methods for Iterating -# * #each, #each_pair: Calls a given block with each key-value pair. -# * #each_key: Calls a given block with each key. -# * #each_value: Calls a given block with each value. -# -# -# #### Methods for Converting -# -# * #inspect, #to_s: Returns a new String containing the hash entries. -# * #to_a: Returns a new array of 2-element arrays; each nested array contains -# a key-value pair from `self`. -# * #to_h: Returns `self` if a Hash; if a subclass of Hash, returns a Hash -# containing the entries from `self`. -# * #to_hash: Returns `self`. -# * #to_proc: Returns a proc that maps a given key to its value. -# -# -# #### Methods for Transforming Keys and Values -# -# * #transform_keys: Returns a copy of `self` with modified keys. -# * #transform_keys!: Modifies keys in `self` -# * #transform_values: Returns a copy of `self` with modified values. -# * #transform_values!: Modifies values in `self`. -# -# -# #### Other Methods -# * #flatten: Returns an array that is a 1-dimensional flattening of `self`. -# * #invert: Returns a hash with the each key-value pair inverted. -# -class Hash[unchecked out K, unchecked out V] < Object - include Enumerable[[ K, V ]] - - # - # Returns a new Hash object populated with the given objects, if any. See - # Hash::new. - # - # With no argument, returns a new empty Hash. - # - # When the single given argument is a Hash, returns a new Hash populated with - # the entries from the given Hash, excluding the default value or proc. - # - # h = {foo: 0, bar: 1, baz: 2} - # Hash[h] # => {:foo=>0, :bar=>1, :baz=>2} - # - # When the single given argument is an Array of 2-element Arrays, returns a new - # Hash object wherein each 2-element array forms a key-value entry: - # - # Hash[ [ [:foo, 0], [:bar, 1] ] ] # => {:foo=>0, :bar=>1} - # - # When the argument count is an even number; returns a new Hash object wherein - # each successive pair of arguments has become a key-value entry: - # - # Hash[:foo, 0, :bar, 1] # => {:foo=>0, :bar=>1} - # - # Raises an exception if the argument list does not conform to any of the above. - # - def self.[]: [U, V] (_ToHash[U, V]) -> ::Hash[U, V] - | [U, V] (Array[[ U, V ]]) -> ::Hash[U, V] - | (*untyped) -> ::Hash[untyped, untyped] - - # - # If `obj` is a Hash object, returns `obj`. - # - # Otherwise if `obj` responds to `:to_hash`, calls `obj.to_hash` and returns the - # result. - # - # Returns `nil` if `obj` does not respond to `:to_hash` - # - # Raises an exception unless `obj.to_hash` returns a Hash object. - # - def self.try_convert: [U, V] (_ToHash[U, V]) -> ::Hash[U, V] - | (untyped) -> (::Hash[untyped, untyped] | nil) - - public - - # - # Returns `true` if `hash` is a proper subset of `other_hash`, `false` - # otherwise: - # h1 = {foo: 0, bar: 1} - # h2 = {foo: 0, bar: 1, baz: 2} - # h1 < h2 # => true - # h2 < h1 # => false - # h1 < h1 # => false - # - def <: [A, B] (::Hash[A, B]) -> bool - - # - # Returns `true` if `hash` is a subset of `other_hash`, `false` otherwise: - # h1 = {foo: 0, bar: 1} - # h2 = {foo: 0, bar: 1, baz: 2} - # h1 <= h2 # => true - # h2 <= h1 # => false - # h1 <= h1 # => true - # - def <=: [A, B] (::Hash[A, B]) -> bool - - # - # Returns `true` if all of the following are true: - # * `object` is a Hash object. - # * `hash` and `object` have the same keys (regardless of order). - # * For each key `key`, `hash[key] == object[key]`. - # - # - # Otherwise, returns `false`. - # - # Equal: - # h1 = {foo: 0, bar: 1, baz: 2} - # h2 = {foo: 0, bar: 1, baz: 2} - # h1 == h2 # => true - # h3 = {baz: 2, bar: 1, foo: 0} - # h1 == h3 # => true - # - def ==: (untyped other) -> bool - - # - # Returns `true` if `hash` is a proper superset of `other_hash`, `false` - # otherwise: - # h1 = {foo: 0, bar: 1, baz: 2} - # h2 = {foo: 0, bar: 1} - # h1 > h2 # => true - # h2 > h1 # => false - # h1 > h1 # => false - # - def >: [A, B] (::Hash[A, B]) -> bool - - # - # Returns `true` if `hash` is a superset of `other_hash`, `false` otherwise: - # h1 = {foo: 0, bar: 1, baz: 2} - # h2 = {foo: 0, bar: 1} - # h1 >= h2 # => true - # h2 >= h1 # => false - # h1 >= h1 # => true - # - def >=: [A, B] (::Hash[A, B]) -> bool - - # - # Returns the value associated with the given `key`, if found: - # h = {foo: 0, bar: 1, baz: 2} - # h[:foo] # => 0 - # - # If `key` is not found, returns a default value (see [Default - # Values](rdoc-ref:Hash@Default+Values)): - # h = {foo: 0, bar: 1, baz: 2} - # h[:nosuch] # => nil - # - def []: %a{implicitly-returns-nil} (K arg0) -> V - - # - # Hash#store is an alias for Hash#[]=. - # - # Associates the given `value` with the given `key`; returns `value`. - # - # If the given `key` exists, replaces its value with the given `value`; the - # ordering is not affected (see [Entry Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 1} - # h[:foo] = 2 # => 2 - # h.store(:bar, 3) # => 3 - # h # => {:foo=>2, :bar=>3} - # - # If `key` does not exist, adds the `key` and `value`; the new entry is last in - # the order (see [Entry Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 1} - # h[:baz] = 2 # => 2 - # h.store(:bat, 3) # => 3 - # h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} - # - def []=: (K arg0, V arg1) -> V - - # - # Returns `true` if any element satisfies a given criterion; `false` otherwise. - # - # With no argument and no block, returns `true` if `self` is non-empty; `false` - # if empty. - # - # With argument `object` and no block, returns `true` if for any key `key` - # `h.assoc(key) == object`: - # h = {foo: 0, bar: 1, baz: 2} - # h.any?([:bar, 1]) # => true - # h.any?([:bar, 0]) # => false - # h.any?([:baz, 1]) # => false + # + # A Hash maps each of its unique keys to a specific value. # - # With no argument and a block, calls the block with each key-value pair; - # returns `true` if the block returns any truthy value, `false` otherwise: - # h = {foo: 0, bar: 1, baz: 2} - # h.any? {|key, value| value < 3 } # => true - # h.any? {|key, value| value > 3 } # => false - # - def any?: () -> bool - | (untyped pattern) -> bool - | () { (K, V) -> boolish } -> bool - - # - # If the given `key` is found, returns a 2-element Array containing that key and - # its value: - # h = {foo: 0, bar: 1, baz: 2} - # h.assoc(:bar) # => [:bar, 1] - # - # Returns `nil` if key `key` is not found. - # - def assoc: (K arg0) -> [ K, V ]? - - # - # Removes all hash entries; returns `self`. - # - def clear: () -> self - - # - # Returns a copy of `self` with all `nil`-valued entries removed: - # h = {foo: 0, bar: nil, baz: 2, bat: nil} - # h1 = h.compact - # h1 # => {:foo=>0, :baz=>2} - # - def compact: () -> ::Hash[K, V] - - # - # Returns `self` with all its `nil`-valued entries removed (in place): - # h = {foo: 0, bar: nil, baz: 2, bat: nil} - # h.compact! # => {:foo=>0, :baz=>2} - # - # Returns `nil` if no entries were removed. - # - def compact!: () -> self? - - # - # Sets `self` to consider only identity in comparing keys; two keys are - # considered the same only if they are the same object; returns `self`. - # - # By default, these two object are considered to be the same key, so `s1` will - # overwrite `s0`: - # s0 = 'x' - # s1 = 'x' - # h = {} - # h.compare_by_identity? # => false - # h[s0] = 0 - # h[s1] = 1 - # h # => {"x"=>1} + # A Hash has certain similarities to an Array, but: + # * An Array index is always an Integer. + # * A Hash key can be (almost) any object. # - # After calling #compare_by_identity, the keys are considered to be different, - # and therefore do not overwrite each other: - # h = {} - # h.compare_by_identity # => {} - # h.compare_by_identity? # => true - # h[s0] = 0 - # h[s1] = 1 - # h # => {"x"=>0, "x"=>1} - # - def compare_by_identity: () -> self - - # - # Returns `true` if #compare_by_identity has been called, `false` otherwise. - # - def compare_by_identity?: () -> bool - - # - # - def deconstruct_keys: (Array[K] | nil) -> self - - # - # Returns the default value for the given `key`. The returned value will be - # determined either by the default proc or by the default value. See [Default - # Values](rdoc-ref:Hash@Default+Values). - # - # With no argument, returns the current default value: - # h = {} - # h.default # => nil - # - # If `key` is given, returns the default value for `key`, regardless of whether - # that key exists: - # h = Hash.new { |hash, key| hash[key] = "No key #{key}"} - # h[:foo] = "Hello" - # h.default(:foo) # => "No key foo" # - def default: (?K arg0) -> V? - - # - # Sets the default value to `value`; returns `value`: - # h = {} - # h.default # => nil - # h.default = false # => false - # h.default # => false + # ### Hash Data Syntax # - # See [Default Values](rdoc-ref:Hash@Default+Values). + # The older syntax for Hash data uses the "hash rocket," `=>`: # - def default=: (V arg0) -> V - - # - # Returns the default proc for `self` (see [Default - # Values](rdoc-ref:Hash@Default+Values)): - # h = {} - # h.default_proc # => nil - # h.default_proc = proc {|hash, key| "Default value for #{key}" } - # h.default_proc.class # => Proc - # - def default_proc: () -> (Proc | nil) - - # - # Sets the default proc for `self` to `proc`: (see [Default - # Values](rdoc-ref:Hash@Default+Values)): - # h = {} - # h.default_proc # => nil - # h.default_proc = proc { |hash, key| "Default value for #{key}" } - # h.default_proc.class # => Proc - # h.default_proc = nil - # h.default_proc # => nil + # h = {:foo => 0, :bar => 1, :baz => 2} + # h # => {:foo=>0, :bar=>1, :baz=>2} # - def default_proc=: (Proc | _ToProc | nil) -> (Proc | _ToProc | nil) - - # - # Deletes the entry for the given `key` and returns its associated value. + # Alternatively, but only for a Hash key that's a Symbol, you can use a newer + # JSON-style syntax, where each bareword becomes a Symbol: # - # If no block is given and `key` is found, deletes the entry and returns the - # associated value: # h = {foo: 0, bar: 1, baz: 2} - # h.delete(:bar) # => 1 - # h # => {:foo=>0, :baz=>2} + # h # => {:foo=>0, :bar=>1, :baz=>2} # - # If no block given and `key` is not found, returns `nil`. + # You can also use a String in place of a bareword: # - # If a block is given and `key` is found, ignores the block, deletes the entry, - # and returns the associated value: - # h = {foo: 0, bar: 1, baz: 2} - # h.delete(:baz) { |key| raise 'Will never happen'} # => 2 - # h # => {:foo=>0, :bar=>1} - # - # If a block is given and `key` is not found, calls the block and returns the - # block's return value: - # h = {foo: 0, bar: 1, baz: 2} - # h.delete(:nosuch) { |key| "Key #{key} not found" } # => "Key nosuch not found" + # h = {'foo': 0, 'bar': 1, 'baz': 2} # h # => {:foo=>0, :bar=>1, :baz=>2} # - def delete: (K arg0) -> V? - | [U] (K arg0) { (K arg0) -> U } -> (U | V) - - # - # If a block given, calls the block with each key-value pair; deletes each entry - # for which the block returns a truthy value; returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.delete_if {|key, value| value > 0 } # => {:foo=>0} + # And you can mix the styles: # - # If no block given, returns a new Enumerator: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.delete_if # => #0, :bar=>1, :baz=>2}:delete_if> - # e.each { |key, value| value > 0 } # => {:foo=>0} + # h = {foo: 0, :bar => 1, 'baz': 2} + # h # => {:foo=>0, :bar=>1, :baz=>2} # - def delete_if: () { (K, V) -> boolish } -> self - | () -> ::Enumerator[[ K, V ], self] - - # - # Finds and returns the object in nested objects that is specified by `key` and - # `identifiers`. The nested objects may be instances of various classes. See - # [Dig Methods](rdoc-ref:dig_methods.rdoc). - # - # Nested Hashes: - # h = {foo: {bar: {baz: 2}}} - # h.dig(:foo) # => {:bar=>{:baz=>2}} - # h.dig(:foo, :bar) # => {:baz=>2} - # h.dig(:foo, :bar, :baz) # => 2 - # h.dig(:foo, :bar, :BAZ) # => nil - # - # Nested Hashes and Arrays: - # h = {foo: {bar: [:a, :b, :c]}} - # h.dig(:foo, :bar, 2) # => :c - # - # This method will use the [default values](rdoc-ref:Hash@Default+Values) for - # keys that are not present: - # h = {foo: {bar: [:a, :b, :c]}} - # h.dig(:hello) # => nil - # h.default_proc = -> (hash, _key) { hash } - # h.dig(:hello, :world) # => h - # h.dig(:hello, :world, :foo, :bar, 2) # => :c - # - def dig: (K, *untyped) -> untyped - - # - # Hash#each is an alias for Hash#each_pair. + # But it's an error to try the JSON-style syntax for a key that's not a bareword + # or a String: # - # Calls the given block with each key-value pair; returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2} + # # Raises SyntaxError (syntax error, unexpected ':', expecting =>): + # h = {0: 'zero'} # - # Output: - # foo: 0 - # bar: 1 - # baz: 2 + # Hash value can be omitted, meaning that value will be fetched from the context + # by the name of the key: # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.each_pair # => #0, :bar=>1, :baz=>2}:each_pair> - # h1 = e.each {|key, value| puts "#{key}: #{value}"} - # h1 # => {:foo=>0, :bar=>1, :baz=>2} - # - # Output: - # foo: 0 - # bar: 1 - # baz: 2 - # - def each: () { ([ K, V ] arg0) -> untyped } -> self - | () -> ::Enumerator[[ K, V ], self] - - # - # Calls the given block with each key; returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.each_key {|key| puts key } # => {:foo=>0, :bar=>1, :baz=>2} + # x = 0 + # y = 100 + # h = {x:, y:} + # h # => {:x=>0, :y=>100} # - # Output: - # foo - # bar - # baz + # ### Common Uses # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.each_key # => #0, :bar=>1, :baz=>2}:each_key> - # h1 = e.each {|key| puts key } - # h1 # => {:foo=>0, :bar=>1, :baz=>2} - # - # Output: - # foo - # bar - # baz - # - def each_key: () { (K arg0) -> untyped } -> ::Hash[K, V] - | () -> ::Enumerator[K, self] - - # - # Hash#each is an alias for Hash#each_pair. - # - # Calls the given block with each key-value pair; returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2} + # You can use a Hash to give names to objects: # - # Output: - # foo: 0 - # bar: 1 - # baz: 2 + # person = {name: 'Matz', language: 'Ruby'} + # person # => {:name=>"Matz", :language=>"Ruby"} # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.each_pair # => #0, :bar=>1, :baz=>2}:each_pair> - # h1 = e.each {|key, value| puts "#{key}: #{value}"} - # h1 # => {:foo=>0, :bar=>1, :baz=>2} - # - # Output: - # foo: 0 - # bar: 1 - # baz: 2 - # - alias each_pair each - - # - # Calls the given block with each value; returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.each_value {|value| puts value } # => {:foo=>0, :bar=>1, :baz=>2} + # You can use a Hash to give names to method arguments: # - # Output: - # 0 - # 1 - # 2 + # def some_method(hash) + # p hash + # end + # some_method({foo: 0, bar: 1, baz: 2}) # => {:foo=>0, :bar=>1, :baz=>2} # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.each_value # => #0, :bar=>1, :baz=>2}:each_value> - # h1 = e.each {|value| puts value } - # h1 # => {:foo=>0, :bar=>1, :baz=>2} - # - # Output: - # 0 - # 1 - # 2 - # - def each_value: () { (V arg0) -> untyped } -> self - | () -> ::Enumerator[V, self] - - # - # Returns `true` if there are no hash entries, `false` otherwise: - # {}.empty? # => true - # {foo: 0, bar: 1, baz: 2}.empty? # => false - # - def empty?: () -> bool - - # - # Returns `true` if all of the following are true: - # * `object` is a Hash object. - # * `hash` and `object` have the same keys (regardless of order). - # * For each key `key`, `h[key] eql? object[key]`. - # - # - # Otherwise, returns `false`. - # - # Equal: - # h1 = {foo: 0, bar: 1, baz: 2} - # h2 = {foo: 0, bar: 1, baz: 2} - # h1.eql? h2 # => true - # h3 = {baz: 2, bar: 1, foo: 0} - # h1.eql? h3 # => true - # - def eql?: (untyped) -> bool - - # - # Returns a new Hash excluding entries for the given `keys`: - # h = { a: 100, b: 200, c: 300 } - # h.except(:a) #=> {:b=>200, :c=>300} - # - # Any given `keys` that are not found are ignored. - # - def except: (*K) -> ::Hash[K, V] - - # - # Returns the value for the given `key`, if found. - # h = {foo: 0, bar: 1, baz: 2} - # h.fetch(:bar) # => 1 + # Note: when the last argument in a method call is a Hash, the curly braces may + # be omitted: # - # If `key` is not found and no block was given, returns `default_value`: - # {}.fetch(:nosuch, :default) # => :default + # some_method(foo: 0, bar: 1, baz: 2) # => {:foo=>0, :bar=>1, :baz=>2} # - # If `key` is not found and a block was given, yields `key` to the block and - # returns the block's return value: - # {}.fetch(:nosuch) {|key| "No key #{key}"} # => "No key nosuch" + # You can use a Hash to initialize an object: # - # Raises KeyError if neither `default_value` nor a block was given. + # class Dev + # attr_accessor :name, :language + # def initialize(hash) + # self.name = hash[:name] + # self.language = hash[:language] + # end + # end + # matz = Dev.new(name: 'Matz', language: 'Ruby') + # matz # => # # - # Note that this method does not use the values of either #default or - # #default_proc. + # ### Creating a Hash # - def fetch: (K arg0) -> V - | [X] (K arg0, X arg1) -> (V | X) - | [X] (K arg0) { (K arg0) -> X } -> (V | X) - - # - # Returns a new Array containing the values associated with the given keys - # *keys: - # h = {foo: 0, bar: 1, baz: 2} - # h.fetch_values(:baz, :foo) # => [2, 0] + # You can create a Hash object explicitly with: # - # Returns a new empty Array if no arguments given. + # * A [hash literal](rdoc-ref:syntax/literals.rdoc@Hash+Literals). # - # When a block is given, calls the block with each missing key, treating the - # block's return value as the value for that key: - # h = {foo: 0, bar: 1, baz: 2} - # values = h.fetch_values(:bar, :foo, :bad, :bam) {|key| key.to_s} - # values # => [1, 0, "bad", "bam"] # - # When no block is given, raises an exception if any given key is not found. + # You can convert certain objects to Hashes with: # - def fetch_values: (*K) -> ::Array[V] - | [X] (*K) { (K) -> X } -> ::Array[V | X] - - # - # Hash#filter is an alias for Hash#select. + # * Method #Hash. # - # Returns a new Hash object whose entries are those for which the block returns - # a truthy value: - # h = {foo: 0, bar: 1, baz: 2} - # h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1} # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.select # => #0, :bar=>1, :baz=>2}:select> - # e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # You can create a Hash by calling method Hash.new. # - def filter: () { (K, V) -> boolish } -> ::Hash[K, V] - | () -> ::Enumerator[[ K, V ], ::Hash[K, V]] - - # - # Hash#filter! is an alias for Hash#select!. + # Create an empty Hash: # - # Returns `self`, whose entries are those for which the block returns a truthy - # value: - # h = {foo: 0, bar: 1, baz: 2} - # h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} + # h = Hash.new + # h # => {} + # h.class # => Hash # - # Returns `nil` if no entries were removed. + # You can create a Hash by calling method Hash.[]. # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.select! # => #0, :bar=>1, :baz=>2}:select!> - # e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1} - # - def filter!: () { (K, V) -> boolish } -> self? - | () -> ::Enumerator[[ K, V ], self?] - - # - # Returns a new Array object that is a 1-dimensional flattening of `self`. - # - # --- - # - # By default, nested Arrays are not flattened: - # h = {foo: 0, bar: [:bat, 3], baz: 2} - # h.flatten # => [:foo, 0, :bar, [:bat, 3], :baz, 2] - # - # Takes the depth of recursive flattening from Integer argument `level`: - # h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]} - # h.flatten(1) # => [:foo, 0, :bar, [:bat, [:baz, [:bat]]]] - # h.flatten(2) # => [:foo, 0, :bar, :bat, [:baz, [:bat]]] - # h.flatten(3) # => [:foo, 0, :bar, :bat, :baz, [:bat]] - # h.flatten(4) # => [:foo, 0, :bar, :bat, :baz, :bat] - # - # When `level` is negative, flattens all nested Arrays: - # h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]} - # h.flatten(-1) # => [:foo, 0, :bar, :bat, :baz, :bat] - # h.flatten(-2) # => [:foo, 0, :bar, :bat, :baz, :bat] - # - # When `level` is zero, returns the equivalent of #to_a : - # h = {foo: 0, bar: [:bat, 3], baz: 2} - # h.flatten(0) # => [[:foo, 0], [:bar, [:bat, 3]], [:baz, 2]] - # h.flatten(0) == h.to_a # => true - # - def flatten: () -> ::Array[K | V] - | (1 level) -> ::Array[K | V] - | (Integer level) -> Array[untyped] - - # - # Methods #has_key?, #key?, and #member? are aliases for #include?. + # Create an empty Hash: # - # Returns `true` if `key` is a key in `self`, otherwise `false`. + # h = Hash[] + # h # => {} # - def has_key?: (K arg0) -> bool - - # - # Method #value? is an alias for #has_value?. + # Create a Hash with initial entries: # - # Returns `true` if `value` is a value in `self`, otherwise `false`. + # h = Hash[foo: 0, bar: 1, baz: 2] + # h # => {:foo=>0, :bar=>1, :baz=>2} # - def has_value?: (V arg0) -> bool - - # - # Returns the Integer hash-code for the hash. + # You can create a Hash by using its literal form (curly braces). # - # Two Hash objects have the same hash-code if their content is the same - # (regardless or order): - # h1 = {foo: 0, bar: 1, baz: 2} - # h2 = {baz: 2, bar: 1, foo: 0} - # h2.hash == h1.hash # => true - # h2.eql? h1 # => true + # Create an empty Hash: # - def hash: () -> Integer - - # - # Methods #has_key?, #key?, and #member? are aliases for #include?. + # h = {} + # h # => {} # - # Returns `true` if `key` is a key in `self`, otherwise `false`. + # Create a Hash with initial entries: # - alias include? has_key? - - # - # Returns a new String containing the hash entries: # h = {foo: 0, bar: 1, baz: 2} - # h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}" + # h # => {:foo=>0, :bar=>1, :baz=>2} # - # Hash#to_s is an alias for Hash#inspect. + # ### Hash Value Basics # - def inspect: () -> String - - # - # Returns a new Hash object with the each key-value pair inverted: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.invert - # h1 # => {0=>:foo, 1=>:bar, 2=>:baz} - # - # Overwrites any repeated new keys: (see [Entry - # Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 0, baz: 0} - # h.invert # => {0=>:baz} - # - def invert: () -> ::Hash[V, K] - - # - # Calls the block for each key-value pair; retains the entry if the block - # returns a truthy value; otherwise deletes the entry; returns `self`. - # h = {foo: 0, bar: 1, baz: 2} - # h.keep_if { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2} + # The simplest way to retrieve a Hash value (instance method #[]): # - # Returns a new Enumerator if no block given: # h = {foo: 0, bar: 1, baz: 2} - # e = h.keep_if # => #0, :bar=>1, :baz=>2}:keep_if> - # e.each { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2} - # - def keep_if: () { (K, V) -> boolish } -> self - | () -> ::Enumerator[[ K, V ], self] - - # - # Returns the key for the first-found entry with the given `value` (see [Entry - # Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 2, baz: 2} - # h.key(0) # => :foo - # h.key(2) # => :bar - # - # Returns `nil` if so such value is found. - # - def key: (V) -> K? - - # - # Methods #has_key?, #key?, and #member? are aliases for #include?. + # h[:foo] # => 0 # - # Returns `true` if `key` is a key in `self`, otherwise `false`. + # The simplest way to create or update a Hash value (instance method #[]=): # - alias key? has_key? - - # - # Returns a new Array containing all keys in `self`: # h = {foo: 0, bar: 1, baz: 2} - # h.keys # => [:foo, :bar, :baz] + # h[:bat] = 3 # => 3 + # h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} + # h[:foo] = 4 # => 4 + # h # => {:foo=>4, :bar=>1, :baz=>2, :bat=>3} # - def keys: () -> ::Array[K] - - # - # Returns the count of entries in `self`: - # {foo: 0, bar: 1, baz: 2}.length # => 3 + # The simplest way to delete a Hash entry (instance method #delete): # - # Hash#length is an alias for Hash#size. + # h = {foo: 0, bar: 1, baz: 2} + # h.delete(:bar) # => 1 + # h # => {:foo=>0, :baz=>2} # - def length: () -> Integer - - # - # Methods #has_key?, #key?, and #member? are aliases for #include?. + # ### Entry Order # - # Returns `true` if `key` is a key in `self`, otherwise `false`. + # A Hash object presents its entries in the order of their creation. This is + # seen in: # - alias member? has_key? - - # - # Returns the new Hash formed by merging each of `other_hashes` into a copy of - # `self`. + # * Iterative methods such as `each`, `each_key`, `each_pair`, `each_value`. + # * Other order-sensitive methods such as `shift`, `keys`, `values`. + # * The String returned by method `inspect`. # - # Each argument in `other_hashes` must be a Hash. # - # --- + # A new Hash has its initial ordering per the given entries: # - # With arguments and no block: - # * Returns the new Hash object formed by merging each successive Hash in - # `other_hashes` into `self`. - # * Each new-key entry is added at the end. - # * Each duplicate-key entry's value overwrites the previous value. + # h = Hash[foo: 0, bar: 1] + # h # => {:foo=>0, :bar=>1} # + # New entries are added at the end: # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h.merge(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # h[:baz] = 2 + # h # => {:foo=>0, :bar=>1, :baz=>2} # - # With arguments and a block: - # * Returns a new Hash object that is the merge of `self` and each given hash. - # * The given hashes are merged left to right. - # * Each new-key entry is added at the end. - # * For each duplicate key: - # * Calls the block with the key and the old and new values. - # * The block's return value becomes the new value for the entry. + # Updating a value does not affect the order: # + # h[:baz] = 3 + # h # => {:foo=>0, :bar=>1, :baz=>3} # + # But re-creating a deleted entry can affect the order: # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h3 = h.merge(h1, h2) { |key, old_value, new_value| old_value + new_value } - # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # h.delete(:foo) + # h[:foo] = 5 + # h # => {:bar=>1, :baz=>3, :foo=>5} # - # With no arguments: - # * Returns a copy of `self`. - # * The block, if given, is ignored. + # ### Hash Keys # + # #### Hash Key Equivalence # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h.merge # => {:foo=>0, :bar=>1, :baz=>2} - # h1 = h.merge { |key, old_value, new_value| raise 'Cannot happen' } - # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # Two objects are treated as the same hash key when their `hash` value is + # identical and the two objects are `eql?` to each other. # - def merge: [A, B] (*::Hash[A, B] other_hashes) -> ::Hash[A | K, B | V] - | [A, B, C] (*::Hash[A, B] other_hashes) { (K key, V oldval, B newval) -> C } -> ::Hash[A | K, B | V | C] - - # - # Merges each of `other_hashes` into `self`; returns `self`. + # #### Modifying an Active Hash Key # - # Each argument in `other_hashes` must be a Hash. + # Modifying a Hash key while it is in use damages the hash's index. # - # Method #update is an alias for #merge!. + # This Hash has keys that are Arrays: # - # With arguments and no block: - # * Returns `self`, after the given hashes are merged into it. - # * The given hashes are merged left to right. - # * Each new entry is added at the end. - # * Each duplicate-key entry's value overwrites the previous value. + # a0 = [ :foo, :bar ] + # a1 = [ :baz, :bat ] + # h = {a0 => 0, a1 => 1} + # h.include?(a0) # => true + # h[a0] # => 0 + # a0.hash # => 110002110 # + # Modifying array element `a0[0]` changes its hash value: # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # a0[0] = :bam + # a0.hash # => 1069447059 # - # With arguments and a block: - # * Returns `self`, after the given hashes are merged. - # * The given hashes are merged left to right. - # * Each new-key entry is added at the end. - # * For each duplicate key: - # * Calls the block with the key and the old and new values. - # * The block's return value becomes the new value for the entry. + # And damages the Hash index: # + # h.include?(a0) # => false + # h[a0] # => nil # + # You can repair the hash index using method `rehash`: # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value } - # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # h.rehash # => {[:bam, :bar]=>0, [:baz, :bat]=>1} + # h.include?(a0) # => true + # h[a0] # => 0 # - # With no arguments: - # * Returns `self`, unmodified. - # * The block, if given, is ignored. + # A String key is always safe. That's because an unfrozen String passed as a key + # will be replaced by a duplicated and frozen String: # + # s = 'foo' + # s.frozen? # => false + # h = {s => 0} + # first_key = h.keys.first + # first_key.frozen? # => true # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h.merge # => {:foo=>0, :bar=>1, :baz=>2} - # h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' } - # h1 # => {:foo=>0, :bar=>1, :baz=>2} - # - def merge!: (*::Hash[K, V] other_hashes) -> self - | (*::Hash[K, V] other_hashes) { (K key, V oldval, V newval) -> V } -> self - - # - # Returns a new 2-element Array consisting of the key and value of the - # first-found entry whose value is `==` to value (see [Entry - # Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 1, baz: 1} - # h.rassoc(1) # => [:bar, 1] - # - # Returns `nil` if no such value found. - # - def rassoc: (V) -> [ K, V ]? - - # - # Rebuilds the hash table by recomputing the hash index for each key; returns - # `self`. - # - # The hash table becomes invalid if the hash value of a key has changed after - # the entry was created. See [Modifying an Active Hash - # Key](rdoc-ref:Hash@Modifying+an+Active+Hash+Key). - # - def rehash: () -> self - - # - # Returns a new Hash object whose entries are all those from `self` for which - # the block returns `false` or `nil`: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.reject {|key, value| key.start_with?('b') } - # h1 # => {:foo=>0} + # #### User-Defined Hash Keys # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.reject # => #0, :bar=>1, :baz=>2}:reject> - # h1 = e.each {|key, value| key.start_with?('b') } - # h1 # => {:foo=>0} - # - def reject: () -> ::Enumerator[[ K, V ], ::Hash[K, V]] - | () { (K, V) -> boolish } -> ::Hash[K, V] - - # - # Returns `self`, whose remaining entries are those for which the block returns - # `false` or `nil`: - # h = {foo: 0, bar: 1, baz: 2} - # h.reject! {|key, value| value < 2 } # => {:baz=>2} + # To be useable as a Hash key, objects must implement the methods `hash` and + # `eql?`. Note: this requirement does not apply if the Hash uses + # #compare_by_identity since comparison will then rely on the keys' object id + # instead of `hash` and `eql?`. # - # Returns `nil` if no entries are removed. + # Object defines basic implementation for `hash` and `eq?` that makes each + # object a distinct key. Typically, user-defined classes will want to override + # these methods to provide meaningful behavior, or for example inherit Struct + # that has useful definitions for these. # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.reject! # => #0, :bar=>1, :baz=>2}:reject!> - # e.each {|key, value| key.start_with?('b') } # => {:foo=>0} + # A typical implementation of `hash` is based on the object's data while `eql?` + # is usually aliased to the overridden `==` method: # - def reject!: () -> ::Enumerator[[ K, V ], self?] - | () { (K, V) -> boolish } -> self? - - # - # Replaces the entire contents of `self` with the contents of `other_hash`; - # returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4} + # class Book + # attr_reader :author, :title # - def replace: (Hash[K, V]) -> self - - # - # Hash#filter is an alias for Hash#select. + # def initialize(author, title) + # @author = author + # @title = title + # end # - # Returns a new Hash object whose entries are those for which the block returns - # a truthy value: - # h = {foo: 0, bar: 1, baz: 2} - # h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # def ==(other) + # self.class === other && + # other.author == @author && + # other.title == @title + # end # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.select # => #0, :bar=>1, :baz=>2}:select> - # e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # alias eql? == # - alias select filter - - # - # Hash#filter! is an alias for Hash#select!. + # def hash + # @author.hash ^ @title.hash # XOR + # end + # end # - # Returns `self`, whose entries are those for which the block returns a truthy - # value: - # h = {foo: 0, bar: 1, baz: 2} - # h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} + # book1 = Book.new 'matz', 'Ruby in a Nutshell' + # book2 = Book.new 'matz', 'Ruby in a Nutshell' # - # Returns `nil` if no entries were removed. + # reviews = {} # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.select! # => #0, :bar=>1, :baz=>2}:select!> - # e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1} + # reviews[book1] = 'Great reference!' + # reviews[book2] = 'Nice and compact!' # - alias select! filter! - - # - # Removes the first hash entry (see [Entry Order](rdoc-ref:Hash@Entry+Order)); - # returns a 2-element Array containing the removed key and value: - # h = {foo: 0, bar: 1, baz: 2} - # h.shift # => [:foo, 0] - # h # => {:bar=>1, :baz=>2} + # reviews.length #=> 1 # - # Returns nil if the hash is empty. + # ### Default Values # - def shift: () -> [ K, V ]? - - # - # Returns the count of entries in `self`: - # {foo: 0, bar: 1, baz: 2}.length # => 3 + # The methods #[], #values_at and #dig need to return the value associated to a + # certain key. When that key is not found, that value will be determined by its + # default proc (if any) or else its default (initially `nil`). # - # Hash#length is an alias for Hash#size. + # You can retrieve the default value with method #default: # - alias size length - - # - # Returns a new Hash object containing the entries for the given `keys`: - # h = {foo: 0, bar: 1, baz: 2} - # h.slice(:baz, :foo) # => {:baz=>2, :foo=>0} + # h = Hash.new + # h.default # => nil # - # Any given `keys` that are not found are ignored. + # You can set the default value by passing an argument to method Hash.new or + # with method #default= # - def slice: (*K) -> ::Hash[K, V] - - # - # Hash#store is an alias for Hash#[]=. - # - # Associates the given `value` with the given `key`; returns `value`. - # - # If the given `key` exists, replaces its value with the given `value`; the - # ordering is not affected (see [Entry Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 1} - # h[:foo] = 2 # => 2 - # h.store(:bar, 3) # => 3 - # h # => {:foo=>2, :bar=>3} - # - # If `key` does not exist, adds the `key` and `value`; the new entry is last in - # the order (see [Entry Order](rdoc-ref:Hash@Entry+Order)): - # h = {foo: 0, bar: 1} - # h[:baz] = 2 # => 2 - # h.store(:bat, 3) # => 3 - # h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} + # h = Hash.new(-1) + # h.default # => -1 + # h.default = 0 + # h.default # => 0 # - alias store []= - - # - # Returns a new Array of 2-element Array objects; each nested Array contains a - # key-value pair from `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.to_a # => [[:foo, 0], [:bar, 1], [:baz, 2]] + # This default value is returned for #[], #values_at and #dig when a key is not + # found: # - def to_a: () -> ::Array[[ K, V ]] - - # - # For an instance of Hash, returns `self`. + # counts = {foo: 42} + # counts.default # => nil (default) + # counts[:foo] = 42 + # counts[:bar] # => nil + # counts.default = 0 + # counts[:bar] # => 0 + # counts.values_at(:foo, :bar, :baz) # => [42, 0, 0] + # counts.dig(:bar) # => 0 # - # For a subclass of Hash, returns a new Hash containing the content of `self`. + # Note that the default value is used without being duplicated. It is not + # advised to set the default value to a mutable object: # - # When a block is given, returns a new Hash object whose content is based on the - # block; the block should return a 2-element Array object specifying the - # key-value pair to be included in the returned Array: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.to_h {|key, value| [value, key] } - # h1 # => {0=>:foo, 1=>:bar, 2=>:baz} + # synonyms = Hash.new([]) + # synonyms[:hello] # => [] + # synonyms[:hello] << :hi # => [:hi], but this mutates the default! + # synonyms.default # => [:hi] + # synonyms[:world] << :universe + # synonyms[:world] # => [:hi, :universe], oops + # synonyms.keys # => [], oops # - def to_h: () -> Hash[K, V] - | [A, B] () { (K, V) -> [ A, B ] } -> Hash[A, B] - - # - # Returns `self`. + # To use a mutable object as default, it is recommended to use a default proc # - def to_hash: () -> self - - # - # Returns a Proc object that maps a key to its value: - # h = {foo: 0, bar: 1, baz: 2} - # proc = h.to_proc - # proc.class # => Proc - # proc.call(:foo) # => 0 - # proc.call(:bar) # => 1 - # proc.call(:nosuch) # => nil + # #### Default Proc # - def to_proc: () -> ^(K) -> V? - - # - # Returns a new String containing the hash entries: - # h = {foo: 0, bar: 1, baz: 2} - # h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}" + # When the default proc for a Hash is set (i.e., not `nil`), the default value + # returned by method #[] is determined by the default proc alone. # - # Hash#to_s is an alias for Hash#inspect. + # You can retrieve the default proc with method #default_proc: # - alias to_s inspect - - # - # Returns a new Hash object; each entry has: - # * A key provided by the block. - # * The value from `self`. + # h = Hash.new + # h.default_proc # => nil # + # You can set the default proc by calling Hash.new with a block or calling the + # method #default_proc= # - # An optional hash argument can be provided to map keys to new keys. Any key not - # given will be mapped using the provided block, or remain the same if no block - # is given. + # h = Hash.new { |hash, key| "Default value for #{key}" } + # h.default_proc.class # => Proc + # h.default_proc = proc { |hash, key| "Default value for #{key.inspect}" } + # h.default_proc.class # => Proc # - # Transform keys: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.transform_keys {|key| key.to_s } - # h1 # => {"foo"=>0, "bar"=>1, "baz"=>2} + # When the default proc is set (i.e., not `nil`) and method #[] is called with + # with a non-existent key, #[] calls the default proc with both the Hash object + # itself and the missing key, then returns the proc's return value: # - # h.transform_keys(foo: :bar, bar: :foo) - # #=> {bar: 0, foo: 1, baz: 2} + # h = Hash.new { |hash, key| "Default value for #{key}" } + # h[:nosuch] # => "Default value for nosuch" # - # h.transform_keys(foo: :hello, &:to_s) - # #=> {:hello=>0, "bar"=>1, "baz"=>2} + # Note that in the example above no entry for key `:nosuch` is created: # - # Overwrites values for duplicate keys: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.transform_keys {|key| :bat } - # h1 # => {:bat=>2} + # h.include?(:nosuch) # => false # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.transform_keys # => #0, :bar=>1, :baz=>2}:transform_keys> - # h1 = e.each { |key| key.to_s } - # h1 # => {"foo"=>0, "bar"=>1, "baz"=>2} - # - def transform_keys: () -> Enumerator[K, Hash[untyped, V]] - | [A] () { (K) -> A } -> Hash[A, V] - - # - # Same as Hash#transform_keys but modifies the receiver in place instead of - # returning a new hash. - # - def transform_keys!: () -> Enumerator[K, self] - | () { (K) -> K } -> self - - # - # Returns a new Hash object; each entry has: - # * A key from `self`. - # * A value provided by the block. - # - # - # Transform values: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = h.transform_values {|value| value * 100} - # h1 # => {:foo=>0, :bar=>100, :baz=>200} + # However, the proc itself can add a new entry: # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.transform_values # => #0, :bar=>1, :baz=>2}:transform_values> - # h1 = e.each { |value| value * 100} - # h1 # => {:foo=>0, :bar=>100, :baz=>200} - # - def transform_values: () -> Enumerator[V, Hash[K, untyped]] - | [A] () { (V) -> A } -> Hash[K, A] - - # - # Returns `self`, whose keys are unchanged, and whose values are determined by - # the given block. - # h = {foo: 0, bar: 1, baz: 2} - # h.transform_values! {|value| value * 100} # => {:foo=>0, :bar=>100, :baz=>200} + # synonyms = Hash.new { |hash, key| hash[key] = [] } + # synonyms.include?(:hello) # => false + # synonyms[:hello] << :hi # => [:hi] + # synonyms[:world] << :universe # => [:universe] + # synonyms.keys # => [:hello, :world] # - # Returns a new Enumerator if no block given: - # h = {foo: 0, bar: 1, baz: 2} - # e = h.transform_values! # => #0, :bar=>100, :baz=>200}:transform_values!> - # h1 = e.each {|value| value * 100} - # h1 # => {:foo=>0, :bar=>100, :baz=>200} + # Note that setting the default proc will clear the default value and vice + # versa. # - def transform_values!: () -> Enumerator[V, self] - | () { (V) -> V } -> self - - # - # Merges each of `other_hashes` into `self`; returns `self`. + # ### What's Here # - # Each argument in `other_hashes` must be a Hash. + # First, what's elsewhere. Class Hash: # - # Method #update is an alias for #merge!. + # * Inherits from [class Object](rdoc-ref:Object@What-27s+Here). + # * Includes [module Enumerable](rdoc-ref:Enumerable@What-27s+Here), which + # provides dozens of additional methods. # - # With arguments and no block: - # * Returns `self`, after the given hashes are merged into it. - # * The given hashes are merged left to right. - # * Each new entry is added at the end. - # * Each duplicate-key entry's value overwrites the previous value. # + # Here, class Hash provides methods that are useful for: # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # * [Creating a Hash](rdoc-ref:Hash@Methods+for+Creating+a+Hash) + # * [Setting Hash State](rdoc-ref:Hash@Methods+for+Setting+Hash+State) + # * [Querying](rdoc-ref:Hash@Methods+for+Querying) + # * [Comparing](rdoc-ref:Hash@Methods+for+Comparing) + # * [Fetching](rdoc-ref:Hash@Methods+for+Fetching) + # * [Assigning](rdoc-ref:Hash@Methods+for+Assigning) + # * [Deleting](rdoc-ref:Hash@Methods+for+Deleting) + # * [Iterating](rdoc-ref:Hash@Methods+for+Iterating) + # * [Converting](rdoc-ref:Hash@Methods+for+Converting) + # * [Transforming Keys and + # Values](rdoc-ref:Hash@Methods+for+Transforming+Keys+and+Values) + # * [And more....](rdoc-ref:Hash@Other+Methods) # - # With arguments and a block: - # * Returns `self`, after the given hashes are merged. - # * The given hashes are merged left to right. - # * Each new-key entry is added at the end. - # * For each duplicate key: - # * Calls the block with the key and the old and new values. - # * The block's return value becomes the new value for the entry. # + # Class Hash also includes methods from module Enumerable. # + # #### Methods for Creating a Hash # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h1 = {bat: 3, bar: 4} - # h2 = {bam: 5, bat:6} - # h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value } - # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # * ::[]: Returns a new hash populated with given objects. + # * ::new: Returns a new empty hash. + # * ::try_convert: Returns a new hash created from a given object. # - # With no arguments: - # * Returns `self`, unmodified. - # * The block, if given, is ignored. # + # #### Methods for Setting Hash State # - # Example: - # h = {foo: 0, bar: 1, baz: 2} - # h.merge # => {:foo=>0, :bar=>1, :baz=>2} - # h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' } - # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # * #compare_by_identity: Sets `self` to consider only identity in comparing + # keys. + # * #default=: Sets the default to a given value. + # * #default_proc=: Sets the default proc to a given proc. + # * #rehash: Rebuilds the hash table by recomputing the hash index for each + # key. # - alias update merge! - - # - # Method #value? is an alias for #has_value?. # - # Returns `true` if `value` is a value in `self`, otherwise `false`. + # #### Methods for Querying # - alias value? has_value? - - # - # Returns a new Array containing all values in `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.values # => [0, 1, 2] + # * #any?: Returns whether any element satisfies a given criterion. + # * #compare_by_identity?: Returns whether the hash considers only identity + # when comparing keys. + # * #default: Returns the default value, or the default value for a given key. + # * #default_proc: Returns the default proc. + # * #empty?: Returns whether there are no entries. + # * #eql?: Returns whether a given object is equal to `self`. + # * #hash: Returns the integer hash code. + # * #has_value?: Returns whether a given object is a value in `self`. + # * #include?, #has_key?, #member?, #key?: Returns whether a given object is a + # key in `self`. + # * #length, #size: Returns the count of entries. + # * #value?: Returns whether a given object is a value in `self`. # - def values: () -> ::Array[V] - - # - # Returns a new Array containing values for the given `keys`: - # h = {foo: 0, bar: 1, baz: 2} - # h.values_at(:baz, :foo) # => [2, 0] # - # The [default values](rdoc-ref:Hash@Default+Values) are returned for any keys - # that are not found: - # h.values_at(:hello, :foo) # => [nil, 0] + # #### Methods for Comparing # - def values_at: (*K arg0) -> ::Array[V?] - - private - - # - # Returns a new empty Hash object. + # * #<: Returns whether `self` is a proper subset of a given object. + # * #<=: Returns whether `self` is a subset of a given object. + # * #==: Returns whether a given object is equal to `self`. + # * #>: Returns whether `self` is a proper superset of a given object + # * #>=: Returns whether `self` is a proper superset of a given object. # - # The initial default value and initial default proc for the new hash depend on - # which form above was used. See [Default Values](rdoc-ref:Hash@Default+Values). # - # If neither an argument nor a block given, initializes both the default value - # and the default proc to `nil`: - # h = Hash.new - # h.default # => nil - # h.default_proc # => nil + # #### Methods for Fetching # - # If argument `default_value` given but no block given, initializes the default - # value to the given `default_value` and the default proc to `nil`: - # h = Hash.new(false) - # h.default # => false - # h.default_proc # => nil + # * #[]: Returns the value associated with a given key. + # * #assoc: Returns a 2-element array containing a given key and its value. + # * #dig: Returns the object in nested objects that is specified by a given + # key and additional arguments. + # * #fetch: Returns the value for a given key. + # * #fetch_values: Returns array containing the values associated with given + # keys. + # * #key: Returns the key for the first-found entry with a given value. + # * #keys: Returns an array containing all keys in `self`. + # * #rassoc: Returns a 2-element array consisting of the key and value of the + # first-found entry having a given value. + # * #values: Returns an array containing all values in `self`/ + # * #values_at: Returns an array containing values for given keys. # - # If a block given but no argument, stores the block as the default proc and - # sets the default value to `nil`: - # h = Hash.new {|hash, key| "Default value for #{key}" } - # h.default # => nil - # h.default_proc.class # => Proc - # h[:nosuch] # => "Default value for nosuch" # - def initialize: () -> void - | (untyped default) -> void - | [A, B] () { (Hash[A, B] hash, A key) -> B } -> void + # #### Methods for Assigning + # + # * #[]=, #store: Associates a given key with a given value. + # * #merge: Returns the hash formed by merging each given hash into a copy of + # `self`. + # * #merge!, #update: Merges each given hash into `self`. + # * #replace: Replaces the entire contents of `self` with the contents of a + # given hash. + # + # + # #### Methods for Deleting + # + # These methods remove entries from `self`: + # + # * #clear: Removes all entries from `self`. + # * #compact!: Removes all `nil`-valued entries from `self`. + # * #delete: Removes the entry for a given key. + # * #delete_if: Removes entries selected by a given block. + # * #filter!, #select!: Keep only those entries selected by a given block. + # * #keep_if: Keep only those entries selected by a given block. + # * #reject!: Removes entries selected by a given block. + # * #shift: Removes and returns the first entry. + # + # + # These methods return a copy of `self` with some entries removed: + # + # * #compact: Returns a copy of `self` with all `nil`-valued entries removed. + # * #except: Returns a copy of `self` with entries removed for specified keys. + # * #filter, #select: Returns a copy of `self` with only those entries + # selected by a given block. + # * #reject: Returns a copy of `self` with entries removed as specified by a + # given block. + # * #slice: Returns a hash containing the entries for given keys. + # + # + # #### Methods for Iterating + # * #each, #each_pair: Calls a given block with each key-value pair. + # * #each_key: Calls a given block with each key. + # * #each_value: Calls a given block with each value. + # + # + # #### Methods for Converting + # + # * #inspect, #to_s: Returns a new String containing the hash entries. + # * #to_a: Returns a new array of 2-element arrays; each nested array contains + # a key-value pair from `self`. + # * #to_h: Returns `self` if a Hash; if a subclass of Hash, returns a Hash + # containing the entries from `self`. + # * #to_hash: Returns `self`. + # * #to_proc: Returns a proc that maps a given key to its value. + # + # + # #### Methods for Transforming Keys and Values + # + # * #transform_keys: Returns a copy of `self` with modified keys. + # * #transform_keys!: Modifies keys in `self` + # * #transform_values: Returns a copy of `self` with modified values. + # * #transform_values!: Modifies values in `self`. + # + # + # #### Other Methods + # * #flatten: Returns an array that is a 1-dimensional flattening of `self`. + # * #invert: Returns a hash with the each key-value pair inverted. + # + class Hash[unchecked out K, unchecked out V] < Object + include Enumerable[[ K, V ]] + + interface _Key + def hash: () -> Integer + + def eql?: (untyped rhs) -> boolish + end + + # + # Returns a new Hash object populated with the given objects, if any. See + # Hash::new. + # + # With no argument, returns a new empty Hash. + # + # When the single given argument is a Hash, returns a new Hash populated with + # the entries from the given Hash, excluding the default value or proc. + # + # h = {foo: 0, bar: 1, baz: 2} + # Hash[h] # => {:foo=>0, :bar=>1, :baz=>2} + # + # When the single given argument is an Array of 2-element Arrays, returns a new + # Hash object wherein each 2-element array forms a key-value entry: + # + # Hash[ [ [:foo, 0], [:bar, 1] ] ] # => {:foo=>0, :bar=>1} + # + # When the argument count is an even number; returns a new Hash object wherein + # each successive pair of arguments has become a key-value entry: + # + # Hash[:foo, 0, :bar, 1] # => {:foo=>0, :bar=>1} + # + # Raises an exception if the argument list does not conform to any of the above. + # + def self.[]: [U, V] (_ToHash[U, V]) -> ::Hash[U, V] + | [U, V] (Array[[ U, V ]]) -> ::Hash[U, V] + | (*untyped) -> ::Hash[untyped, untyped] + + # + # If `obj` is a Hash object, returns `obj`. + # + # Otherwise if `obj` responds to `:to_hash`, calls `obj.to_hash` and returns the + # result. + # + # Returns `nil` if `obj` does not respond to `:to_hash` + # + # Raises an exception unless `obj.to_hash` returns a Hash object. + # + def self.try_convert: [U, V] (_ToHash[U, V]) -> ::Hash[U, V] + | (untyped) -> (::Hash[untyped, untyped] | nil) + + public + + # + # Returns `true` if `hash` is a proper subset of `other_hash`, `false` + # otherwise: + # h1 = {foo: 0, bar: 1} + # h2 = {foo: 0, bar: 1, baz: 2} + # h1 < h2 # => true + # h2 < h1 # => false + # h1 < h1 # => false + # + def <: [A, B] (::Hash[A, B]) -> bool + + # + # Returns `true` if `hash` is a subset of `other_hash`, `false` otherwise: + # h1 = {foo: 0, bar: 1} + # h2 = {foo: 0, bar: 1, baz: 2} + # h1 <= h2 # => true + # h2 <= h1 # => false + # h1 <= h1 # => true + # + def <=: [A, B] (::Hash[A, B]) -> bool + + # + # Returns `true` if all of the following are true: + # * `object` is a Hash object. + # * `hash` and `object` have the same keys (regardless of order). + # * For each key `key`, `hash[key] == object[key]`. + # + # + # Otherwise, returns `false`. + # + # Equal: + # h1 = {foo: 0, bar: 1, baz: 2} + # h2 = {foo: 0, bar: 1, baz: 2} + # h1 == h2 # => true + # h3 = {baz: 2, bar: 1, foo: 0} + # h1 == h3 # => true + # + def ==: (untyped other) -> bool + + # + # Returns `true` if `hash` is a proper superset of `other_hash`, `false` + # otherwise: + # h1 = {foo: 0, bar: 1, baz: 2} + # h2 = {foo: 0, bar: 1} + # h1 > h2 # => true + # h2 > h1 # => false + # h1 > h1 # => false + # + def >: [A, B] (::Hash[A, B]) -> bool + + # + # Returns `true` if `hash` is a superset of `other_hash`, `false` otherwise: + # h1 = {foo: 0, bar: 1, baz: 2} + # h2 = {foo: 0, bar: 1} + # h1 >= h2 # => true + # h2 >= h1 # => false + # h1 >= h1 # => true + # + def >=: [A, B] (::Hash[A, B]) -> bool + + # + # Returns the value associated with the given `key`, if found: + # h = {foo: 0, bar: 1, baz: 2} + # h[:foo] # => 0 + # + # If `key` is not found, returns a default value (see [Default + # Values](rdoc-ref:Hash@Default+Values)): + # h = {foo: 0, bar: 1, baz: 2} + # h[:nosuch] # => nil + # + def []: %a{implicitly-returns-nil} (K arg0) -> V + + # + # Hash#store is an alias for Hash#[]=. + # + # Associates the given `value` with the given `key`; returns `value`. + # + # If the given `key` exists, replaces its value with the given `value`; the + # ordering is not affected (see [Entry Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 1} + # h[:foo] = 2 # => 2 + # h.store(:bar, 3) # => 3 + # h # => {:foo=>2, :bar=>3} + # + # If `key` does not exist, adds the `key` and `value`; the new entry is last in + # the order (see [Entry Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 1} + # h[:baz] = 2 # => 2 + # h.store(:bat, 3) # => 3 + # h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} + # + def []=: (K arg0, V arg1) -> V + + # + # Returns `true` if any element satisfies a given criterion; `false` otherwise. + # + # With no argument and no block, returns `true` if `self` is non-empty; `false` + # if empty. + # + # With argument `object` and no block, returns `true` if for any key `key` + # `h.assoc(key) == object`: + # h = {foo: 0, bar: 1, baz: 2} + # h.any?([:bar, 1]) # => true + # h.any?([:bar, 0]) # => false + # h.any?([:baz, 1]) # => false + # + # With no argument and a block, calls the block with each key-value pair; + # returns `true` if the block returns any truthy value, `false` otherwise: + # h = {foo: 0, bar: 1, baz: 2} + # h.any? {|key, value| value < 3 } # => true + # h.any? {|key, value| value > 3 } # => false + # + def any?: () -> bool + | (untyped pattern) -> bool + | () { (K, V) -> boolish } -> bool + + # + # If the given `key` is found, returns a 2-element Array containing that key and + # its value: + # h = {foo: 0, bar: 1, baz: 2} + # h.assoc(:bar) # => [:bar, 1] + # + # Returns `nil` if key `key` is not found. + # + def assoc: (K arg0) -> [ K, V ]? + + # + # Removes all hash entries; returns `self`. + # + def clear: () -> self + + # + # Returns a copy of `self` with all `nil`-valued entries removed: + # h = {foo: 0, bar: nil, baz: 2, bat: nil} + # h1 = h.compact + # h1 # => {:foo=>0, :baz=>2} + # + def compact: () -> ::Hash[K, V] + + # + # Returns `self` with all its `nil`-valued entries removed (in place): + # h = {foo: 0, bar: nil, baz: 2, bat: nil} + # h.compact! # => {:foo=>0, :baz=>2} + # + # Returns `nil` if no entries were removed. + # + def compact!: () -> self? + + # + # Sets `self` to consider only identity in comparing keys; two keys are + # considered the same only if they are the same object; returns `self`. + # + # By default, these two object are considered to be the same key, so `s1` will + # overwrite `s0`: + # s0 = 'x' + # s1 = 'x' + # h = {} + # h.compare_by_identity? # => false + # h[s0] = 0 + # h[s1] = 1 + # h # => {"x"=>1} + # + # After calling #compare_by_identity, the keys are considered to be different, + # and therefore do not overwrite each other: + # h = {} + # h.compare_by_identity # => {} + # h.compare_by_identity? # => true + # h[s0] = 0 + # h[s1] = 1 + # h # => {"x"=>0, "x"=>1} + # + def compare_by_identity: () -> self + + # + # Returns `true` if #compare_by_identity has been called, `false` otherwise. + # + def compare_by_identity?: () -> bool + + # + # + def deconstruct_keys: (Array[K] | nil) -> self + + # + # Returns the default value for the given `key`. The returned value will be + # determined either by the default proc or by the default value. See [Default + # Values](rdoc-ref:Hash@Default+Values). + # + # With no argument, returns the current default value: + # h = {} + # h.default # => nil + # + # If `key` is given, returns the default value for `key`, regardless of whether + # that key exists: + # h = Hash.new { |hash, key| hash[key] = "No key #{key}"} + # h[:foo] = "Hello" + # h.default(:foo) # => "No key foo" + # + def default: (?K arg0) -> V? + + # + # Sets the default value to `value`; returns `value`: + # h = {} + # h.default # => nil + # h.default = false # => false + # h.default # => false + # + # See [Default Values](rdoc-ref:Hash@Default+Values). + # + def default=: (V arg0) -> V + + # + # Returns the default proc for `self` (see [Default + # Values](rdoc-ref:Hash@Default+Values)): + # h = {} + # h.default_proc # => nil + # h.default_proc = proc {|hash, key| "Default value for #{key}" } + # h.default_proc.class # => Proc + # + def default_proc: () -> (Proc | nil) + + # + # Sets the default proc for `self` to `proc`: (see [Default + # Values](rdoc-ref:Hash@Default+Values)): + # h = {} + # h.default_proc # => nil + # h.default_proc = proc { |hash, key| "Default value for #{key}" } + # h.default_proc.class # => Proc + # h.default_proc = nil + # h.default_proc # => nil + # + def default_proc=: (Proc | _ToProc | nil) -> (Proc | _ToProc | nil) + + # + # Deletes the entry for the given `key` and returns its associated value. + # + # If no block is given and `key` is found, deletes the entry and returns the + # associated value: + # h = {foo: 0, bar: 1, baz: 2} + # h.delete(:bar) # => 1 + # h # => {:foo=>0, :baz=>2} + # + # If no block given and `key` is not found, returns `nil`. + # + # If a block is given and `key` is found, ignores the block, deletes the entry, + # and returns the associated value: + # h = {foo: 0, bar: 1, baz: 2} + # h.delete(:baz) { |key| raise 'Will never happen'} # => 2 + # h # => {:foo=>0, :bar=>1} + # + # If a block is given and `key` is not found, calls the block and returns the + # block's return value: + # h = {foo: 0, bar: 1, baz: 2} + # h.delete(:nosuch) { |key| "Key #{key} not found" } # => "Key nosuch not found" + # h # => {:foo=>0, :bar=>1, :baz=>2} + # + def delete: (K arg0) -> V? + | [U] (K arg0) { (K arg0) -> U } -> (U | V) + + # + # If a block given, calls the block with each key-value pair; deletes each entry + # for which the block returns a truthy value; returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.delete_if {|key, value| value > 0 } # => {:foo=>0} + # + # If no block given, returns a new Enumerator: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.delete_if # => #0, :bar=>1, :baz=>2}:delete_if> + # e.each { |key, value| value > 0 } # => {:foo=>0} + # + def delete_if: () { (K, V) -> boolish } -> self + | () -> ::Enumerator[[ K, V ], self] + + # + # Finds and returns the object in nested objects that is specified by `key` and + # `identifiers`. The nested objects may be instances of various classes. See + # [Dig Methods](rdoc-ref:dig_methods.rdoc). + # + # Nested Hashes: + # h = {foo: {bar: {baz: 2}}} + # h.dig(:foo) # => {:bar=>{:baz=>2}} + # h.dig(:foo, :bar) # => {:baz=>2} + # h.dig(:foo, :bar, :baz) # => 2 + # h.dig(:foo, :bar, :BAZ) # => nil + # + # Nested Hashes and Arrays: + # h = {foo: {bar: [:a, :b, :c]}} + # h.dig(:foo, :bar, 2) # => :c + # + # This method will use the [default values](rdoc-ref:Hash@Default+Values) for + # keys that are not present: + # h = {foo: {bar: [:a, :b, :c]}} + # h.dig(:hello) # => nil + # h.default_proc = -> (hash, _key) { hash } + # h.dig(:hello, :world) # => h + # h.dig(:hello, :world, :foo, :bar, 2) # => :c + # + def dig: (K, *untyped) -> untyped + + # + # Hash#each is an alias for Hash#each_pair. + # + # Calls the given block with each key-value pair; returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo: 0 + # bar: 1 + # baz: 2 + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.each_pair # => #0, :bar=>1, :baz=>2}:each_pair> + # h1 = e.each {|key, value| puts "#{key}: #{value}"} + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo: 0 + # bar: 1 + # baz: 2 + # + def each: () { ([ K, V ] arg0) -> untyped } -> self + | () -> ::Enumerator[[ K, V ], self] + + # + # Calls the given block with each key; returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.each_key {|key| puts key } # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo + # bar + # baz + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.each_key # => #0, :bar=>1, :baz=>2}:each_key> + # h1 = e.each {|key| puts key } + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo + # bar + # baz + # + def each_key: () { (K arg0) -> untyped } -> ::Hash[K, V] + | () -> ::Enumerator[K, self] + + # + # Hash#each is an alias for Hash#each_pair. + # + # Calls the given block with each key-value pair; returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo: 0 + # bar: 1 + # baz: 2 + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.each_pair # => #0, :bar=>1, :baz=>2}:each_pair> + # h1 = e.each {|key, value| puts "#{key}: #{value}"} + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # foo: 0 + # bar: 1 + # baz: 2 + # + alias each_pair each + + # + # Calls the given block with each value; returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.each_value {|value| puts value } # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # 0 + # 1 + # 2 + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.each_value # => #0, :bar=>1, :baz=>2}:each_value> + # h1 = e.each {|value| puts value } + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + # Output: + # 0 + # 1 + # 2 + # + def each_value: () { (V arg0) -> untyped } -> self + | () -> ::Enumerator[V, self] + + # + # Returns `true` if there are no hash entries, `false` otherwise: + # {}.empty? # => true + # {foo: 0, bar: 1, baz: 2}.empty? # => false + # + def empty?: () -> bool + + # + # Returns `true` if all of the following are true: + # * `object` is a Hash object. + # * `hash` and `object` have the same keys (regardless of order). + # * For each key `key`, `h[key] eql? object[key]`. + # + # + # Otherwise, returns `false`. + # + # Equal: + # h1 = {foo: 0, bar: 1, baz: 2} + # h2 = {foo: 0, bar: 1, baz: 2} + # h1.eql? h2 # => true + # h3 = {baz: 2, bar: 1, foo: 0} + # h1.eql? h3 # => true + # + def eql?: (untyped) -> bool + + # + # Returns a new Hash excluding entries for the given `keys`: + # h = { a: 100, b: 200, c: 300 } + # h.except(:a) #=> {:b=>200, :c=>300} + # + # Any given `keys` that are not found are ignored. + # + def except: (*K) -> ::Hash[K, V] + + # + # Returns the value for the given `key`, if found. + # h = {foo: 0, bar: 1, baz: 2} + # h.fetch(:bar) # => 1 + # + # If `key` is not found and no block was given, returns `default_value`: + # {}.fetch(:nosuch, :default) # => :default + # + # If `key` is not found and a block was given, yields `key` to the block and + # returns the block's return value: + # {}.fetch(:nosuch) {|key| "No key #{key}"} # => "No key nosuch" + # + # Raises KeyError if neither `default_value` nor a block was given. + # + # Note that this method does not use the values of either #default or + # #default_proc. + # + def fetch: (K arg0) -> V + | [X] (K arg0, X arg1) -> (V | X) + | [X] (K arg0) { (K arg0) -> X } -> (V | X) + + # + # Returns a new Array containing the values associated with the given keys + # *keys: + # h = {foo: 0, bar: 1, baz: 2} + # h.fetch_values(:baz, :foo) # => [2, 0] + # + # Returns a new empty Array if no arguments given. + # + # When a block is given, calls the block with each missing key, treating the + # block's return value as the value for that key: + # h = {foo: 0, bar: 1, baz: 2} + # values = h.fetch_values(:bar, :foo, :bad, :bam) {|key| key.to_s} + # values # => [1, 0, "bad", "bam"] + # + # When no block is given, raises an exception if any given key is not found. + # + def fetch_values: (*K) -> ::Array[V] + | [X] (*K) { (K) -> X } -> ::Array[V | X] + + # + # Hash#filter is an alias for Hash#select. + # + # Returns a new Hash object whose entries are those for which the block returns + # a truthy value: + # h = {foo: 0, bar: 1, baz: 2} + # h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.select # => #0, :bar=>1, :baz=>2}:select> + # e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + def filter: () { (K, V) -> boolish } -> ::Hash[K, V] + | () -> ::Enumerator[[ K, V ], ::Hash[K, V]] + + # + # Hash#filter! is an alias for Hash#select!. + # + # Returns `self`, whose entries are those for which the block returns a truthy + # value: + # h = {foo: 0, bar: 1, baz: 2} + # h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} + # + # Returns `nil` if no entries were removed. + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.select! # => #0, :bar=>1, :baz=>2}:select!> + # e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + def filter!: () { (K, V) -> boolish } -> self? + | () -> ::Enumerator[[ K, V ], self?] + + # + # Returns a new Array object that is a 1-dimensional flattening of `self`. + # + # --- + # + # By default, nested Arrays are not flattened: + # h = {foo: 0, bar: [:bat, 3], baz: 2} + # h.flatten # => [:foo, 0, :bar, [:bat, 3], :baz, 2] + # + # Takes the depth of recursive flattening from Integer argument `level`: + # h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]} + # h.flatten(1) # => [:foo, 0, :bar, [:bat, [:baz, [:bat]]]] + # h.flatten(2) # => [:foo, 0, :bar, :bat, [:baz, [:bat]]] + # h.flatten(3) # => [:foo, 0, :bar, :bat, :baz, [:bat]] + # h.flatten(4) # => [:foo, 0, :bar, :bat, :baz, :bat] + # + # When `level` is negative, flattens all nested Arrays: + # h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]} + # h.flatten(-1) # => [:foo, 0, :bar, :bat, :baz, :bat] + # h.flatten(-2) # => [:foo, 0, :bar, :bat, :baz, :bat] + # + # When `level` is zero, returns the equivalent of #to_a : + # h = {foo: 0, bar: [:bat, 3], baz: 2} + # h.flatten(0) # => [[:foo, 0], [:bar, [:bat, 3]], [:baz, 2]] + # h.flatten(0) == h.to_a # => true + # + def flatten: () -> ::Array[K | V] + | (1 level) -> ::Array[K | V] + | (Integer level) -> Array[untyped] + + # + # Methods #has_key?, #key?, and #member? are aliases for #include?. + # + # Returns `true` if `key` is a key in `self`, otherwise `false`. + # + def has_key?: (K arg0) -> bool + + # + # Method #value? is an alias for #has_value?. + # + # Returns `true` if `value` is a value in `self`, otherwise `false`. + # + def has_value?: (V arg0) -> bool + + # + # Returns the Integer hash-code for the hash. + # + # Two Hash objects have the same hash-code if their content is the same + # (regardless or order): + # h1 = {foo: 0, bar: 1, baz: 2} + # h2 = {baz: 2, bar: 1, foo: 0} + # h2.hash == h1.hash # => true + # h2.eql? h1 # => true + # + def hash: () -> Integer + + # + # Methods #has_key?, #key?, and #member? are aliases for #include?. + # + # Returns `true` if `key` is a key in `self`, otherwise `false`. + # + alias include? has_key? + + # + # Returns a new String containing the hash entries: + # h = {foo: 0, bar: 1, baz: 2} + # h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}" + # + # Hash#to_s is an alias for Hash#inspect. + # + def inspect: () -> String + + # + # Returns a new Hash object with the each key-value pair inverted: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.invert + # h1 # => {0=>:foo, 1=>:bar, 2=>:baz} + # + # Overwrites any repeated new keys: (see [Entry + # Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 0, baz: 0} + # h.invert # => {0=>:baz} + # + def invert: () -> ::Hash[V, K] + + # + # Calls the block for each key-value pair; retains the entry if the block + # returns a truthy value; otherwise deletes the entry; returns `self`. + # h = {foo: 0, bar: 1, baz: 2} + # h.keep_if { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.keep_if # => #0, :bar=>1, :baz=>2}:keep_if> + # e.each { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2} + # + def keep_if: () { (K, V) -> boolish } -> self + | () -> ::Enumerator[[ K, V ], self] - # - # Replaces the entire contents of `self` with the contents of `other_hash`; - # returns `self`: - # h = {foo: 0, bar: 1, baz: 2} - # h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4} - # - def initialize_copy: (self object) -> self -end + # + # Returns the key for the first-found entry with the given `value` (see [Entry + # Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 2, baz: 2} + # h.key(0) # => :foo + # h.key(2) # => :bar + # + # Returns `nil` if so such value is found. + # + def key: (V) -> K? + + # + # Methods #has_key?, #key?, and #member? are aliases for #include?. + # + # Returns `true` if `key` is a key in `self`, otherwise `false`. + # + alias key? has_key? + + # + # Returns a new Array containing all keys in `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.keys # => [:foo, :bar, :baz] + # + def keys: () -> ::Array[K] + + # + # Returns the count of entries in `self`: + # {foo: 0, bar: 1, baz: 2}.length # => 3 + # + # Hash#length is an alias for Hash#size. + # + def length: () -> Integer + + # + # Methods #has_key?, #key?, and #member? are aliases for #include?. + # + # Returns `true` if `key` is a key in `self`, otherwise `false`. + # + alias member? has_key? + + # + # Returns the new Hash formed by merging each of `other_hashes` into a copy of + # `self`. + # + # Each argument in `other_hashes` must be a Hash. + # + # --- + # + # With arguments and no block: + # * Returns the new Hash object formed by merging each successive Hash in + # `other_hashes` into `self`. + # * Each new-key entry is added at the end. + # * Each duplicate-key entry's value overwrites the previous value. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h.merge(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # + # With arguments and a block: + # * Returns a new Hash object that is the merge of `self` and each given hash. + # * The given hashes are merged left to right. + # * Each new-key entry is added at the end. + # * For each duplicate key: + # * Calls the block with the key and the old and new values. + # * The block's return value becomes the new value for the entry. + # + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h3 = h.merge(h1, h2) { |key, old_value, new_value| old_value + new_value } + # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # + # With no arguments: + # * Returns a copy of `self`. + # * The block, if given, is ignored. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h.merge # => {:foo=>0, :bar=>1, :baz=>2} + # h1 = h.merge { |key, old_value, new_value| raise 'Cannot happen' } + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + def merge: [A, B] (*::Hash[A, B] other_hashes) -> ::Hash[A | K, B | V] + | [A, B, C] (*::Hash[A, B] other_hashes) { (K key, V oldval, B newval) -> C } -> ::Hash[A | K, B | V | C] + + # + # Merges each of `other_hashes` into `self`; returns `self`. + # + # Each argument in `other_hashes` must be a Hash. + # + # Method #update is an alias for #merge!. + # + # With arguments and no block: + # * Returns `self`, after the given hashes are merged into it. + # * The given hashes are merged left to right. + # * Each new entry is added at the end. + # * Each duplicate-key entry's value overwrites the previous value. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # + # With arguments and a block: + # * Returns `self`, after the given hashes are merged. + # * The given hashes are merged left to right. + # * Each new-key entry is added at the end. + # * For each duplicate key: + # * Calls the block with the key and the old and new values. + # * The block's return value becomes the new value for the entry. + # + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value } + # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # + # With no arguments: + # * Returns `self`, unmodified. + # * The block, if given, is ignored. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h.merge # => {:foo=>0, :bar=>1, :baz=>2} + # h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' } + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + def merge!: (*::Hash[K, V] other_hashes) -> self + | (*::Hash[K, V] other_hashes) { (K key, V oldval, V newval) -> V } -> self + + # + # Returns a new 2-element Array consisting of the key and value of the + # first-found entry whose value is `==` to value (see [Entry + # Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 1, baz: 1} + # h.rassoc(1) # => [:bar, 1] + # + # Returns `nil` if no such value found. + # + def rassoc: (V) -> [ K, V ]? + + # + # Rebuilds the hash table by recomputing the hash index for each key; returns + # `self`. + # + # The hash table becomes invalid if the hash value of a key has changed after + # the entry was created. See [Modifying an Active Hash + # Key](rdoc-ref:Hash@Modifying+an+Active+Hash+Key). + # + def rehash: () -> self + + # + # Returns a new Hash object whose entries are all those from `self` for which + # the block returns `false` or `nil`: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.reject {|key, value| key.start_with?('b') } + # h1 # => {:foo=>0} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.reject # => #0, :bar=>1, :baz=>2}:reject> + # h1 = e.each {|key, value| key.start_with?('b') } + # h1 # => {:foo=>0} + # + def reject: () -> ::Enumerator[[ K, V ], ::Hash[K, V]] + | () { (K, V) -> boolish } -> ::Hash[K, V] + + # + # Returns `self`, whose remaining entries are those for which the block returns + # `false` or `nil`: + # h = {foo: 0, bar: 1, baz: 2} + # h.reject! {|key, value| value < 2 } # => {:baz=>2} + # + # Returns `nil` if no entries are removed. + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.reject! # => #0, :bar=>1, :baz=>2}:reject!> + # e.each {|key, value| key.start_with?('b') } # => {:foo=>0} + # + def reject!: () -> ::Enumerator[[ K, V ], self?] + | () { (K, V) -> boolish } -> self? + + # + # Replaces the entire contents of `self` with the contents of `other_hash`; + # returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4} + # + def replace: (Hash[K, V]) -> self + + # + # Hash#filter is an alias for Hash#select. + # + # Returns a new Hash object whose entries are those for which the block returns + # a truthy value: + # h = {foo: 0, bar: 1, baz: 2} + # h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.select # => #0, :bar=>1, :baz=>2}:select> + # e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + alias select filter + + # + # Hash#filter! is an alias for Hash#select!. + # + # Returns `self`, whose entries are those for which the block returns a truthy + # value: + # h = {foo: 0, bar: 1, baz: 2} + # h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} + # + # Returns `nil` if no entries were removed. + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.select! # => #0, :bar=>1, :baz=>2}:select!> + # e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1} + # + alias select! filter! + + # + # Removes the first hash entry (see [Entry Order](rdoc-ref:Hash@Entry+Order)); + # returns a 2-element Array containing the removed key and value: + # h = {foo: 0, bar: 1, baz: 2} + # h.shift # => [:foo, 0] + # h # => {:bar=>1, :baz=>2} + # + # Returns nil if the hash is empty. + # + def shift: () -> [ K, V ]? + + # + # Returns the count of entries in `self`: + # {foo: 0, bar: 1, baz: 2}.length # => 3 + # + # Hash#length is an alias for Hash#size. + # + alias size length + + # + # Returns a new Hash object containing the entries for the given `keys`: + # h = {foo: 0, bar: 1, baz: 2} + # h.slice(:baz, :foo) # => {:baz=>2, :foo=>0} + # + # Any given `keys` that are not found are ignored. + # + def slice: (*K) -> ::Hash[K, V] + + # + # Hash#store is an alias for Hash#[]=. + # + # Associates the given `value` with the given `key`; returns `value`. + # + # If the given `key` exists, replaces its value with the given `value`; the + # ordering is not affected (see [Entry Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 1} + # h[:foo] = 2 # => 2 + # h.store(:bar, 3) # => 3 + # h # => {:foo=>2, :bar=>3} + # + # If `key` does not exist, adds the `key` and `value`; the new entry is last in + # the order (see [Entry Order](rdoc-ref:Hash@Entry+Order)): + # h = {foo: 0, bar: 1} + # h[:baz] = 2 # => 2 + # h.store(:bat, 3) # => 3 + # h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} + # + alias store []= + + # + # Returns a new Array of 2-element Array objects; each nested Array contains a + # key-value pair from `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.to_a # => [[:foo, 0], [:bar, 1], [:baz, 2]] + # + def to_a: () -> ::Array[[ K, V ]] + + # + # For an instance of Hash, returns `self`. + # + # For a subclass of Hash, returns a new Hash containing the content of `self`. + # + # When a block is given, returns a new Hash object whose content is based on the + # block; the block should return a 2-element Array object specifying the + # key-value pair to be included in the returned Array: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.to_h {|key, value| [value, key] } + # h1 # => {0=>:foo, 1=>:bar, 2=>:baz} + # + def to_h: () -> Hash[K, V] + | [A, B] () { (K, V) -> [ A, B ] } -> Hash[A, B] + + # + # Returns `self`. + # + def to_hash: () -> self + + # + # Returns a Proc object that maps a key to its value: + # h = {foo: 0, bar: 1, baz: 2} + # proc = h.to_proc + # proc.class # => Proc + # proc.call(:foo) # => 0 + # proc.call(:bar) # => 1 + # proc.call(:nosuch) # => nil + # + def to_proc: () -> ^(K) -> V? + + # + # Returns a new String containing the hash entries: + # h = {foo: 0, bar: 1, baz: 2} + # h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}" + # + # Hash#to_s is an alias for Hash#inspect. + # + alias to_s inspect + + # + # Returns a new Hash object; each entry has: + # * A key provided by the block. + # * The value from `self`. + # + # + # An optional hash argument can be provided to map keys to new keys. Any key not + # given will be mapped using the provided block, or remain the same if no block + # is given. + # + # Transform keys: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.transform_keys {|key| key.to_s } + # h1 # => {"foo"=>0, "bar"=>1, "baz"=>2} + # + # h.transform_keys(foo: :bar, bar: :foo) + # #=> {bar: 0, foo: 1, baz: 2} + # + # h.transform_keys(foo: :hello, &:to_s) + # #=> {:hello=>0, "bar"=>1, "baz"=>2} + # + # Overwrites values for duplicate keys: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.transform_keys {|key| :bat } + # h1 # => {:bat=>2} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.transform_keys # => #0, :bar=>1, :baz=>2}:transform_keys> + # h1 = e.each { |key| key.to_s } + # h1 # => {"foo"=>0, "bar"=>1, "baz"=>2} + # + def transform_keys: () -> Enumerator[K, Hash[untyped, V]] + | [A] () { (K) -> A } -> Hash[A, V] + + # + # Same as Hash#transform_keys but modifies the receiver in place instead of + # returning a new hash. + # + def transform_keys!: () -> Enumerator[K, self] + | () { (K) -> K } -> self + + # + # Returns a new Hash object; each entry has: + # * A key from `self`. + # * A value provided by the block. + # + # + # Transform values: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = h.transform_values {|value| value * 100} + # h1 # => {:foo=>0, :bar=>100, :baz=>200} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.transform_values # => #0, :bar=>1, :baz=>2}:transform_values> + # h1 = e.each { |value| value * 100} + # h1 # => {:foo=>0, :bar=>100, :baz=>200} + # + def transform_values: () -> Enumerator[V, Hash[K, untyped]] + | [A] () { (V) -> A } -> Hash[K, A] + + # + # Returns `self`, whose keys are unchanged, and whose values are determined by + # the given block. + # h = {foo: 0, bar: 1, baz: 2} + # h.transform_values! {|value| value * 100} # => {:foo=>0, :bar=>100, :baz=>200} + # + # Returns a new Enumerator if no block given: + # h = {foo: 0, bar: 1, baz: 2} + # e = h.transform_values! # => #0, :bar=>100, :baz=>200}:transform_values!> + # h1 = e.each {|value| value * 100} + # h1 # => {:foo=>0, :bar=>100, :baz=>200} + # + def transform_values!: () -> Enumerator[V, self] + | () { (V) -> V } -> self + + # + # Merges each of `other_hashes` into `self`; returns `self`. + # + # Each argument in `other_hashes` must be a Hash. + # + # Method #update is an alias for #merge!. + # + # With arguments and no block: + # * Returns `self`, after the given hashes are merged into it. + # * The given hashes are merged left to right. + # * Each new entry is added at the end. + # * Each duplicate-key entry's value overwrites the previous value. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5} + # + # With arguments and a block: + # * Returns `self`, after the given hashes are merged. + # * The given hashes are merged left to right. + # * Each new-key entry is added at the end. + # * For each duplicate key: + # * Calls the block with the key and the old and new values. + # * The block's return value becomes the new value for the entry. + # + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h1 = {bat: 3, bar: 4} + # h2 = {bam: 5, bat:6} + # h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value } + # h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5} + # + # With no arguments: + # * Returns `self`, unmodified. + # * The block, if given, is ignored. + # + # + # Example: + # h = {foo: 0, bar: 1, baz: 2} + # h.merge # => {:foo=>0, :bar=>1, :baz=>2} + # h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' } + # h1 # => {:foo=>0, :bar=>1, :baz=>2} + # + alias update merge! + + # + # Method #value? is an alias for #has_value?. + # + # Returns `true` if `value` is a value in `self`, otherwise `false`. + # + alias value? has_value? + + # + # Returns a new Array containing all values in `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.values # => [0, 1, 2] + # + def values: () -> ::Array[V] + + # + # Returns a new Array containing values for the given `keys`: + # h = {foo: 0, bar: 1, baz: 2} + # h.values_at(:baz, :foo) # => [2, 0] + # + # The [default values](rdoc-ref:Hash@Default+Values) are returned for any keys + # that are not found: + # h.values_at(:hello, :foo) # => [nil, 0] + # + def values_at: (*K arg0) -> ::Array[V?] + + private + + # + # Returns a new empty Hash object. + # + # The initial default value and initial default proc for the new hash depend on + # which form above was used. See [Default Values](rdoc-ref:Hash@Default+Values). + # + # If neither an argument nor a block given, initializes both the default value + # and the default proc to `nil`: + # h = Hash.new + # h.default # => nil + # h.default_proc # => nil + # + # If argument `default_value` given but no block given, initializes the default + # value to the given `default_value` and the default proc to `nil`: + # h = Hash.new(false) + # h.default # => false + # h.default_proc # => nil + # + # If a block given but no argument, stores the block as the default proc and + # sets the default value to `nil`: + # h = Hash.new {|hash, key| "Default value for #{key}" } + # h.default # => nil + # h.default_proc.class # => Proc + # h[:nosuch] # => "Default value for nosuch" + # + def initialize: () -> void + | (untyped default) -> void + | [A, B] () { (Hash[A, B] hash, A key) -> B } -> void + + # + # Replaces the entire contents of `self` with the contents of `other_hash`; + # returns `self`: + # h = {foo: 0, bar: 1, baz: 2} + # h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4} + # + def initialize_copy: (self object) -> self + end diff --git a/core/struct.rbs b/core/struct.rbs index 5d6f391426..4faac4852d 100644 --- a/core/struct.rbs +++ b/core/struct.rbs @@ -46,12 +46,10 @@ # * Includes [module Enumerable](rdoc-ref:Enumerable@What-27s+Here), which # provides dozens of additional methods. # -# # See also Data, which is a somewhat similar, but stricter concept for defining # immutable value objects. # # Here, class Struct provides methods that are useful for: -# # * [Creating a Struct # Subclass](rdoc-ref:Struct@Methods+for+Creating+a+Struct+Subclass) # * [Querying](rdoc-ref:Struct@Methods+for+Querying) @@ -61,25 +59,23 @@ # * [Iterating](rdoc-ref:Struct@Methods+for+Iterating) # * [Converting](rdoc-ref:Struct@Methods+for+Converting) # -# # ### Methods for Creating a Struct Subclass # -# * ::new: Returns a new subclass of Struct. -# +# * ::new: Returns a new subclass of Struct.# # # ### Methods for Querying # # * #hash: Returns the integer hash code. -# * #length, #size: Returns the number of members. -# +# * #length, #size: Returns the number of members.# # # ### Methods for Comparing # -# * #==: Returns whether a given object is equal to `self`, using `==` to -# compare member values. -# * #eql?: Returns whether a given object is equal to `self`, using `eql?` to -# compare member values. -# +# [#==](#method-i-3D-3D) +# : Returns whether a given object is equal to `self`, using `==` to compare +# member values. +# #eql? +# : Returns whether a given object is equal to `self`, using `eql?` to compare +# member values.# # # ### Methods for Fetching # @@ -103,19 +99,24 @@ # # ### Methods for Iterating # -# * #each: Calls a given block with each member name. -# * #each_pair: Calls a given block with each member name/value pair. +# * #inspect, #to_s: Returns a string representation of `self`. +# * #to_h: Returns a hash of the member name/value pairs in `self`. # # # ### Methods for Converting # -# * #inspect, #to_s: Returns a string representation of `self`. -# * #to_h: Returns a hash of the member name/value pairs in `self`. +# #inspect, #to_s +# : Returns a string representation of `self`. +# #to_h +# : Returns a hash of the member name/value pairs in `self`. # -class Struct[Elem] < Object - include Enumerable[Elem?] +class Struct[Elem] + include Enumerable[Elem] - type attribute_name = interned + # The types that can be used when "indexing" into a `Struct` via `[]`, `[]=`, `dig`, and + # `deconstruct_keys`. + # + type index = String | Symbol | int # + # Returns the member names of the Struct descendant as an array: + # + # Customer = Struct.new(:name, :address, :zip) + # Customer.members # => [:name, :address, :zip] + # + def self.members: () -> Array[Symbol] + + # + # Returns `true` if the class was initialized with `keyword_init: true`. + # Otherwise returns `nil` or `false`. + # + # Examples: + # Foo = Struct.new(:a) + # Foo.keyword_init? # => nil + # Bar = Struct.new(:a, keyword_init: true) + # Bar.keyword_init? # => true + # Baz = Struct.new(:a, keyword_init: false) + # Baz.keyword_init? # => false + # + def self.keyword_init?: () -> bool? + + # + # Returns `true` if and only if the following are true; otherwise returns + # `false`: + # + # * `other.class == self.class`. + # * For each member name `name`, `other.name == self.name`. + # + # + # Examples: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe_jr == joe # => true + # joe_jr[:name] = 'Joe Smith, Jr.' + # # => "Joe Smith, Jr." + # joe_jr == joe # => false + # + def ==: (untyped other) -> bool + + # + # Returns `true` if and only if the following are true; otherwise returns + # `false`: + # + # * `other.class == self.class`. + # * For each member name `name`, `other.name.eql?(self.name)`. + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe_jr.eql?(joe) # => true + # joe_jr[:name] = 'Joe Smith, Jr.' + # joe_jr.eql?(joe) # => false + # + # + # Related: Object#==. + # + def eql?: (untyped other) -> bool + + # + # Returns the integer hash value for `self`. + # + # Two structs of the same class and with the same content will have the same + # hash code (and will compare using Struct#eql?): + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.hash == joe_jr.hash # => true + # joe_jr[:name] = 'Joe Smith, Jr.' + # joe.hash == joe_jr.hash # => false + # + # Related: Object#hash. + # + def hash: () -> Integer + + # + # Returns a string representation of `self`: + # + # Customer = Struct.new(:name, :address, :zip) # => Customer + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.inspect # => "#" + # + # Struct#to_s is an alias for Struct#inspect. + # + def inspect: () -> String + + # + # Returns a string representation of `self`: + # + # Customer = Struct.new(:name, :address, :zip) # => Customer + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.inspect # => "#" + # + # Struct#to_s is an alias for Struct#inspect. + # + alias to_s inspect + + # + # Returns the values in `self` as an array: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345] + # + # Struct#values and Struct#deconstruct are aliases for Struct#to_a. + # + # Related: #members. + # + def to_a: () -> Array[Elem] + + # + # Returns a hash containing the name and value for each member: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # h = joe.to_h + # h # => {:name=>"Joe Smith", :address=>"123 Maple, Anytown NC", :zip=>12345} + # + # If a block is given, it is called with each name/value pair; the block should + # return a 2-element array whose elements will become a key/value pair in the + # returned hash: + # + # h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]} + # h # => {:NAME=>"JOE SMITH", :ADDRESS=>"123 MAPLE, ANYTOWN NC", :ZIP=>"12345"} + # + # Raises ArgumentError if the block returns an inappropriate value. + # + def to_h: () -> Hash[Symbol, Elem] + | [K, V] () { (Symbol key, Elem value) -> [K, V] } -> Hash[K, V] + + # + # Returns the values in `self` as an array: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345] + # + # Struct#values and Struct#deconstruct are aliases for Struct#to_a. + # + # Related: #members. + # + alias values to_a + + # + # Returns the number of members. + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.size #=> 3 + # + # Struct#length is an alias for Struct#size. + # + def size: () -> Integer + + # + # Returns the number of members. + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.size #=> 3 + # + # Struct#length is an alias for Struct#size. + # + alias length size # - # Returns the member names of the Struct descendant as an array: + # Calls the given block with each member name/value pair; returns `self`: + # + # Customer = Struct.new(:name, :address, :zip) # => Customer + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.each_pair {|(name, value)| p "#{name} => #{value}" } + # + # Output: + # + # "name => Joe Smith" + # "address => 123 Maple, Anytown NC" + # "zip => 12345" + # + # Returns an Enumerator if no block is given. + # + # Related: #each. + # + def each_pair: () -> Enumerator[[Symbol, Elem], self] + | () { ([Symbol, Elem] key_value) -> void } -> self + + # + # Returns a value from `self`. + # + # With symbol or string argument `name` given, returns the value for the named + # member: # # Customer = Struct.new(:name, :address, :zip) - # Customer.members # => [:name, :address, :zip] + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe[:zip] # => 12345 + # + # Raises NameError if `name` is not the name of a member. + # + # With integer argument `n` given, returns `self.values[n]` if `n` is in range; + # see Array@Array+Indexes: # - def self.members: () -> ::Array[Symbol] + # joe[2] # => 12345 + # joe[-2] # => "123 Maple, Anytown NC" + # + # Raises IndexError if `n` is out of range. + # + def []: (index name_or_position) -> Elem # - # Returns `true` if the class was initialized with `keyword_init: true`. - # Otherwise returns `nil` or `false`. + # Assigns a value to a member. + # + # With symbol or string argument `name` given, assigns the given `value` to the + # named member; returns `value`: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe[:zip] = 54321 # => 54321 + # joe # => # + # + # Raises NameError if `name` is not the name of a member. + # + # With integer argument `n` given, assigns the given `value` to the `n`-th + # member if `n` is in range; see Array@Array+Indexes: + # + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe[2] = 54321 # => 54321 + # joe[-3] = 'Joseph Smith' # => "Joseph Smith" + # joe # => # + # + # Raises IndexError if `n` is out of range. + # + def []=: (index name_or_position, Elem value) -> Elem + + # + # With a block given, returns an array of values from `self` for which the block + # returns a truthy value: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # a = joe.select {|value| value.is_a?(String) } + # a # => ["Joe Smith", "123 Maple, Anytown NC"] + # a = joe.select {|value| value.is_a?(Integer) } + # a # => [12345] + # + # With no block given, returns an Enumerator. + # + # Struct#filter is an alias for Struct#select. + # + def select: () -> Enumerator[Elem, Array[Elem]] + | () { (Elem value) -> boolish } -> Array[Elem] + + # + # With a block given, returns an array of values from `self` for which the block + # returns a truthy value: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # a = joe.select {|value| value.is_a?(String) } + # a # => ["Joe Smith", "123 Maple, Anytown NC"] + # a = joe.select {|value| value.is_a?(Integer) } + # a # => [12345] + # + # With no block given, returns an Enumerator. + # + # Struct#filter is an alias for Struct#select. + # + alias filter select + + # + # Returns an array of values from `self`. + # + # With integer arguments `integers` given, returns an array containing each + # value given by one of `integers`: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.values_at(0, 2) # => ["Joe Smith", 12345] + # joe.values_at(2, 0) # => [12345, "Joe Smith"] + # joe.values_at(2, 1, 0) # => [12345, "123 Maple, Anytown NC", "Joe Smith"] + # joe.values_at(0, -3) # => ["Joe Smith", "Joe Smith"] + # + # Raises IndexError if any of `integers` is out of range; see + # Array@Array+Indexes. + # + # With integer range argument `integer_range` given, returns an array containing + # each value given by the elements of the range; fills with `nil` values for + # range elements larger than the structure: + # + # joe.values_at(0..2) + # # => ["Joe Smith", "123 Maple, Anytown NC", 12345] + # joe.values_at(-3..-1) + # # => ["Joe Smith", "123 Maple, Anytown NC", 12345] + # joe.values_at(1..4) # => ["123 Maple, Anytown NC", 12345, nil, nil] + # + # Raises RangeError if any element of the range is negative and out of range; + # see Array@Array+Indexes. + # + def values_at: (*int | range[int?] positions) -> Array[Elem] + + # + # Returns the member names from `self` as an array: + # + # Customer = Struct.new(:name, :address, :zip) + # Customer.new.members # => [:name, :address, :zip] + # + # Related: #to_a. + # + def members: () -> Array[Symbol] + + # + # Finds and returns an object among nested objects. The nested objects may be + # instances of various classes. See [Dig Methods](rdoc-ref:dig_methods.rdoc). + # + # Given symbol or string argument `name`, returns the object that is specified + # by `name` and `identifiers`: # - # Examples: # Foo = Struct.new(:a) - # Foo.keyword_init? # => nil - # Bar = Struct.new(:a, keyword_init: true) - # Bar.keyword_init? # => true - # Baz = Struct.new(:a, keyword_init: false) - # Baz.keyword_init? # => false + # f = Foo.new(Foo.new({b: [1, 2, 3]})) + # f.dig(:a) # => #[1, 2, 3]}> + # f.dig(:a, :a) # => {:b=>[1, 2, 3]} + # f.dig(:a, :a, :b) # => [1, 2, 3] + # f.dig(:a, :a, :b, 0) # => 1 + # f.dig(:b, 0) # => nil + # + # Given integer argument `n`, returns the object that is specified by `n` and + # `identifiers`: + # + # f.dig(0) # => #[1, 2, 3]}> + # f.dig(0, 0) # => {:b=>[1, 2, 3]} + # f.dig(0, 0, :b) # => [1, 2, 3] + # f.dig(0, 0, :b, 0) # => 1 + # f.dig(:b, 0) # => nil + # + def dig: (index name_or_position) -> Elem + | (index name_or_position, untyped, *untyped) -> untyped + + # + # Returns the values in `self` as an array: + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345] + # + # Struct#values and Struct#deconstruct are aliases for Struct#to_a. + # + # Related: #members. + # + alias deconstruct to_a + + # + # Returns a hash of the name/value pairs for the given member names. + # + # Customer = Struct.new(:name, :address, :zip) + # joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) + # h = joe.deconstruct_keys([:zip, :address]) + # h # => {:zip=>12345, :address=>"123 Maple, Anytown NC"} + # + # Returns all names and values if `array_of_names` is `nil`: + # + # h = joe.deconstruct_keys(nil) + # h # => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345} # - def self.keyword_init?: () -> (true | false | nil) + def deconstruct_keys: (Array[index & Hash::_Key]? indices) -> Hash[index & Hash::_Key, Elem] end diff --git a/test/stdlib/Struct_test.rb b/test/stdlib/Struct_test.rb index 103bc0647f..3af71167c4 100644 --- a/test/stdlib/Struct_test.rb +++ b/test/stdlib/Struct_test.rb @@ -1,16 +1,219 @@ -require_relative 'test_helper' +require_relative "test_helper" -class StructTest < Test::Unit::TestCase +class StructSingletonTest < Test::Unit::TestCase include TypeAssertions - testing '::Struct[::Integer]' + testing "singleton(::Struct)" + + def test_new + # Since we're redefining `TestNewStruct` constantly, we need to set `$VERBOSE` to nil to suppress + # the redeclaration warnings. + old_verbose = $VERBOSE + $VERBOSE = nil + + with_string 'TestNewStruct' do |classname| + with_interned :field1 do |field1| + with_interned :field2 do |field2| + assert_send_type "(::string?, *::interned) -> singleton(Struct)", + Struct, :new, classname, field1, field2 + assert_send_type "(::string?, *::interned) { (self) -> void } -> singleton(Struct)", + Struct, :new, classname, field1, field2 do end + + if Symbol === field1 # can't use `.is_a?` since `ToStr` doesn't define it. + assert_send_type "(Symbol, *::interned) -> singleton(Struct)", + Struct, :new, field1, field2 + assert_send_type "(Symbol, *::interned) { (self) -> void } -> singleton(Struct)", + Struct, :new, field1, field2 do end + end + + ['yes', false, nil].each do |kwinit| + assert_send_type "(::string?, *::interned, keyword_init: ::boolish?) ?{ (self) -> void } -> singleton(Struct)", + Struct, :new, classname, field1, field2, keyword_init: kwinit + assert_send_type "(::string?, *::interned, keyword_init: ::boolish?) ?{ (self) -> void } -> singleton(Struct)", + Struct, :new, classname, field1, field2, keyword_init: kwinit do end + + if Symbol === field1 # can't use `.is_a?` since `ToStr` doesn't define it. + assert_send_type "(Symbol, *::interned, keyword_init: ::boolish?) -> singleton(Struct)", + Struct, :new, field1, field2, keyword_init: kwinit + assert_send_type "(Symbol, *::interned, keyword_init: ::boolish?) { (self) -> void } -> singleton(Struct)", + Struct, :new, field1, field2, keyword_init: kwinit do end + end + end + end + end + end + ensure + $VERBOSE = old_verbose + end + + def test_members + assert_send_type "() -> Array[Symbol]", + Struct.new(:foo, :bar), :members + end + + def test_keyword_init? + assert_send_type "() -> bool?", + Struct.new(:foo), :keyword_init? + assert_send_type "() -> bool?", + Struct.new(:foo, keyword_init: true), :keyword_init? + assert_send_type "() -> bool?", + Struct.new(:foo, keyword_init: false), :keyword_init? + assert_send_type "() -> bool?", + Struct.new(:foo, keyword_init: nil), :keyword_init? + end +end + +class StructInstanceTest < Test::Unit::TestCase + include TypeAssertions + + testing "::Struct[Rational]" MyStruct = Struct.new(:foo, :bar) + Instance = MyStruct.new(1r, 2r) + + + def with_index(int=1, str=:bar, &block) + block.call str.to_s + block.call str.to_sym + with_int(int, &block) + end + + def test_equal + assert_send_type "(untyped) -> bool", + Instance, :==, 3 + end + + def test_eql? + assert_send_type "(untyped) -> bool", + Instance, :eql?, 3 + end + + def test_hash + assert_send_type "() -> Integer", + Instance, :hash + end + + def test_inspect + assert_send_type "() -> String", + Instance, :inspect + end + + def test_to_s + assert_send_type "() -> String", + Instance, :to_s + end + + def test_to_a + assert_send_type "() -> Array[Rational]", + Instance, :to_a + end + + def test_to_h + assert_send_type "() -> Hash[Symbol, Rational]", + Instance, :to_h + assert_send_type "[K, V] () { (Symbol, Rational) -> [K, V] } -> Hash[K, V]", + Instance, :to_h do [1, 2] end + end + + def test_values + assert_send_type "() -> Array[Rational]", + Instance, :values + end + + def test_size + assert_send_type "() -> Integer", + Instance, :size + end + + def test_length + assert_send_type "() -> Integer", + Instance, :length + end def test_each - assert_send_type '() { (::Integer?) -> void } -> self', - MyStruct.new(42), :each do end - assert_send_type '() -> ::Enumerator[::Integer?, self]', - MyStruct.new(42), :each + assert_send_type "() -> Enumerator[Rational, self]", + Instance, :each + assert_send_type "() { (Rational) -> void } -> self", + Instance, :each do end + end + + def test_each_pair + assert_send_type "() -> Enumerator[[Symbol, Rational], self]", + Instance, :each_pair + assert_send_type "() { ([Symbol, Rational]) -> void } -> self", + Instance, :each_pair do end + end + + def test_aref + with_index do |idx| + assert_send_type "(Struct::index) -> Rational", + Instance, :[], idx + end + end + + def test_aset + with_index do |idx| + assert_send_type "(Struct::index, Rational) -> Rational", + Instance, :[]=, idx, 1r + end + end + + def test_select + assert_send_type "() -> Enumerator[Rational, Array[Rational]]", + Instance, :select + assert_send_type "() { (Rational) -> ::boolish } -> Array[Rational]", + Instance, :select do end + end + + + def test_filter + assert_send_type "() -> Enumerator[Rational, Array[Rational]]", + Instance, :filter + assert_send_type "() { (Rational) -> ::boolish } -> Array[Rational]", + Instance, :filter do end + end + + def test_values_at + assert_send_type "() -> Array[Rational]", + Instance, :values_at + + with_int 1 do |idx| + assert_send_type "(*::int | ::range[::int?]) -> Array[Rational]", + Instance, :values_at, idx, idx..nil + end + end + + def test_members + assert_send_type "() -> Array[Symbol]", + Instance, :members + end + + def test_dig + array_instance = MyStruct.new([1]) + with_index do |idx| + assert_send_type "(Struct::index) -> Rational", + Instance, :dig, idx + assert_send_type "(Struct::index, untyped, *untyped) -> untyped", + array_instance, :dig, idx, 1 + end + end + + def test_deconstruct + assert_send_type "() -> Array[Rational]", + Instance, :deconstruct + end + + def test_deconstruct_keys + assert_send_type "(nil) -> Hash[Symbol, Rational]", + Instance, :deconstruct_keys, nil + + with_index do |idx| + # Ensure that the `ToInt` variants have `hash` and `eql?` defined. + def idx.hash = 0 unless defined? idx.hash + def idx.eql?(r) = false unless defined? idx.eql? + + assert_send_type "(Array[Struct::index & Hash::_Key]) -> Hash[Struct::index & Hash::_Key, Rational]", + Instance, :deconstruct_keys, [idx] + end end end