Map an array modifying only elements matching a certain condition

old_a.map! { |a| a == "b" ? a + "!" : a }

gives

=> ["a", "b!", "c"]

map! modifies the receiver in place, so old_a is now that returned array.


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.


I agree that the map statement is good as it is. It's clear and simple,, and would easy for anyone to maintain.

If you want something more complex, how about this?

module Enumerable
  def enum_filter(&filter)
    FilteredEnumerator.new(self, &filter)
  end
  alias :on :enum_filter
  class FilteredEnumerator
    include Enumerable
    def initialize(enum, &filter)
      @enum, @filter = enum, filter
      if enum.respond_to?(:map!)
        def self.map!
          @enum.map! { |elt| @filter[elt] ? yield(elt) : elt }
        end
      end
    end
    def each
      @enum.each { |elt| yield(elt) if @filter[elt] }
    end
    def each_with_index
      @enum.each_with_index { |elt,index| yield(elt, index) if @filter[elt] } 
    end
    def map
      @enum.map { |elt| @filter[elt] ? yield(elt) : elt }
    end
    alias :and :enum_filter
    def or
      FilteredEnumerator.new(@enum) { |elt| @filter[elt] || yield(elt) }
    end
  end
end

%w{ a b c }.on { |x| x == 'b' }.map { |x| x + "!" } #=> [ 'a', 'b!', 'c' ]

require 'set'
Set.new(%w{ He likes dogs}).on { |x| x.length % 2 == 0 }.map! { |x| x.reverse } #=> #<Set: {"likes", "eH", "sgod"}>

('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy"

Your map solution is the best one. I'm not sure why you think map_modifying_only_elements_where is somehow better. Using map is cleaner, more concise, and doesn't require multiple blocks.