Ruby:拆分散列中的字符串

i34xakig  于 2023-02-03  发布在  Ruby
关注(0)|答案(4)|浏览(101)

我有一根绳子
str = "race_1: 650m, 215m, 265m, 315m\r\nrace_2: 165m, 215m, 265m, 315m."
预期结果:我想把它分解成这样的散列:

hash = {
   race_1 => [650, 215, 265, 315],
   race_2 => [165, 215, 265, 315]
}

有人能告诉我如何创建匹配的散列吗?

iq0todco

iq0todco1#

当输入总是遵循相同的模式时,我将使用带有Regexp的String#scan来提取有效值。

string = "race_1: 650m, 215m, 265m, 315m\r\nrace_2: 165m, 215m, 265m, 315m."
regexp = /(race_\d+).*?(\d+(?=m)).*?(\d+(?=m)).*?(\d+(?=m)).*?(\d+(?=m))/

string.scan(regexp)
#=> [["race_1", "650", "215", "265", "315"], ["race_2", "165", "215", "265", "315"]]

这些嵌套的数组值可以转换为如下的哈希值:

string.scan(regexp).to_h { |values| [values[0], values[1..-1]] }
#=> {"race_1"=>["650", "215", "265", "315"], "race_2"=>["165", "215", "265", "315"]}

因为你希望数组中的数字是整数:

string.scan(regexp).to_h { |values| [values[0], values[1..-1].map(&:to_i)] }
#=> {"race_1"=>[650, 215, 265, 315], "race_2"=>[165, 215, 265, 315]}
lfapxunr

lfapxunr2#

您可以编写以下代码
输入

str = "race_1: 650m, 215m, 265m, 315m\r\nrace_2: 165m, 215m, 265m, 315m."

代码
用冒号拆分代码:并替换末尾的m

hash = str.scan(/(race_\d+): (.*)/).each_with_object({}) do |(race, distances), hash|
  hash["#{race}"] = distances.split(', ').map { |d| d.sub(/m$/, '').to_i }
end
p hash

产出

{"race_1"=>[650, 215, 265, 315], "race_2"=>[165, 215, 265, 315]}
yuvru6vn

yuvru6vn3#

下面允许任意数量的比赛,每个比赛可以有任意数量的相关距离(下面的str中有四个)。

str = "race_1: 650m, 215m, 265m, 315m\r\nrace_2: 165m, 215m, 265m, 315m"
str.gsub(/(\w+): ((?:\d+m, *)*\d+)/).with_object({}) do |_s,h|
  h[$1] = $2.split(',').map(&:to_i)
end
  #=> {"race_1"=>[650, 215, 265, 315],
  #    "race_2"=>[165, 215, 265, 315]}

它使用了一种很少使用的String #gsub的一种(并且被大大低估了)形式,它只接受一个参数,但不接受块,并返回一个枚举数。枚举数只生成gsub参数的匹配项,因此与字符串替换无关。当scan '的参数是包含一个或多个捕获组的正则表达式。
作为gsub参数的正则表达式可以用 * free-spacing * 模式表示,以使其自文档化。

/
(          # begin capture group 1
  \w+      # match >= 1 word characters
)          # end capture group 1
:          # match a colon
[ ]        # match a space
(          # begin capture group 2
  (?:      # begin non-capture group
    \d+    # match >= 1 digits
    m,[ ]* # match "m," followed by >= 0 spaces
  )        # end non-capture group
  *        # execute preceding non-capture group >= 0 times
  \d+      # match >= 1 digits
)          # end capture group 2
/x         # invoke free-spacing regex definition mode

注意,在自由空格模式下,表达式中的空格必须被保护起来,有很多方法可以做到这一点,我把每个空格放在一个字符类([ ])中。
在上面的例子中,我们计算下面的枚举数。

enum = str.gsub(/(\w+): ((?:\d+m, *)*\d+)/)
  #=> #<Enumerator: "race_1: 650m, 215m, 265m, 315m\r\n
  #     race_2: 165m, 215m, 265m, 315m":
  #     gsub(/(\w+): ((?:\d+m, *)*\d+)/)>

它将生成的元素如下所示。

enum.next
  #=> "race_1: 650m, 215m, 265m, 315"
enum.next
  #=> "race_2: 165m, 215m, 265m, 315"
enum.next
  #=> StopIteration: iteration reached an end

还要注意

arr = "650m, 215m, 265m, 315".split(',')
  #=> ["650m", " 215m", " 265m", " 315"]

arr.map(&:to_i)
  #=> [650, 215, 265, 315]

这种方法的一种变体是

rgx = /\w+: (?:\d+m, *)*\d+/

str.gsub(rgx).with_object({}) do |s,h|
  key, value = s.split(':')
  h[key] = value.split(',').map(&:to_i)
end
  #=> {"race_1"=>[650, 215, 265, 315],
  #    "race_2"=>[165, 215, 265, 315]}

由于正则表达式现在没有捕获组,因此将第一行替换为

str.scan(rgx).each_with_object({}) do |s,h|
roejwanj

roejwanj4#

你能试试下面的代码吗?

str = "race_1: 650m, 215m, 265m, 315m\r\nrace_2: 165m, 215m, 265m, 315m."
rows = str.delete('.').split("\r\n") # => ["race_1: 650m, 215m, 265m, 315m", "race_2: 165m, 215m, 265m, 315m"] 
hash_result = {}
rows.each do |row|
  key = row.split(':').first # => race_1
  value = row.split(':').last.split('m, ').map(&:to_i) # => [650, 215, 265, 315]
  hash_result[key.to_sym] = value
end
# hash_result = {:race_1=>[650, 215, 265, 315], :race_2=>[165, 215, 265, 315]}

p/s:我认为你应该自己做,以提高自己

相关问题