[ACCEPTED]-Map an array modifying only elements matching a certain condition-collect
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 6 the loop, make sure you use a method that 5 alters the object rather than creating a 4 new object. E.g. upcase!
compared to upcase
.
The exact 3 procedure depends on what exactly you are 2 trying to achieve. It's hard to nail a definite 1 answer with foo-bar examples.
old_a.map! { |a| a == "b" ? a + "!" : a }
gives
=> ["a", "b!", "c"]
map!
modifies the receiver in place, so 1 old_a
is now that returned array.
I agree that the map statement is good as 3 it is. It's clear and simple,, and would 2 easy for anyone to maintain.
If you want 1 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 3 sure why you think map_modifying_only_elements_where 2 is somehow better. Using map
is cleaner, more 1 concise, and doesn't require multiple blocks.
One liner:
["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }
In the code above, you start 17 with [] "cumulative". As you enumerate through 16 an Enumerator (in our case the array, ["a", "b", "c"]), cumulative 15 as well as "the current" item get passed 14 to our block (|cumulative, i|) and the result 13 of our block's execution is assigned to 12 cumulative. What I do above is keep cumulative 11 unchanged when the item isn't "b" and append 10 "b!" to cumulative array and return it when 9 it is a b.
There is an answer above that 8 uses select
, which is the easiest way to do (and 7 remember) it.
You can combine select
with map
in order 6 to achieve what you're looking for:
arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
=> ["b!"]
Inside 5 the select
block, you specify the conditions for 4 an element to be "selected". This will return 3 an array. You can call "map" on the resulting 2 array to append the exclamation mark to 1 it.
Ruby 2.7+
As of 2.7 there's a definitive answer.
Ruby 2.7 is introducing filter_map
for this exact 4 purpose. It's idiomatic and performant, and 3 I'd expect it to become the norm very soon.
For 2 example:
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
Here's a good read on the subject.
Hope that's useful to 1 someone!
If you don't need the old array, I prefer 3 map! in this case because you can use the 2 ! method to represent you are changing the 1 array in place.
self.answers.map!{ |x| (x=="b" ? x+"!" : x) }
I prefer this over:
new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }
It's a few lines long, but here's an alternative 2 for the hell of it:
oa = %w| a b c |
na = oa.partition { |a| a == 'b' }
na.first.collect! { |a| a+'!' }
na.flatten! #Add .sort! here if you wish
p na
# >> ["b!", "a", "c"]
The collect with ternary 1 seems best in my opinion.
I've found that the best way to accomplish 1 this is by using tap
arr = [1,2,3,4,5,6]
[].tap do |a|
arr.each { |x| a << x if x%2==0 }
end
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.