Map an array modifying only elements matching a certain condition

Yang Meyer picture Yang Meyer · Mar 5, 2009 · Viewed 30k times · Source

In Ruby, what is the most expressive way to map an array in such a way that certain elements are modified and the others left untouched?

This is a straight-forward way to do it:

old_a = ["a", "b", "c"]                         # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) }  # ["a", "b!", "c"]

Omitting the "leave-alone" case of course if not enough:

new_a = old_a.map { |x| x+"!" if x=="b" }       # [nil, "b!", nil]

What I would like is something like this:

new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"}) 
        do |y|
          y + "!"
        end
# ["a", "b!", "c"]

Is there some nice way to do this in Ruby (or maybe Rails has some kind of convenience method that I haven't found yet)?


Thanks everybody for replying. While you collectively convinced me that it's best to just use map with the ternary operator, some of you posted very interesting answers!

Answer

August Lilleaas picture August Lilleaas · Mar 9, 2009

Because arrays are pointers, this also works:

a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }

puts a.inspect
# => ["hello", "to!", "you!", "dude"]

In the loop, make sure you use a method that alters the object rather than creating a new object. E.g. upcase! compared to upcase.

The exact procedure depends on what exactly you are trying to achieve. It's hard to nail a definite answer with foo-bar examples.