在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
和三元运算符,但有些人发布了非常有趣的答案!
答案 0 :(得分:25)
因为数组是指针,所以这也有效:
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的例子很难确定答案。
答案 1 :(得分:9)
old_a.map! { |a| a == "b" ? a + "!" : a }
给出
=> ["a", "b!", "c"]
map!
修改了接收器,因此old_a
现在是返回的数组。
答案 2 :(得分:6)
我同意地图声明很好。它清晰简单,而且很容易 任何人都可以维持。
如果你想要更复杂的东西,那怎么样?
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"
答案 3 :(得分:3)
您的map
解决方案是最好的解决方案。我不确定为什么你认为map_modifying_only_elements_where在某种程度上更好。使用map
更简洁,更简洁,并且不需要多个块。
答案 4 :(得分:2)
一个班轮:
["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }
在上面的代码中,您以[]“cumulative”开头。当您通过枚举器枚举时(在我们的例子中是数组,[“a”,“b”,“c”]),累积以及“当前”项目将传递给我们的块(| cumulative,i |)和我们块执行的结果被分配给累积。我上面做的是当项目不是“b”并且追加“b!”时保持累积不变。累积数组,当它是b时返回它。
上面的答案使用select
,这是最简单的方法(并记住)。
您可以将select
与map
合并,以实现您的目标:
arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
=> ["b!"]
在select
块内,指定元素“选中”的条件。这将返回一个数组。您可以在结果数组上调用“map”以向其附加感叹号。
答案 5 :(得分:1)
如果您不需要旧阵列,我更喜欢地图!在这种情况下因为你可以使用!表示您正在更改数组的方法。
self.answers.map!{ |x| (x=="b" ? x+"!" : x) }
我更喜欢这个:
new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }
答案 6 :(得分:1)
它只有几行,但这里有一个替代它的地狱:
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"]
在我看来,三元收藏似乎是最好的。
答案 7 :(得分:1)
我发现实现此目标的最佳方法是使用tap
arr = [1,2,3,4,5,6]
[].tap do |a|
arr.each { |x| a << x if x%2==0 }
end
答案 8 :(得分:0)
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]
希望对某人有用!