Here we are the third installment of Enumerating Enumerable, my attempt to do a better job of documenting Ruby’s Enumerable
module than RubyDoc.org does. In this installment, I cover Enumerable#collect
and its syntactic sugar twin (and the one I prefer), Enumerable#map
.
In case you missed the earlier installments, they’re listed (and linked) below:
Enumerable#collect/Enumerable#map Quick Summary
In the simplest possible terms | Create a new array by performing some operation on every item in the given collection. collect and map are synonyms — you can use either. I personally prefer map as it’s shorter and makes sense if you think of the method as being like functional mapping. |
---|---|
Ruby version | 1.8 and 1.9 |
Expects | A block containing the criteria. This block is optional, but you’re likely to use one in most cases. |
Returns | An array made up of items created by performing some operation on the given collection. |
RubyDoc.org’s entry | Enumerable#collect / Enumerable#map |
Enumerable#collect/Enumerable#map and Arrays
When used on an array and a block is provided, collect
/map
passes each item to the block, where the operation in the block is performed on the item and the result is then added to the result array. Note the the result array has the same number of elements as the given array.
[1, 2, 3, 4].map {|number| number ** 2} => [1, 4, 9, 16] ["Aqua", "Bat", "Super", "Wonder Wo"].map {|adjective| adjective + "man"} => ["Aquaman", "Batman", "Superman", "Wonder Woman"]
When the block is omitted, collect
/map
uses this implied block: {|item| item}
, which means when applied on an array without a block, collect
/map
is the identity function — the resulting array is the same as the given array.
[1, 2, 3, 4].map => [1, 2, 3, 4] ["Aqua", "Bat", "Super", "Wonder Wo"].map => ["Aqua", "Bat", "Super", "Wonder Wo"]
Enumerable#collect/Enumerable#map and Hashes
When used on a hash and a block is provided, collect
and map
pass each key/value pair in the hash to the block, which you can “catch” as either:
- A two-element array, with the key as element 0 and its corresponding value as element 1, or
- Two separate items, with the key as the first item and its corresponding value as the second item.
Each key/value pair is passed to the block, where the operation in the block is performed on the item and the result is then added to the result array. Note the the result array has the same number of elements as the given array.
burgers = {"Big Mac" => 300, "Whopper with cheese" => 450, "Wendy's Double with cheese" => 320} # What if I had just half a burger? burgers.map {|burger| burger[1] / 2} => [160, 150, 225] burgers.map {|sandwich, calories| calories / 2} => [160, 150, 225] burgers.map {|burger| "Have a tasty #{burger[0]}!"} => ["Have a tasty Wendy's Double with cheese!", "Have a tasty Big Mac!", "Have a tasty Whopper with cheese!"] burgers.map {|sandwich, calories| "Have a tasty #{sandwich}!"} => ["Have a tasty Wendy's Double with cheese!", "Have a tasty Big Mac!", "Have a tasty Whopper with cheese!"] burgers.map {|sandwich, calories| ["Half a #{sandwich}", calories / 2]} => [["Half a Wendy's Double with cheese", 160], ["Half a Big Mac", 150], ["Half a Whopper with cheese", 225]]
When the block is omitted, collect
/map
uses this implied block: {|item| item}
, which means when applied on an hash without a block, collect
/map
returns an array containing a set of two-item arrays, one for each key/value pair in the hash. For each two-item array, item 0 is the key and item 1 is the corresponding value.
burgers = {"Big Mac" => 300, "Whopper with cheese" => 450, "Wendy's Double with cheese" => 320} burgers.map => [["Wendy's Double with cheese", 320], ["Big Mac", 300], ["Whopper with cheese", 450]]
Special Case: Using Enumerable#collect/Enumerable#map on Empty Arrays and Hashes
When applied to an empty array or hash, with or without a block, collect
and map
always return an empty array.
# Let's try it with an empty array [].map => [] [].map {|item| item * 2} => [] # Now let's try it with an empty hash {}.map => [] {}.map {|sandwich, calories| "Have a tasty #{sandwich}!"} => []
9 replies on “Enumerating Enumerable: Enumerable#collect/Enumerable#map”
You’re discussing
Enumerable
, so this is slightly tangential…Most folks will encounter these methods for Arrays. The
Array
class implements destructive versions of these methods suffixed with a bang(!
)For example:
But
@Reg Braithwaite: Ah, yes. I’ve been following the
Enumerable
docs so much that I’d forgotten about some of the similar (and sometimes overlapping) methods inArray
. Althoughcollect!
/map!
are technically inArray
, this series is really more about practical results, which means I should update the article to include those methods.Thanks for the heads-up!
[…] there were a numbers of interesting articles: Using select, reject, collect, inject and detect, Enumerating Enumerable, Macros, Hygiene, and Call By Name in Ruby Eliminating code duplication with Metaprogramming. Also […]
[…] collect / map […]
[…] collect / map […]
[…] collect / map […]
[…] collect / map […]
[…] collect / map […]
[…] functional niceties), but I have no excuse for blowing this. I’ve actually written a whole series of articles on the power of Ruby’s Enumerable module, including the select method (and what […]