- The Ruby Workshop
- Akshat Paul Peter Philips Dániel Szabó Cheyne Wallace
- 2027字
- 2021-06-11 13:04:38
Hashes
Hashes are collections that save data as key-value pairs. This means that a key will identify every value that we want to save. This also means that keys within a hash have to be unique, otherwise we will overwrite the old values.
In terms of performance, hashes have a lookup time of O(1), which means that finding a value in a hash by its key will not be dependent on the size of the hash, nor on the order in which the hash was created. O(1) means a constant lookup time for the operation performed on a hash, irrespective of the data size. This differs from arrays, where finding a value in an O(n) operation means that the time taken to find an element in an array will be dependent on the length of an array. This means that we are looking for an element and we are going to find it on the nth location; the n does not denote the size of the array, but the location of the element.
An empty hash can be created as follows:
my_hash = {}
my_hash = Hash.new
The output will be as follows:

Figure 2.36: Output for the Hash.new method
We can also create hashes that have a default value, as shown in the following code snippet:
my_hash = Hash.new("No Value")
my_hash["Non existent key"]
my_hash = Hash.new("test")
my_hash["No such key"]
The output with default values would be as follows:

Figure 2.37: Output for default values in hashes
We can also initialize hashes when creating them as well, as you will see in the following code:
my_hash = {"jan" => 1, "feb" => 2, "mar" => 3}
The initialized hash would now appear as follows:

Figure 2.38: Output for initialized hashes
We can also add values to the hash. When adding values to the hash, we can use two syntaxes. When the keys are symbols, values can be assigned to the hash as follows:
my_hash = {}
my_hash[:jan] = 1
my_hash[:feb] = 2
my_hash[:mar] = 3
my_hash
:jan is called a symbol, and it's very similar to a string; however, strings are used to store data, while symbols are used as identifiers in Ruby.
The updated hash will now appear as follows:

Figure 2.39: Output for the hash with assigned values
Note
Keys are preceded with a : and not with double quotes.
When the keys are strings, values are assigned to the hash as follows:
my_hash = {}
my_hash["jan"] = 1
my_hash["feb"] = 2
my_hash["mar"] = 3
my_hash
The output, when keys are strings, would be as follows:

Figure 2.40: Output for the hash when keys are strings
As you can see, we have used both symbols and strings as keys in the hash. While we can use symbols and strings in a single hash, it is not advisable as the person reading our code would not be able to guess what we are using, and this would always be a source of confusion when retrieving values.
The value in the key-value pair inside a hash can be any data type, such as an array of our custom object, or even an array of our custom class objects.
We can use whichever method we want, but while using the hash, we must stick to just one. If the key is a symbol and we try to retrieve it as a string, the hash will return nil. The following code will show you the same:
my_hash = {}
my_hash[:jan] = 1
my_hash[:feb] = 2
my_hash[:mar] = 3
my_hash[:mar]
my_hash[:mar.to_s]
my_hash[:jan.to_s]
The output will now appear as follows:

Figure 2.41: Output for the hash when the symbol is retrieved as a string
Note how referring to :mar gives us the correct value. However, when we convert the keys to the :mar.to_s string, it fails to find the appropriate value.
The most common way to iterate through a hash is to use the .each iterator, which gives access to both keys and values of a hash while iterating.
Consider the following example:
my_hash = {}
my_hash[:jan] = 1
my_hash[:feb] = 2
my_hash[:mar] = 3
my_hash.each do |key, value|
puts "#{key} => #{value}"
end
The output will be as follows:

Figure 2.42: Output using the .each iterator
In the preceding code, we have created a hash and defined the keys – :jan, :feb, and :mar, with values. We have used the .each iterator to iterate through the key-value pairs.
As you can see, the .each iterator has pulled up both the keys and values of the hash through the iteration.
Ruby is purely an object-oriented language, which means that basic data types such as hashes are also objects in Ruby. This can be realized by the following code snippet:
my_hash = {}
my_hash.class
The object type of hash is displayed as follows:

Figure 2.43: Object type of hash
As you can see, the hash belongs to the Hash class. This also means that we can figure out which methods the Hash class responds to as well, as shown in the following code:
my_hash = {}
my_hash.methods
The output will be as follows:

Figure 2.44: Output for methods on hashes
In Ruby, .methods will return all the method names on which the object can be called. The preceding figure has 147 methods, so we'll just dig deep into a few of the most common and important methods.
Soon, when we familiarize ourselves with functions and methods in Ruby that can be called on objects, we need to learn respond_to?, which will tell us what functionality is implemented or inherited in our instance of a specific class. In this case, we inspect the my_hash variable, whose functionality is supported as shown in the following code:
my_hash = {}
my_hash.respond_to?("length")
my_hash.respond_to?("garbage")
respond_to? will provide the following output:

Figure 2.45: Output for respond_to?
Just as for arrays, the respond_to? method will return the values for methods that are defined for hashes. As you can see in the preceding diagram, respond_to? returns true for the length method and false for the garbage method.
Operations on Hashes
In this section, we will perform various operations on hashes, just like we did for arrays, as mentioned in the following list:
- Getting values based on keys
- Setting values for keys
- Sorting
- Merging
- Deleting values of a key
- Removing or rejecting keys
- Searching for values or keys
Let's have a look at each of these in detail.
Getting Values from the Hash
It's the key that fetches the value from the hash. If the key does not exist, a nil value will be returned:
my_hash = {:jan => 1, :feb => 2}
my_hash[:feb]
my_hash[:march]
The output will be as follows:

