映射一个数组,只修改匹配某个条件的元素
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
,这是最简单的方法(并记住)。
您可以将select
与map
结合使用,以实现您想要的:
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
更干净、更简洁,并且不需要多个块。
【讨论】:
以上是关于映射一个数组,只修改匹配某个条件的元素的主要内容,如果未能解决你的问题,请参考以下文章