我有一个这样的散列:
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
我想要一个简单的方法来拒绝所有不是choice+int的键。可以是choice1,或者从choice1到choice10。它变化。
我如何用单词选择和后面的一个或多个数字来挑选键?
奖金:
将散列转换为以tab (\t)作为分隔符的字符串。我这样做了,但它花了几行代码。通常大师级的卢比手可以在一行或几行内完成。
这是一条解决完整原始问题的直线:
params.select { |k,_| k[/choice/]}.values.join('\t')
但是上面的大多数解决方案都是使用slice或简单的regexp来解决需要提前知道键的情况。
下面是另一种适用于简单和更复杂用例的方法,它在运行时是可切换的
data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)
现在,这不仅允许在匹配键或值时使用更复杂的逻辑,而且也更容易测试,并且可以在运行时交换匹配逻辑。
解决原问题:
def some_method(hash, matcher)
hash.select(&matcher).values.join('\t')
end
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait"
some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string"
这是一条解决完整原始问题的直线:
params.select { |k,_| k[/choice/]}.values.join('\t')
但是上面的大多数解决方案都是使用slice或简单的regexp来解决需要提前知道键的情况。
下面是另一种适用于简单和更复杂用例的方法,它在运行时是可切换的
data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)
现在,这不仅允许在匹配键或值时使用更复杂的逻辑,而且也更容易测试,并且可以在运行时交换匹配逻辑。
解决原问题:
def some_method(hash, matcher)
hash.select(&matcher).values.join('\t')
end
params = { :irrelevant => "A String",
:choice1 => "Oh look, another one",
:choice2 => "Even more strings",
:choice3 => "But wait",
:irrelevant2 => "The last string" }
some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait"
some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string"
在Ruby中,hash# select是一个正确的选项。如果你使用Rails,你可以使用Hash#slice和Hash#slice!例:(rails 3.2.13)
h1 = {:a => 1, :b => 2, :c => 3, :d => 4}
h1.slice(:a, :b) # return {:a=>1, :b=>2}, but h1 is not changed
h2 = h1.slice!(:a, :b) # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4}
把它放到初始化式中
class Hash
def filter(*args)
return nil if args.try(:empty?)
if args.size == 1
args[0] = args[0].to_s if args[0].is_a?(Symbol)
self.select {|key| key.to_s.match(args.first) }
else
self.select {|key| args.include?(key)}
end
end
end
然后你就可以
{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # => {a: "1", b: "b"}
or
{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/) # => {a: "1"}
关于奖金问题:
If you have output from #select method like this (list of 2-element arrays):
[[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "But wait"]]
then simply take this result and execute:
filtered_params.join("\t")
# or if you want only values instead of pairs key-value
filtered_params.map(&:last).join("\t")
If you have output from #delete_if method like this (hash):
{:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}
then:
filtered_params.to_a.join("\t")
# or
filtered_params.values.join("\t")