Figure 2.46: Retrieving values from the hash
As you can see, the hash returns the value 2 for the :feb key, but false for the :march key as the latter is not a part of the hash.
Sorting a Hash
Sorting a hash is not as simple as it is in an array. You can sort a hash by value by using .sort_by. The result of .sort_by is an array, with each key-value pair as an array element in a sorted manner. The .sort_by function operates differently based on which object is called.
The following code will show you how you can iterate over the |key,value| pairs of a hash, and then sort based on the second argument, which will be age, which is the value for the given hash key:
my_hash = {:bill => 34, :steve => 66, :eric => 6}
my_hash.sort_by { |name, age| age }
my_hash.sort_by { |name, age| age }.reverse
The output will be as follows:

Figure 2.47: Output for sorting and reversing arrays
As can be seen from the output, you can infer that the keys have been sorted and arranged as defined by the key-value pair by using the .sort_by method. The same hash is now reversed using the .reverse method.
Sorting can even be done on a more complex hash. It can be sorted by what we provide as a parameter in the .sort_by method.
Our next hash is a nested hash, where each key holds a small hash. We will sort the hash based on the :age of the hashes located inside the keys:
my_hash = {:bill => {:name => "Bill", :age => 55}, :steve => {:name => "Steve", :age => 60}, :eric => {:name =>"Eric", :age => 50}}
my_hash.sort_by {|key, value| value[:age]}
The output will be as follows:

Figure 2.48: Sorting keys in a hash
Merging Hashes
Hashes can be merged together just like arrays, but there is something you need to be mindful of the order in which the keys are passed within the hash. To understand this, let's look at the following example:
my_hash_1 = {:a => 10, :b => 20}
my_hash_2 = {:c => 30, :d => 40}
my_hash_1.merge(my_hash_2)
The output will be as follows:

Figure 2.49: Output for merging hashes
In the preceding example, we merged two hashes with unique keys. However, when merging hashes with common keys, the latter will override the former.
As shown in the following example, the value in the resulting hash would be my_hash_2 instead of my_hash_1. We are going to overwrite the value in the first hash with the value in the second hash as follows:
my_hash_1 = {:a => 10, :b => 20}
my_hash_2 = {:c => 30, :d => 40, :a => 33}
my_hash_1.merge(my_hash_2)
The output will be as follows:

Figure 2.50: Output depicting the overriding of hashes
Retrieving Keys or Values from a Hash
We can also get all the keys or values from a hash using .keys and .values, as shown in the following code:
my_hash= {:a => 10, :b => 20}
my_hash.keys
my_hash.values
The keys and values of the hash will be as follows:

Figure 2.51: Keys and values of a hash
Deleting a Value from a Hash by Key
If the key is found, it will return the value. If the key is not found, nil would be returned. Look at the following example:
my_hash= {:a => 10, :b => 20}
my_hash.delete(:b)
my_hash
my_hash.delete(:c)
The output will be as follows:

Figure 2.52: Deleting a value from a hash
You can see how the value for :b is deleted and also nil is returned when :c is called.
Removing or Rejecting Elements from a Hash
Based on a logical condition, if the value of a specific key is below a threshold, .reject returns a new copy with the elements omitted, while reject! actually removes them from the hash. Consider the following example:
my_hash= {:a => 10, :b => 20, :c => 23, :d => 2}
my_hash.reject { |key, value| value < 20 }
The output will be as follows:

Figure 2.53: Using reject on a hash
As you can see in the preceding code, the values below 20 were removed from the hash.
Establishing whether a Hash Contains a Particular Value
This operation will compare the value to the predefined value and display the result accordingly:
Consider the following example:
my_hash= {:a => 10, :b => 20}
my_hash.has_value?(10)
my_hash.has_value?(100)
The output will be as follows:

Figure 2.54: Finding values within the hash
The preceding output shows that the .has_value? method has returned true for the key 10, as it is a part of the hash. It has subsequently returned false for the key 100.
Let's now solve an exercise that will strengthen our understanding of hashes.
Exercise 2.03: Converting a Time String to a Hash
In this exercise, we will write a program to standardize the input time to hh:mm:ss.s format. The following steps will help with the solution:
- Go to the Terminal and use irb to enter the Interactive Ruby Shell.
- Define a string, which may be the following format – hh:mm:ss.s.
my_string_time = "00:05:23.323"
my_hash_time = {}
- Split the string into appropriate keys and update the hash:
my_hash_time["hh"] = my_string_time.split(":")[0]
my_hash_time["mm"] = my_string_time.split(":")[1]
my_hash_time["ss"] = my_string_time.split(":")[2].split(".")[0]
my_hash_time["s"] = my_string_time.split(":")[2].split(".")[1]
The output will be as follows:
Figure 2.55: Splitting the string
- Inspect the hash to see the standardized input:
my_hash_time.inspect
The output will now appear as follows:
Figure 2.56: Output for the inspect method
Make sure you follow the original time format, as the program would fail if you changed it. But this exercise shows you how to convert the time string to a hash, and then refer to each part of the string as you would like to, as shown in the following code:
my_hash_time["hh"]
my_hash_time["mm"]
my_hash_time["ss"]
my_hash_time["s"]
The output will now look like the following code block:

Figure 2.57: Separating strings from a hash
- 數據科學實戰手冊(R+Python)
- Node.js Design Patterns
- Moodle Administration Essentials
- Visual Basic編程:從基礎到實踐(第2版)
- Building a Recommendation Engine with Scala
- HTML5+CSS3網頁設計
- C語言程序設計教程
- Android開發案例教程與項目實戰(在線實驗+在線自測)
- Flowable流程引擎實戰
- 寫給程序員的Python教程
- Go語言底層原理剖析
- 零基礎學HTML+CSS第2版
- Visual C++開發寶典
- Web前端開發最佳實踐
- WordPress Search Engine Optimization(Second Edition)