Map with Index
Notes: Working with ruby 1.8.7 but 1.9 solution presented at bottom.
While working on my main contract today I needed to create a new array
based off mutated values of an existing array. Simple enough I’ll just
use Enumerable::map
right? Almost immediatley I realized I needed
to make a reference to an array via the loops index. I first
checked to see if a map_with_index
existed similar to
Enumerable::each_with_index.
It doesn’t.
I took a quick read through the docs and realized that each_with_index
actually returns an Enumerable.
This means I can use each_with_index
and map
to achieve the desired
effect. Here’s an example:
arr = [:cat, :dog, :moose] # Canada eh?
new_arr = arr.each_with_index.map { |value,index| "The #{value} is at position #{index}" }
=> ["The cat is at position 0", "The dog is at position 1", "The moose is at position 2"]
A closer look at the Enumerable returned by each_with_index
shows us
exactly what each_with_index returns that lets map play so nicely:
arr = [:cat, :dog, :moose]
arr.each_with_index.to_a
=> [[:cat, 0], [:dog, 1], [:moose, 2]]
From here the array of arrays will get mapped. I had never thought of
passing multiple values into a map
block but it works wonders. The
map
block takes each value of the inner array and offers it as a value
inside the block.
The actual reason I needed this code was to parse and alter a string into keys with multiple possible matches. I plan to write a more in-depth post regarding this topic later but until then here’s the snippet I used:
phase = 2 # Dynamic
string = 'awards.about.marquee.title' # A key for a yaml translation
a = string.split('.')
possibilities = a.each_with_index.map do |v,i|
b = a.clone
b[i] = "p#{phase}_#{v}"
b.join('.')
end
=> ["p2_awards.about.marquee.title", "awards.p2_about.marquee.title", "awards.about.p2_marquee.title", "awards.about.marquee.p2_title"]
This output the different possibilites where one of the potential keys could be prefixed with ‘p2’.
Ruby 1.9
While writing this post and sifting through documentation I also found
the
Enumberable#with_index
method which essentially does the same thing but can make the syntax
easier to read:
arr = [:cat, :dog, :moose]
new_arr = arr.map.with_index {|value,index| "The #{value} is at position #{index}" }
=> ["The cat is at position 0", "The dog is at position 1", "The moose is at position 2"]
So there we have it: Map with index for ruby 1.8 and 1.9.