映射数组仅修改与特定条件匹配的元素

在Ruby中,以某种方式映射数组的最具表现力的方式是什么?某些元素被修改而其他元素保持不变

这是一种直截了当的方式:

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

当然,如果不够的话,省略“单独”的情况:

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

我想要的是这样的:

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

有没有一些很好的方法在Ruby中执行此操作(或者Rails有一些我还没有找到的方便方法)?


谢谢大家的回复。 虽然你总是说服我最好只使用三元运算符的map ,但有些人发布了非常有趣的答案!

我同意地图声明是好的。 它清晰简单,任何人都可以轻松维护。

如果你想要更复杂的东西,那怎么样?

 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{ abc }.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 } #=> # ('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy" 

因为数组是指针,所以这也有效:

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

在循环中,确保使用改变对象的方法而不是创建新对象。 upcase!upcase相比。

确切的程序取决于您要实现的目标。 用foo-bar示例很难确定答案。

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

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

map! 修改接收器到位,所以old_a现在是返回的数组。

您的map解决方案是最好的。 我不确定为什么你认为map_modifying_only_elements_where在某种程度上更好。 使用map更清晰,更简洁,并且不需要多个块。

一个class轮:

 ["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative } 

在上面的代码中,您以[]“cumulative”开头。 当您通过枚举器枚举时(在我们的例子中是数组,[“a”,“b”,“c”]),累积以及“当前”项目将传递给我们的块(| cumulative,i |)和块执行的结果被分配给累积。 我上面做的是当项目不是“b”并且追加“b!”时保持累积不变。 累积数组,当它是b时返回它。

上面有一个答案,使用select ,这是最简单的方法(并记住)它。

您可以将selectmap结合使用,以实现您的目标:

  arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" } => ["b!"] 

select块内,指定元素“选中”的条件。 这将返回一个数组。 您可以在结果数组上调用“map”以向其附加感叹号。

如果你不需要旧arrays,我更喜欢地图! 在这种情况下因为你可以使用! 表示您正在更改数组的方法。

 self.answers.map!{ |x| (x=="b" ? x+"!" : x) } 

我更喜欢这个:

 new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) } 

这是几行长,但这里有一个替代它的地狱:

 oa = %w| abc | na = oa.partition { |a| a == 'b' } na.first.collect! { |a| a+'!' } na.flatten! #Add .sort! here if you wish p na # >> ["b!", "a", "c"] 

在我看来,三元收集​​似乎是最好的。

我发现实现这一目标的最佳方法是使用tap

 arr = [1,2,3,4,5,6] [].tap do |a| arr.each { |x| a << x if x%2==0 } end 
Interesting Posts