What the heck is Ruby Hash ??
You can think of Ruby Hash is kind of an Array without the numerical indexes. You access the Hash values with Keys
. A Hash is a data structure used to store data in the form of UNIQUE key-value pairs.
A Hash has certain similarities to an Array, but:
β
An Array index is always an Integer
β
A Hash key can be (almost) any object
We are gonna dive π€Ώ into the Hash world in Ruby. But not too deep, just at the level where most people dive in :p.
note: I encourage you to open
irb
in your favourite terminal app. Make sure you get your hands π dirty.
TL;DR
- 1οΈβ£ Hash Data Syntax
- 2οΈβ£ Common use case of using Hash
- 3οΈβ£ How to Create a Hash
- 4οΈβ£ How to create or update a Hash value
- 5οΈβ£ Values in a Ruby Hash
- 6οΈβ£ Hash Entry Order
- 7οΈβ£ How to delete a Hash entry
- 8οΈβ£ How to Access Values From a Hash
- 9οΈβ£ Extract a nested Hash value
- π Hash Keys 101
- 1οΈβ£1οΈβ£ Default Values
- 1οΈβ£2οΈβ£ How to Merge Two Ruby Hashes
- 1οΈβ£3οΈβ£ Multiple Hash values for one key
- 1οΈβ£4οΈβ£ Get All Keys and Values From a Hash
- 1οΈβ£5οΈβ£ Check if key exists in hash
- 1οΈβ£6οΈβ£ How to change a Hash to Array
β Hash Data Syntax
Hash has three Syntax style as Ruby 3.0.
1) hash rocket =>
hashy = {:foo => 0, :bar => 1, :baz => 2}
hashy # => {:foo=>0, :bar=>1, :baz=>2}
2) JSON-style syntax
hashy = {foo: 0, bar: 1, baz: 2}
hashy # => {:foo=>0, :bar=>1, :baz=>2}
note: The Hash key become a
Symbol
note: You will get an error if you use the key that's not a bareword or a String
# Raises SyntaxError (syntax error, unexpected ':', expecting =>):
hashy = {0: 'zero'}
3) String
hashy = {'foo': 0, 'bar': 1, 'baz': 2}
hashy # => {:foo=>0, :bar=>1, :baz=>2}
note: same like number 2. But the key is change to the
String
, instead of symbol.
And you can mix the styles;
hashy = {foo: 0, :bar => 1, 'baz': 2}
hashy # => {:foo=>0, :bar=>1, :baz=>2}
β Common use case of using Hash
1) Give names to objects
book = {author: "Daniel J. Levitin", title: "This is Your Brain on Music}"
book # => {:author=>"Daniel J. Levitin", :title=>"This is Your Brain on Music"}
2) 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}
3) initialize an object
class Book
attr_accessor :author, :title
def initialize(hash)
self.author = hash[:author]
self.title = hash[:title]
end
end
book1 = Book.new(author: 'Daniel J. Levitin', title: "'This is Your Brain on Music')"
book1 # => #<Book: @author="Daniel J. Levitin", @title="This is Your Brain on Music">
4) Get the frequency from a list of numbers
numbers = [1, 1, 1, 2, 4, 65, 55, 54, 55]
freq_hash = numbers.each_with_object(Hash.new(0)) { |number, hash| hash[number] += 1 }
puts "#{freq_hash}"
# {1=>3, 2=>1, 4=>1, 65=>1, 55=>2, 54=>1}
Another tricks is using tally
method.
numbers = [1, 1, 1, 2, 4, 65, 55, 54, 55]
numbers.tally
# output
# {1=>3, 2=>1, 4=>1, 65=>1, 55=>2, 54=>1}
note: This is for Ruby 2.7+ only. :P
5) Specify routes
in Ruby on Rails
This is a line from the config/routes.rb
file, the Router in a Rails application:
# defines a GET route mapped to the new action in the PostsController
get '/posts/new', to: 'posts#new'
The example of the above could be rewritten as:
get('/posts/new', { to: 'posts#new' })
The Hash is the second parameter passed to the get(...)
method.
β How to Create a Hash
Here are three ways to create a Hash:
1) Method Hash.new
You can create a Hash by calling method Hash.new
h = Hash.new #Define empty hash
h # => {}
h.class # => Hash
h[:first] = 10
h[:second] = 20
h[:third] = 30
puts h # => {:first=>10, :second=>20, :third=>30}
2) Method 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}
3) Literal form {}
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}
β How to create or update a Hash value
We can use 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}
β Values in a Ruby Hash
Values can be any Ruby Object
. Including:
- Strings
- Integers & Floats
- Arrays
note Keys are unique, you can only have one
:foo
key, or one:bar
key. When you add the same key twice, the latter will override the former value.
β Hash Entry Order
A Hash object presents its entries in the order of their creation.
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}
β How to delete a Hash entry
The simplest way is using instance method delete
:
h = {foo: 0, bar: 1, baz: 2}
h.delete(:bar) # => 1
h # => {:foo=>0, :baz=>2}
β How to Access Values From a Hash
Hash value/element are access by particular key.
The simplest way to retrieve a Hash value is using instance method []
:
h = {foo: 0, bar: 1, baz: 2}
h[:foo] # => 0
We also can use fetch
method. It does same as the square bracket lookup []
, but it will raise an error
if the key is not defined:
$ irb
> dictionary = { "one" => "satu" } #Malay language π²πΎ
> dictionary.fetch("one")
=> "satu"
> dictionary.fetch("two")
KeyError: key not found: "two"
We can prevent this error using the default value.
β Extract a nested Hash value
dig
is handy for nested Hash
.
h = { foo: {bar: {baz: 11}}}
h.dig(:foo, :bar, :baz) # => 11
h.dig(:foo, :zot, :xyz) # => nil
g = { foo: [10, 11, 12] }
g.dig(:foo, 1) # => 11
note: This is only for Ruby 2.3+
β Hash Keys 101
Hash Key Equivalence
From Documentation: Two objects are treated as the same
hash
key when their hash value is identical and the two objects areeql?
to each other.
irb> g = {foo: 1}
=> {:foo=>1}
irb> h = {foo: 1}
=> {:foo=>1}
irb> g.eql?(h)
=> true
Modifying an Active Hash Key
Modifying a Hash key while it is in use damages the hash 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
β Default values
By default, accessing a key which has not been added to the hash returns nil
, meaning it is always safe to attempt to look up a key's value:
my_hash = {}
my_hash[:name] # => nil
Hashes can also contain keys in strings
. If you try to access them normally it will just return a nil
, instead you access them by their string keys:
my_hash = { "name" => "asyraf" }
my_hash[:name] # => nil
my_hash["name"] # => asyraf
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(100)
h.default # => 100
h.default = 99
h.default # => 99
β How to Merge Two Ruby Hashes
I think you can guess the method name :p. Tadaaaa, we will use merge
method.
defaults = { a: 1, b: 2, c: 3 }
preferences = { c: 4 }
defaults.merge!(preferences)
# {:a=>1, :b=>2, :c=>4}
Notice that because keys are unique, newer values overwrite older values.
β Multiple Hash values for one key
Malaysia = {
food: [
"Nasi Lemak",
"Roti Canai"
],
city: [
"Kuala Lumpur",
"Malacca City",
"George Town"
]
}
Malaysia[:city][1]
Where Malaysia[:city]
gives you an array & [1]
gives you the 2nd element from that array.
The key is a symbol & the values are arrays. When you access the hash, you get an array back which you access normally, like any other array.
β Get All Keys and Values From a Hash
If you want a list of all the keys, good news, there is a method for that!
Here it is:
{ foo: 1, bar: 2 }.keys
# [:foo, :bar]
Thereβs also a method for values:
{ foo: 1, bar: 2 }.values
# [1, 2]
β Check if key exists in hash
If you want to know if a key exists in a hash, use the key?
method.
hash_data = {'Country'=>"Malaysia",'Food'=>"Nasi Lemak",}
puts hash_data.key?("Country") #true
puts hash_data.key?("Code") #false
β How to change a Hash to Array
We can do Hash
π Array
. Converting a hash of key/value pairs into an array will produce an array containing nested arrays for pair:
{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]
In the opposite direction a Hash
can be created from an array
of the same format:
[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }
Similarly, Hashes can be initialized using Hash[]
and a list of alternating keys and values:
Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }
Or from an array of arrays with two values each:
Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }
Hashes can be converted back to an Array
of alternating keys and values using flatten()
:
{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]
Super cool
Pretty cool, hah... π
There are other cool stuff on Ruby Hash. But as i said, this is just a level of most of people dive in.
Maybe for specific tricks like Hash iteration, sorting and other methods for future articles :P.
And probably this is my notes about Ruby Hash, I will add new stuff as I get new knowledge or experience with it.
Thank you for traveling with me π, I hope you enjoyed the journey π₯.
The End
Resources:
Top comments (0)