如何根据数组处理结果为变量赋值



对于这样一个微不足道的任务,有更好的解决方案吗?

给定一个字符串数组,如下所示:

roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]

要根据角色值id值提取角色值,我使用以下正则表达式来匹配它:

r =~ /id=Admin/

愚蠢的简单解决方案只是迭代roles数组,分配匹配的值并返回如下:

role = nil
roles.each do |r|
role = 'admin' if r =~ /id=Admin/
role = 'national' if r =~ /id=National/
role = 'local' if r =~ /id=Local/
end
role

有没有更好的解决方案?

您可以定义一个正则表达式来同时匹配多个角色。这是一个简单的:

/id=(Admin|National|Local)/

括号充当角色名称的捕获组。您可能希望添加锚点,例如仅匹配每行中的第一个id=value对。或者,如果这些值可能不明确,请确保匹配整个值,而不仅仅是开头。

然后可以将模式传递给返回匹配行的grep

roles.grep(/id=(Admin|National|Local)/)
#=> ["id=Admin,id=YOYO,id=custom"]

将块传递给grep允许我们转换匹配项:($1是指第一个捕获组(

roles.grep(/id=(Admin|National|Local)/) { $1.downcase }
#=> ["admin"]

要获取first角色,请执行以下操作:

roles.grep(/id=(Admin|National|Local)/) { $1.downcase }.first
#=> "admin"

如果您的数组很大,您可以使用lazy枚举器,该枚举器将在第一次匹配后停止遍历:

roles.lazy.grep(/id=(Admin|National|Local)/) { $1.downcase }.first
#=> "admin"

我认为显而易见的方法是简单地解析整个角色数组:

roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]
user_roles = roles.join(',').split(',').map { |r| r.split('=', 2).last.downcase }

user_roles变成:

["accountant", "toto", "client", "admin", "yoyo", "custom", "cdi", "sc"]

现在,您可以简单地执行以下操作:

user_roles.include?('admin')

或者查找任何"管理员"、"国家"、"本地"事件:

# array1 AND array2, finds the elements that occur in both:
> %w(admin national local) & user_roles
=> ["admin"]

或者,也许只是要找出用户是否具有以下任何角色:

# When there are no matching elements, it will return an empty array
> (%w(admin national local) & user_roles).empty?
=> false 
> (["megaboss", "superadmin"] & user_roles).empty?
=> true 

这是一个更完整的示例,其中包含常量和方法以及所有!

SUPERVISOR_ROLES = %w(admin national local)
def is_supervisor?(roles)
!(SUPERVISOR_ROLES & roles).empty?
end
def parse_roles(raw_array)
raw_array.flat_map { |r| r.split(',').map { |r| r.split('=', 2).last.downcase } }
end
roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]
raise "you no boss :(" unless is_supervisor?(parse_roles(roles))

当然,如果数据集很大,这可能效率低下,但比执行这样的正则表达式更干净,甚至更安全,例如,有人可以创建一个名为AdminHack的角色,该角色仍将与/id=Admin/正则表达式匹配,并且如果您想出于其他目的检查其他角色,通过编写这样的通用角色解析器可能会变得有用。

(是的,显然这个解决方案创建了大量的中间数组和其他 insta 丢弃的对象,并且有足够的优化空间(

如果我们想从一组牢房中找到一个特定的间谍,我们只需要从所有牢房中收集所有间谍,并按顺序检查他们,直到找到罪魁祸首。

这里的等效方法是从给定数组中join字符串以形成单个字符串,然后在该字符串中搜索给定的子字符串:

str = roles.join(' ').downcase
#=> "id=accountant,id=toto,id=client id=admin,id=yoyo,id=custom id=cdi,id=sc"

join的参数可以是空格、换行符、逗号或其他几个字符串中的任何一个(我用了一个空格(。

然后,我们简单地使用方法 String#[] 和正则表达式查找匹配项:

r = /
(?<=id=)                 # match 'id=' in a positive lookbehind
(?:admin|national|local) # match 'admin', 'national' or 'local'
(?!w)                   # do not match a word character (negative lookahead)
/x                       # free-spacing regex definition mode

在正常(非自由间距模式(下,这是:

/(?<=id=)(?:admin|national|local)(?!w)/

'id=',处于积极的回望状态,不包括在比赛中。负前瞻(?!w)确保匹配项不会紧跟单词字符。例如,这阻止了"管理"一词的匹配。

我们现在只需提取匹配项,如果有的话:

str[r] #=> "admin"

如果没有比赛,nil就会被退回。

我们本可以在最后缩小大小写:

str = roles.join(' ')
str[/(?<=id=)([aA]dmin|[nN]ational|[lL]ocal)(?!w)/i].downcase

我喜欢Stefen 的回答,但不喜欢如果角色列表真的很大,它可以在退出grep之前运行很长时间来抓取id值。我也不喜欢这种模式,因为它没有锚定到搜索字符串的开头,迫使引擎做更多的工作。

我宁愿看到代码在第一次点击时停止,所以这是第一次尝试:

roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]
found_role = nil
roles.each do |i|
r = i[/^id=(Admin|National|Local)/]
if r
found_role = r.downcase 
break
end
end
found_role # => "id=admin"

想到这里,我一直在唠叨我太啰嗦了,所以这个突然冒出来了:

roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]
roles.find { |i| i[/^id=(Admin|National|Local)/] }.downcase[/^(id=w+),/, 1]
# => "id=admin"

分解一下,以下是亮点:

  • i[/^id=(Admin|National|Local)/]返回匹配的字符串"id=Admin..."并退出循环。
  • downcase[/^(id=w+),/, 1]抓住第一对并归还。

然后,和我一样肛门,我想downcase也会做太多的工作,所以发生了这样的事情:

roles.find { |i| i[/^id=(Admin|National|Local)/] }[/^(id=w+),/, 1].downcase

这是非常神秘的Ruby,我们真的不应该以这种方式编写代码,但我曾经写过C和Perl,所以对我来说似乎是合理的。

有趣的部分:

require 'fruity'
roles = [
"id=Accountant,id=TOTO,id=client",
"id=Admin,id=YOYO,id=custom",
"id=CDI,id=SC"
]
compare do
numero_uno {
found_role = nil
roles.each do |i|
r = i[/^id=(Admin|National|Local)/]
if r
found_role = r.downcase 
break
end
end
found_role
}
numero_dos { roles.find { |i| i[/^id=(Admin|National|Local)/] }.downcase[/^(id=w+),/, 1] }
numero_tres { roles.find { |i| i[/^id=(Admin|National|Local)/] }[/^(id=w+),/, 1].downcase }
end
# >> Running each test 2048 times. Test will take about 1 second.
# >> numero_uno is similar to numero_tres
# >> numero_tres is faster than numero_dos by 10.000000000000009% ± 10.0%

最新更新