将两个Ruby语句合并成一个

4

这个问题是针对一个非常特定的情况,我可能会更改参数。这不是任何Rails应用程序的一部分。

params是{:email =>“ ab”,:xyz =>“ ”,:opq => nil},我运行以下命令:

params.each{|_, v| v.strip! if v}
params.keep_if{|_, v| v.length > 0 if v}

如何将参数合并为{:email => "ab"}

有没有一种方式可以将上述两行代码合并成一行?


3
在Rails中,我认为修改 params 对象是一种不好的编程方式。建议使用返回新哈希值的方法,例如 Hash#select - Alex Wayne
params.each{|, v| v.strip! if v}.keep_if{|, v| v.length > 0 if v}. - tokland
5个回答

4
ruby-1.9.3-p125 :011 > params ={:email => " ab", :xyz => " ", :opq => nil}
 => {:email=>" ab", :xyz=>" ", :opq=>nil} 
ruby-1.9.3-p125 :012 > params.reject! { |_, v| !v || v.strip!.length == 0 }
 => {:email=>"ab"}

1
使用 strip! 来匹配提问者所需的值。 - Casey Foster
这不是在制作优雅的代码时的复杂性吗?他想要在一个语句中选择特定元素并对它们进行转换(去除)。 - spike
@Dave 它不会改变参数。我必须借助中间变量的帮助。ab = params.select { |_, v| v && v.strip.length > 0 } 然后 params = ab 所以仍然是两行。 - JVK
@JVK params = params.select ..(所以仍然只有一行,没有令人讨厌的偷偷摸摸的副作用 - user166390
2
@JVK 正确,我认为你也不应该这样做。修改答案会导致错误的结果。 - Dave Newton
显示剩余5条评论

1
params.each{|_, v| v.strip! if v}.reject!{|_, v| !v || v.length == 0}

1
我会避免使用具有副作用的函数,除非确实有必要:在这种情况下,我不能反驳有必要使用它(但可以争论这里没有必要)。 - user166390

0

实际上,你写的这些代码是可链式调用的:

params.each{|_, v| v.strip! if v}.keep_if{|_, v| v.length > 0 if v}

这段代码之所以有效,是因为each返回了对象本身(已更新)。然而,在我看来,原地更新通常不是一个好主意,因此这里提供了一种函数式的方法:
params2 = Hash[params.map { |k, v| [k, v.strip] if v && v.strip.size > 0 }.compact]

但这样做过于冗长,因为stdlib缺乏一些基本的抽象。借助于Enumerable#mashObject#present?,它就变得非常简单:

params2 = params.mash { |k, v| [k, v.strip] if v.present? }

0

这里有一个选项似乎在技术上回答了你的问题,但我猜想这不是你想要的:

params.select{|_,v| v.strip.length > 0 if v}

你遇到的根本问题是 Hash#map 方法并不返回一个 Hash,而是返回一个数组。许多人,包括我自己,都觉得这很烦人。例如:http://www.ruby-forum.com/topic/185611 下面是一种使用 inject 的冗长选项。
params.inject({}) {|h,(k,v)| h.merge(k => v ? v.strip : v)}.select{|_,v| v.length > 0 if v}

如果这对你很重要,这两种解决方案都不会对参数进行任何破坏性修改。

1.9.3-p125 :030 > params = {:email => " ab", :xyz => " ", :opq => nil}
 => {:email=>" ab", :xyz=>" ", :opq=>nil} 
1.9.3-p125 :031 > params.inject({}) {|h,(k,v)| h.merge(k => v ? v.strip : v)}.select{|_,v|   v.length > 0 if v}
 => {:email=>"ab"} 
1.9.3-p125 :032 > params
 => {:email=>" ab", :xyz=>" ", :opq=>nil} 

Inject是一个棘手的函数,需要一些时间来适应。我将尝试逐步解释这行代码:

  • params.inject({}}表示创建一个空哈希表来存储我们的结果。
  • |h, (k,v)|表示将新的空哈希表传递到变量h中,同时将原始params哈希表中的键值对分别传递到k、v中。

接下来就有点复杂了。我将从内部开始逐步解析下一个命令。

  • v ? v.strip : v使用三元运算符,在v为真时返回v.strip,在v为假或nil时返回v。

  • k => v ? v.strip : v创建一个新的哈希表并返回结果。

  • h.merge(k => v ? v.strip : v)将我们的新哈希表合并到最初为空的h中,并将结果传递给inject循环的下一次迭代。

  • 在循环的下一次迭代中,h将不再为空,并且未来的结果将继续合并到其中。

此时,我们已经去除了哈希值,如果我们在这里停止,结果看起来像这样:

1.9.3-p125 :032 > params
 => {:email=>" ab", :xyz=>" ", :opq=>nil} 
1.9.3-p125 :033 > params.inject({}) {|h,(k,v)| h.merge(k => v ? v.strip : v)}
 => {:email=>"ab", :xyz=>"", :opq=>nil} 

现在哈希已经被剥离,选择语句就很简单了。我在这里使用select而不是keep_if,因为尽管没有!,keep_if是一种破坏性方法。请参见此抱怨:http://news.ycombinator.com/item?id=2247352。由于我们的注入语句返回一个哈希,因此我们可以直接调用.select,但我不建议在实际操作中将代码行变得如此长。
1.9.3-p125 :034 > params
 => {:email=>" ab", :xyz=>" ", :opq=>nil} 
1.9.3-p125 :035 > params.inject({}) {|h,(k,v)| h.merge(k => v ? v.strip : v)}.select{|_,v| v.length > 0 if v}
 => {:email=>"ab"} 

0
params.keys.each{|k| v = h.delete(k); h[k] = v.strip if v and !v.empty?}

或者

params.select!{|k, v| v and v.strip!; v and !v.empty?}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接