我有一个地图,改变一个值或设置它为nil。然后我想从列表中删除nil项。这个清单不需要保存。
这是我目前拥有的:
# A simple example function, which returns a value or nil
def transform(n)
rand > 0.5 ? n * 10 : nil }
end
items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]
我知道我可以只做一个循环,并有条件地收集另一个数组,像这样:
new_items = []
items.each do |x|
x = transform(x)
new_items.append(x) unless x.nil?
end
items = new_items
但它看起来并不是那么地道。有没有一个很好的方法来映射一个函数在一个列表,删除/排除nils,因为你去?
完成它的另一种方法如下所示。在这里,我们使用Enumerable#each_with_object来收集值,并使用object# tap来消除临时变量,否则需要对process_x方法的结果进行nil检查。
items.each_with_object([]) {|x, obj| (process x).tap {|r| obj << r unless r.nil?}}
完整的示例说明:
items = [1,2,3,4,5]
def process x
rand(10) > 5 ? nil : x
end
items.each_with_object([]) {|x, obj| (process x).tap {|r| obj << r unless r.nil?}}
备选方案:
通过查看调用process_x url的方法,并不清楚在该方法中输入x的目的是什么。如果我假设你将通过传递一些url来处理x的值,并确定哪些xs真的被处理成有效的非nil结果-那么,可能是Enumerabble。group_by比Enumerable#map更好。
h = items.group_by {|x| (process x).nil? ? "Bad" : "Good"}
#=> {"Bad"=>[1, 2], "Good"=>[3, 4, 5]}
h["Good"]
#=> [3,4,5]
如果你想要一个更宽松的拒绝标准,例如,拒绝空字符串以及nil,你可以使用:
[1, nil, 3, 0, ''].reject(&:blank?)
=> [1, 3, 0]
如果你想进一步拒绝零值(或者对进程应用更复杂的逻辑),你可以传递一个块来拒绝:
[1, nil, 3, 0, ''].reject do |value| value.blank? || value==0 end
=> [1, 3]
[1, nil, 3, 0, '', 1000].reject do |value| value.blank? || value==0 || value>10 end
=> [1, 3]
Ruby 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]
在你的例子中,当块计算为假时,简单地:
items.filter_map { |x| process_x url }
“Ruby 2.7添加了可枚举的#filter_map”是关于这个主题的一个很好的阅读,针对这个问题的一些早期方法提供了一些性能基准:
N = 100_000
enum = 1.upto(1_000)
Benchmark.bmbm do |x|
x.report("select + map") { N.times { enum.select { |i| i.even? }.map{ |i| i + 1 } } }
x.report("map + compact") { N.times { enum.map { |i| i + 1 if i.even? }.compact } }
x.report("filter_map") { N.times { enum.filter_map { |i| i + 1 if i.even? } } }
end
# Rehearsal -------------------------------------------------
# select + map 8.569651 0.051319 8.620970 ( 8.632449)
# map + compact 7.392666 0.133964 7.526630 ( 7.538013)
# filter_map 6.923772 0.022314 6.946086 ( 6.956135)
# --------------------------------------- total: 23.093686sec
#
# user system total real
# select + map 8.550637 0.033190 8.583827 ( 8.597627)
# map + compact 7.263667 0.131180 7.394847 ( 7.405570)
# filter_map 6.761388 0.018223 6.779611 ( 6.790559)