给出哈希
PLATFORMS = {
:mac => /(mac)|(macintosh)/i,
:win => /(win)|(windows)/i,
:ipad => /(ipad)/i,
:iphone => /(iphone)/i,
:ipod => /(ipod)|(ipod touch)/i
}
我对返回哈希键感兴趣,其中值(在这种情况下是正则表达式)返回true。
因此,如果给我一串"windows"
,我应该返回密钥:win
。
到目前为止,我的尝试是:
current_platform = BrowserExperience::ExperienceKeeper::PLATFORMS.detect do |platform, regex|
regex.match(user_agent_obj.platform)
end[0]
返回[:win,/(win)|(windows)/i]
然而,这只返回一个数组,其中索引0返回我想要的键值。有更简单的方法吗?
答案 0 :(得分:4)
为什么不使用case
声明?这是一种更常见的方式:
strings = [
'this is a Windows box',
'Welcome to Macintosh',
'My music is on an iPod',
'My photos are on an iPod Touch',
'I read books on an iPad'
]
strings.each do |str|
os = case str
when /\b(?:mac|macintosh)\b/i
:mac
when /\b(?:win|windows)\b/i
:win
when /\b(?:ipad)\b/i
:ipad
when /\b(?:iphone)\b/i
:iphone
when /\b(?:ipod|ipod\ touch)\b/i
:ipod
end
os # => :win, :mac, :ipod, :ipod, :ipad
end
也可以这样做:
PLATFORMS = {
mac: /\b(?:mac|macintosh)\b/i,
win: /\b(?:win|windows)\b/i,
ipad: /\b(?:ipad)\b/i,
iphone: /\b(?:iphone)\b/i,
ipod: /\b(?:ipod|ipod\ touch)\b/i
}
strings.each do |str|
key = nil
PLATFORMS.each_pair do |k, v|
if str =~ v
key = k
break
end
end
key # => :win, :mac, :ipod, :ipod, :ipad
end
或者最好:
strings.each do |str|
PLATFORMS.find { |k, v| str =~ v }.first # => :win, :mac, :ipod, :ipod, :ipad
end
如果你使用哈希和正则表达式,那么你的模式会更简洁。 \b
是一个单词边界,我们告诉Regexp引擎是否匹配子字符串或整个单词:
'machine'[/(?:mac|macintosh)/i] # => "mac"
VS
'machine'[/\b(?:mac|macintosh)\b/i] # => nil
这里还有一点:
'mac'[/\b(?:mac|macintosh)\b/i] # => "mac"
'macintosh'[/\b(?:mac|macintosh)\b/i] # => "macintosh"
'win'[/\b(?:win|windows)\b/i] # => "win"
'windows'[/\b(?:win|windows)\b/i] # => "windows"
'ipad'[/\b(?:ipad)\b/i] # => "ipad"
'iphone'[/\b(?:iphone)\b/i] # => "iphone"
'ipod touch'[/\b(?:ipod|ipod\ touch)\b/i] # => "ipod"
我可能会做这样的事情来定义哈希:
require 'regexp_trie'
PLATFORMS = {
mac: ['mac', 'macintosh'],
win: ['win', 'windows'],
ipad: ['ipad'],
iphone: ['iphone'],
ipod: ['ipod', 'ipod touch']
}
然后,我将模式转换为更高效的模式:
PLATFORMS_RE = {}
PLATFORMS.each_pair do |k, v|
PLATFORMS_RE[k] = /\b(?:#{RegexpTrie.union(v).source})\b/i
end
导致:
PLATFORMS_RE
# => {:mac=>/\b(?:mac(?:intosh)?)\b/i,
# :win=>/\b(?:win(?:dows)?)\b/i,
# :ipad=>/\b(?:ipad)\b/i,
# :iphone=>/\b(?:iphone)\b/i,
# :ipod=>/\b(?:ipod(?:\ touch)?)\b/i}
然后像以前一样工作:
strings.each do |str|
PLATFORMS_RE.find { |k, v| str =~ v }.first # => :win, :mac, :ipod, :ipod, :ipad
end
答案 1 :(得分:3)
不是一个很大的改进,但你只能检测键而不是键值对
platform_to_regex = BrowserExperience::ExperienceKeeper::PLATFORMS
current_platform = platform_to_regex.keys.detect do |platform|
platform_to_regex[platform].match(user_agent_obj.platform)
end
答案 2 :(得分:2)
对于给定的哈希,我会写下以下内容。
def attempt_match(str)
PLATFORMS.keys.find { |k| str.match? PLATFORMS[k] }
end
attempt_match 'windows' #=> :win
attempt_match 'DOS' #=> nil
DRYer方法是使用更简单的哈希并动态构造正则表达式(它们都非常相似)。这可以实现如下 1 。
def attempt_match(h, s)
sdn = s.downcase
h.find { |k, _| sdn.match?(/\b#{k}\b/) }&.last
end
h = {"mac"=>:mac, "windows"=>:win, "ipad"=>:ipad, "iphone"=>:iphone, "ipod"=>:ipod}
attempt_match(h, 'windows') #=> :win
attempt_match(h, 'DOD') #=> :nil
&
中的 &.last
是Ruby的safe navigation operator,在v2.3中引入。
修改哈希值(例如,添加"linux"=>:linux
)是一件简单的事情,而不必担心正确使用正则表达式。
1观察如果“ipod touch”匹配,那么“ipod”也是如此。因此,前者是多余的。
答案 3 :(得分:1)
您可以使用select
并使用match
检查哪些(或哪些)键值与传递的字符串匹配:
PLATFORMS = {
mac: /(mac)|(macintosh)/i,
win: /(win)|(windows)/i,
ipad: /(ipad)/i,
iphone: /(iphone)/i,
ipod: /(ipod)|(ipod touch)/i
}
def match_regex(string)
PLATFORMS.select{|_,v| string.match(v)}.keys[0]
end
p match_regex('windows')
# => :win