In yesterday’s post, I showed how we can use Python’s “reduce” function to create a dictionary. Ruby, of course, also has dictionaries, but calls them “hashes.” In this posting, I’m going to show how we can create a hash in Ruby when iterating over an enumerable. In so doing, we’ll see how we can use the “reduce” method to create interesting hashes, building them up one step at a time.
Let’s start with the simplest and easiest solutions, which will be quite similar to what we did with Python: We’ll take an array of arrays, and turn that into a hash:
[['a', 1], ['b', 2]].to_h
Oh, wait — you’re not using Ruby 2.1? Too bad; that version added Array#to_h, a method that creates key-value pairs from nested arrays. The idea is that each inner array should have two elements, the first being a key and the second being a value. So the above call to Array#to_h will result in:
{"a"=>1, "b"=>2}
Of course, we often want to use symbols for our hash keys, rather than strings. But let’s ignore that for today, and just concentrate on how we can create hashes, rather than quibble over key types.
If you’re using a version of Ruby earlier than 2.1, and thus don’t have access to Array#to_h, then you can always use Hash[],
Hash['a', 1, 'b', 2] => {"a"=>1, "b"=>2}
Note that whereas Array#to_h expected to get an array of arrays, Hash[] expects to get a single array between the [ ]. So if we’re interested in using the “reduce” method to build up a hash, we can do it as follows:
Hash[%w(a b c).reduce([]) {|total, current| total << [current, current.ord]}]
In other words, we create an array consisting of three strings (‘a’, ‘b’, and ‘c’), by creating an array of arrays, and then turning that into a hash. But of course, hashes (like all data structures in Ruby) are mutable, and (more importantly) methods that modify data structures often return the object on which they worked. So we can actually build up our hash in a more direct way:
%w(a b c).reduce({}) {|total, current| total.update(current => current.ord)}
Here, we once again create an array of “a”, “b”, and “c”. Then we initialize our call to “reduce” with an empty hash. Then we reduce, with each call invoking Hash#update. Hash#update not only merges the parameter’s value (a hash) into “total”, but returns the resulting hash. Thus, with each invocation, “total” is overwritten with the new hash, to which we have added a new pair.
Of course, this example is a pretty trivial one; you can imagine that instead of invoking current.ord, that you create a new object based on the parameter, or that you do a calculation based on it instead. The bottom line, though, is that creating hashes incrementally in Ruby is pretty easy to do. Moreover, if you find that a single-line block is not enough space in which to do all of the calculations you need, you can always use a do-end style of block, which will let you have as many lines as you want.
Next time: Implementing “map” and “filter” with “reduce”! If you understand those, then you’ve totally internalized the power (and dare I say it, fun) of this method.