1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

Mapping Arrays

By

Mapping Arrays

In previous articles we've looked at creating arrays, using them in simple ways and even sorting arrays. However, there is one operation that is continually useful that is often ignored: mapping. Mapping an array is to iterate over an array and, for every value, pass that value to a block and insert the return from the block into a new array. You're "mapping" the values of an array to a new array via a function (the block you passed to the Array).

Map and Enumerable

Yet again, the map method doesn't really belong to the Array class, it belongs to the Enumerable module. And while an Array does have a faster version implemented in C, you can think of this as the same method being imported from the Enumerable module. This is a common theme with Arrays. They are Enumerable objects, yet the Array class implements many of the Enumerable methods itself, often in C. This is purely for a nice bump in performance. If you can make Array operations just a little faster, since they're used so often you make everything faster.

So on to a practical example. Say you have an array of symbols, as well as a hash. For each symbol in the array, you want to look up that symbol in the hash and add the value from the hash to a new array. Without using the map method, the code would look something like this.


names = %w{ Alice Bob }
scores = { "Alice" => 100, "Bob", 75 }

scoreboard = []
names.each do|n|
  scoreboard 

Not very compact. Not very Ruby-like. So let me just show you how it looks when using map.


names = %w{ Alice Bob }
scores = { "Alice" => 100, "Bob", 75 }

scoreboard = names.map{|n| scores[n] }

Better, right? And it doesn't lose any of the readability of the original. I show you both examples because, really, the first example is a lot like how the second example works. Map will iterate over the array, and for every element call a block with that element in the array and add them to the output array. The concept is simple, and once you know how to use them you can start slinging maps all over the place.

There is, of course, a "harder" map method. If you want to map your Array in place, you can use the map! method. This will replace your Array's contents with the output of the map method, instead of creating a new array object. This can be faster, and should be used where appropriate. There are also two methods called collect and collect!, which are synonyms for map and map!.

Map Magic

A common thing to do with map is to run a single method over every element in an array. And while map itself is already compact and expressive, there is an even smaller but somewhat more cryptic way to do this. This is the "implicit to_proc method." When calling a method that expects a block, you can specify a callable object in place of the block itself by prepending an ampersand to it. And, this is where the magic comes in, if the object you pass is not a callable proc object, Ruby will try to call a to_proc method on it.

Let's look at a simple example first. I want to double all of the elements in an array using a proc object. I'll create the callable proc object then pass it using an ampersand to map.


double = Proc.new{|n| n*2 }
puts [1,2,3].map(&double)

Note that a reference to a callable proc object is required. If you have a method you want to call in this fashion, you'll have to generate a callable object to it using the method method.


def double(n)
  n * 2
end

puts [1,2,3].map(&method(:double))

But the important thing to note here is the ampersand when calling map and that you must feed it a callable proc object. This alone can be useful for DRY (Don't Repeat Yourself) code, if you're doing the same operation over and over again in different parts of your code, don't copy and paste the block all over the place. If you have to change that block just once, you will have to change it multiple times. And what will happen if you forget just one instance? Very sneaky bugs, that's what happens.

On to the magic. Remember that Ruby will call to_proc on any object that you pass to a block parameter. The Symbol#to_proc method returns a callable proc object that sends that symbol to the parameter as a message, essentially calling the specified method on every object. So &:to_s is the equivalent of the block {|o| o.to_s}. So, if we have an array of numbers but we want strings, we can do something like this.


[ 1, 2, 3, 4 ].map(&:to_s)
# This will produce the array [ "1", "2", "3", "4" ]
  1. About.com
  2. Technology
  3. Ruby
  4. Beginning Ruby
  5. Ruby's Basic Features
  6. Method for Mapping the Values of an Array

©2014 About.com. All rights reserved.