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"] 1 2 3 4 5 [ 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"] 1 2 3 4 5 [ 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]] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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]] 1 2 3 4 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.