簡體   English   中英

Ruby:在hash中拆分字符串

[英]Ruby: split string in hash

我有一個字符串

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

預期結果:我想像這樣將其拆分為 hash:

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

有人可以指導我創建匹配 hash 的方向嗎?

當輸入始終遵循相同的模式時,我會使用String#scan和 Regexp 來提取重要值。

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"]]

然后可以將這些嵌套的值數組轉換為 hash,如下所示:

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]}

以下允許任意數量的比賽,並且每場比賽都有任意數量的相關距離(在下面的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有時是String#scan的方便替代品。

作為gsub參數的正則表達式可以用自由間距模式表示,以使其自記錄。

/
(          # 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

請注意,在自由間距模式中,作為表達式一部分的空格必須受到保護。 有多種方法可以做到這一點。 我已將每個空格括在一個字符 class ( [ ] ) 中。


在上面的示例中,我們計算了以下枚舉器。

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|

你可以寫這段代碼

輸入

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

Output

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

你能試試下面的代碼嗎?

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: 我覺得你應該自己動手來提升自己

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2025 STACKOOM.COM