Skip to content

Latest commit

 

History

History
415 lines (331 loc) · 11.9 KB

markdown.md

File metadata and controls

415 lines (331 loc) · 11.9 KB

About Hash

A Hash is a dictionary-like collection of key-value pairs, wherein the keys are unique. (The values need not be unique.)

A Hash has certain similarities to an Array, but:

  • An Array index is always an Integer.
  • A Hash key can be (almost) any object.

Contents

Hash Data Syntax

Until its version 1.9, Ruby supported only the "hash rocket" syntax for Hash data:

h = {:foo => 0, :bar => 1, :baz => 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

(The "hash rocket" is =>, sometimes in other languages called the "fat comma.")

Beginning with version 1.9, you can write a Hash key that's a Symbol in a 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 Strings instead of barewords:

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:

h = {0: 'zero'} # Raises SyntaxError (syntax error, unexpected ':', expecting =>)

Common Uses

Giving Names to Objects

You can use a Hash to give names to objects:

matz = {name: 'Matz', language: 'Ruby'}
matz # => {:name=>"Matz", :language=>"Ruby"}

Giving Names to Arguments

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}

Initializing Objects

You can use a Hash to initialize an object:

class Dev
  attr_accessor :name, :language
  def initialize(hash)
    hash.each_pair do |key, value|
      setter_method = "#{key}=".to_sym
      send(setter_method, value)
    end
  end
end
matz = Dev.new(name: 'Matz', language: 'Ruby')
matz # => #<Dev: @name="Matz", @language="Ruby">

Creating a Hash

Here are three ways to create a Hash:

  • Constructor method: Hash.new.
  • Literal method: Hash[].
  • Implicit Form: {}.

Constructor Hash.new

You can create a Hash by using the constructor method, Hash.new.

Create an empty Hash:

h = Hash.new
h # => {}
h.class # => Hash

Hash Literal

You can create a Hash by using its literal 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}

Hash Implicit Form

You can create a Hash by using its implicit 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

Getting a Hash Value

The simplest way to get a Hash value (instance method []):

h[:foo] # => 0

Setting a Hash Value

The simplest way to create or update a Hash value (instance method []=):

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}

Deleting a Hash Value

The simplest way to delete a Hash entry (instance method delete):

h.delete(:bat) # => 3
h # => {:foo=>4, :bar=>1, :baz=>2}

Chaining Method Calls

Many Hash instance methods return self. For those methods, you can chain method calls:

h = {foo: 0, bar: 1, baz: 2}
h.keep_if { |key, value| key.start_with?('b') }.size # => 2

Method Calls with Blocks

For some Hash methods, a call to the method can include a block:

There are several other families of methods that can call blocks:

General Iterators

Each of these general iterators traverses Hash entries, doing only what the block specifies, and returning self:

Specialized Iterators

Each of these iterators returns a new Hash, including or excluding entries based on whether the block returns a truthy value:

Each of these iterators returns self, retaining or deleting entries based on whether the block returns a truthy value:

Each of these iterators returns a new Hash with modified keys or values based on the block:

Each of these iterators returns self, modifying keys or values based on the block:

Missing-Key Handlers

Each of these methods can handle missing keys in a block:

Duplicate-Key Handlers

Each of these methods can handle duplicate keys in a block:

Methods Returning Enumerators

Each Hash method that allows a block for iteration returns an Enumerator if no block is given. These are:

Example using method hash.each_pair:

h = {foo: 0, bar: 1, baz: 2}
e = h.each_pair
e # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_pair>`

And perhaps later on:

h1 = e.each { |key, value| puts "#{key}: #{value}"}
h1 # => {:foo=>0, :bar=>1, :baz=>2}
h1.object_id == h.object_id # => true

The enumerator has a reference to the Hash, not a copy of it:

e # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_pair>`
h.delete(:baz) # => 2
e # => #<Enumerator: {:foo=>0, :bar=>1}:each_pair>

But making the original Hash unavailable causes the enumerator to make a copy:

h = nil
e # => #<Enumerator: {:foo=>0, :bar=>1}:each_pair>