Extracting Portions of Hashes
Problem
You have a hash that contains a lot of values, but only a few of them are interesting. You want to select the interesting values and ignore the rest.
Solution
You can use the Hash#select method to extract part of a hash that follows a certain rule. Suppose you had a hash where the keys were Time objects representing a certain date, and the values were the number of web site clicks for that given day. We'll simulate such as hash with random data:
require 'time' click_counts = {} 1.upto(30) { |i| click_counts[Time.parse("2006-09-#{i}")] = 400 + rand(700) } p click_counts # {Sat Sep 23 00:00:00 EDT 2006=>803, Tue Sep 12 00:00:00 EDT 2006=>829, # Fri Sep 01 00:00:00 EDT 2006=>995, Mon Sep 25 00:00:00 EDT 2006=>587, # …
You might want to know the days when your click counts were low, to see if you could spot a trend. Hash#select can do that for you:
low_click_days = click_counts.select {|key, value| value < 450 } # [[Thu Sep 14 00:00:00 EDT 2006, 449], [Mon Sep 11 00:00:00 EDT 2006, 406], # [Sat Sep 02 00:00:00 EDT 2006, 440], [Mon Sep 04 00:00:00 EDT 2006, 431], # …
Discussion
The array returned by Hash#select contains a number of key-value pairs as two-element arrays. The first element of one of these inner arrays is a key into the hash, and the second element is the corresponding value. This is similar to how Hash#each yields a succession of two-element arrays.
If you want another hash instead of an array of key-value pairs, you can use Hash#inject instead of Hash#select. In the code below, kv is a two-element array containing a key-value pair. kv[0] is a key from click_counts, and kv[1] is the corresponding value.
low_click_days_hash = click_counts.inject({}) do |h, kv| k, v = kv h[k] = v if v < 450 h end # => {Mon Sep 25 00:00:00 EDT 2006=>403, # Wed Sep 06 00:00:00 EDT 2006=>443, # Thu Sep 28 00:00:00 EDT 2006=>419}
You can also use the Hash.[] constructor to create a hash from the array result of Hash#select:
low_click_days_hash = Hash[*low_click_days.flatten] # => {Thu Sep 14 00:00:00 EDT 2006=>449, Mon Sep 11 00:00:00 EDT 2006=>406, # Sat Sep 02 00:00:00 EDT 2006=>440, Mon Sep 04 00:00:00 EDT 2006=>431, # …
See Also
- Recipe 4.13, " Extracting Portions of Arrays"