官术网_书友最值得收藏!

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:

  1. Go to the Terminal and use irb to enter the Interactive Ruby Shell.
  2. Define a string, which may be the following format – hh:mm:ss.s.

    my_string_time = "00:05:23.323"

    my_hash_time = {}

  3. 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

  4. 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

主站蜘蛛池模板: 新乡市| 黄梅县| 高陵县| 常德市| 深州市| 宁强县| 云南省| 阿拉尔市| 合肥市| 新丰县| 台山市| 周口市| 石首市| 临邑县| 铜陵市| 赤峰市| 博乐市| 巩留县| 潜江市| 禄劝| 泸水县| 鹤岗市| 靖西县| 营口市| 镇远县| 米脂县| 林甸县| 保山市| 五常市| 舞阳县| 温泉县| 七台河市| 荔浦县| 克什克腾旗| 托里县| 海淀区| 醴陵市| 合作市| 双鸭山市| 崇仁县| 搜索|