我有一个map
,它要么改变一个值,要么将其设置为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
型
但好像不太习惯。有没有一种很好的方法来Map一个函数到一个列表上,在你去的时候删除/排除nils?
9条答案
按热度按时间y0u0uwnf1#
可以使用
compact
:字符串
我想提醒大家,如果你得到一个包含nils的数组作为
map
块的输出,并且该块试图有条件地返回值,那么你就有了代码气味,需要重新考虑你的逻辑。例如,如果你正在做这样的事情:
型
那么就不要。相反,在
map
,reject
你不想要的东西或select
你想要的东西之前:型
我认为使用
compact
来清理混乱是最后的努力,以摆脱我们没有正确处理的事情,通常是因为我们不知道会发生什么。我们应该总是知道什么样的数据在我们的程序中被抛出;意外/未知数据是错误的。每当我在我正在处理的数组中看到nils时,我都会深入研究它们存在的原因,看看我是否可以改进生成数组的代码,而不是让Ruby浪费时间和内存生成nils,然后筛选数组以删除它们。型
webghufk2#
尝试使用
reduce
或inject
。字符串
我同意公认的答案,我们不应该
map
和compact
,但原因不同。我内心深处觉得
map
然后compact
等价于select
然后map
。考虑:map
是一对一函数。如果你是从某个值集Map的,并且你是map
,那么你希望在输出集中为输入集中的每个值都有一个值。如果你必须事先select
,那么你可能不希望在设置上使用map
。如果你必须在之后使用select
(或compact
),那么你可能不希望在设置中使用map
。在这两种情况下,您都在整个集合上迭代了两次,而reduce
只需要执行一次。同样,在英语中,你试图“将一组整数减少到一组偶数”。
qeeaahzv3#
Ruby 2.7+
Ruby 2.7引入
filter_map
正是为了这个目的。它是惯用的和高性能的,我希望它很快成为规范。举例来说:
字符串
在你的例子中,当块的计算结果为falsey时,只需:
型
“Ruby 2.7 adds Enumerable#filter_map”是一本关于这个主题的好书,其中有一些针对这个问题的早期方法的性能基准测试:
型
rt4zxlrg4#
毫无疑问,
compact
是解决这个任务的最佳方法。然而,我们可以通过简单的减法来实现相同的结果:字符串
ryevplcw5#
在您的示例中:
字符串
除了被替换为
nil
之外,这些值看起来没有改变。如果是这种情况,则:型
就足够了
lp0sw83n6#
如果你想要一个更宽松的拒绝标准,例如,拒绝空字符串和nil,你可以用途:
字符串
如果你想更进一步拒绝零值(或者在这个过程中应用更复杂的逻辑),你可以传递一个块来拒绝:
型
twh00eeo7#
你可以在结果数组上使用
#compact
方法。字符串
0ejtzxu18#
each_with_object
可能是这里最干净的方法:字符串
在我看来,
each_with_object
在条件情况下比inject
/reduce
更好,因为你不必担心块的返回值。mutmk8jj9#
实现它的另一种方式将如下所示。在这里,我们使用
Enumerable#each_with_object
来收集值,并使用Object#tap
来删除临时变量,否则nil
检查transform x
方法的结果时需要临时变量。字符串
完整的示例说明:
型
替代方法:
通过查看调用
transform x
的方法,我们不清楚在该方法中输入x
的目的是什么。如果我假设你要通过传递一些url
来处理x
的值,并确定哪些x
真正被处理成有效的非空结果,那么Enumerabble.group_by
可能是比Enumerable#map
更好的选择。型