映射一个数组,只修改匹配某个条件的元素

Posted

技术标签:

【中文标题】映射一个数组,只修改匹配某个条件的元素【英文标题】:Map an array modifying only elements matching a certain condition 【发布时间】:2010-10-11 11:45:16 【问题描述】:

在 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 与三元运算符一起使用,但你们中的一些人发布了非常有趣的答案!

【问题讨论】:

我认为#map 的东西很好。 ;-) 是的,我同意。如果这让你更喜欢它,你可以取出括号! 关门了?超光速。看我的帖子=P 我来这里是为了寻找一种只保留某些元素的方法,我发现这很有帮助:***.com/questions/5152098/… Ruby 2.7 为此引入了内置解决方案:filter_map, more info here。 【参考方案1】:

Ruby 2.7+

从 2.7 开始,有一个明确的答案。

Ruby 2.7 正是为此目的引入了filter_map。它既惯用又高效,我希望它很快就会成为常态。

例如:

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map  |i| i * 2 if i.even? 
# => [4, 16, 20]

这是good read on the subject。

希望对某人有用!

【讨论】:

【参考方案2】:

一个班轮:

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

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

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

您可以将selectmap 结合使用,以实现您想要的:

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

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

【讨论】:

【参考方案3】:

我发现最好的方法是使用tap

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

【讨论】:

【参考方案4】:

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

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 的例子很难得出一个明确的答案。

【讨论】:

【参考方案5】:

它有几行长,但这里有一个替代方案:

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"]

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

【讨论】:

【参考方案6】:
old_a.map!  |a| a == "b" ? a + "!" : a 

给予

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

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

【讨论】:

【参考方案7】:

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

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

我更喜欢这个:

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

【讨论】:

【参考方案8】:

我同意地图声明是好的。它清晰而简单,而且很容易 任何人都可以维护。

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

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"

【讨论】:

你已经把一行变成了三十多岁。我喜欢你的风格。 确实说在地图中粘贴条件是最好的:)【参考方案9】:

您的map 解决方案是最好的解决方案。我不确定您为什么认为 map_modifying_only_elements_where 更好。使用map 更干净、更简洁,并且不需要多个块。

【讨论】:

以上是关于映射一个数组,只修改匹配某个条件的元素的主要内容,如果未能解决你的问题,请参考以下文章

检查数组中的最后一个元素是不是匹配条件

如何显示多个满足条件的数组元素?

检查数组中的每个元素是不是匹配条件

python数组中怎样删除符合条件的元素

猫鼬更新对象数组中匹配条件的元素的值

从与数组上的查询条件匹配的第一个元素中投影特定字段