正则表达式将ruby字符串拆分为多个哈希值

时间:2017-05-17 11:16:20

标签: ruby-on-rails ruby regex postgresql

我在Postgresql数据库的jsonb列中有一个像这样的字符串:

"{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"

现在我想使用正则表达式使它看起来像这样:

[{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"}, 
{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},
{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}
]

我正在尝试使用正则表达式拆分的方法来完成任务。但是,我仍然无法弄清楚如何只分割字符串中右大括号后面的逗号。 我能想到的就是用ruby中的扫描方法测试它,如下所示:

test =  "{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"
result = test.scan(/\"},/)
=> result = ["\"},", "\"},"]

看起来我正走在正确的轨道上,但我被困在这里。任何建议将不胜感激。

更新 感谢Tinman先生您的深思熟虑的解释。我将数据库更改回hstore,并在将数据传递到后端之前使用JSON.stringify。在这里和那里有一些扭曲,我终于得到你想要的哈希,如你所说:

{"monday"=>{"start_time"=>9, "end_time"=>10.5}, "tuesday"=>{"start_time"=>8.5, "end_time"=>10}, "wednesday"=>{"start_time"=>8.5, "end_time"=>10}}

。但是,在将其保存到数据库之后,散列的所有值都被序列化为字符串:

{
       "monday" => "{\"start_time\"=>9, \"end_time\"=>10.5}",
      "tuesday" => "{\"start_time\"=>7, \"end_time\"=>8.5}",
    "wednesday" => "{\"start_time\"=>7, \"end_time\"=>8.5}"
}

我知道我可以使用eval将它们转换回哈希值。但是,有没有什么方法可以将这些值保存为数据库中的哈希值,或者因为Hstore机制而无法将其保存?

1 个答案:

答案 0 :(得分:1)

看起来您尝试创建自己的JSON序列化程序,而不是使用Ruby附带的序列化程序。或者,也许你没有意识到JSON是一个东西,想要存储哈希数组,并试图发明自己的。无论哪种方式,它都不是一条好的道路。

这是你的定义,在被检索后仍然是一个字符串。它似乎是哈希数组的元素,没有周围的数组[]

foo = "{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"

它仍然是一个字符串:

foo.class # => String

可以将它重新组合成一个可以评估并转回哈希数组的形式,但它并不简单,也不干净,不方便或优雅。

我怀疑你的原始对象是这样的:

foo = {"monday"=>{:start_time=>9.0, :end_time=>10.5}},{"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},{"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}
# => [{"monday"=>{:start_time=>9.0, :end_time=>10.5}},
#     {"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},
#     {"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}]

这是一个数组:

foo.class # => Array

现在它是一个数组,因为你有多个用逗号分隔的Hash元素(,),Ruby在分配给单个变量时会将其解释为数组。这将是一个视觉上不那么令人困惑的例子:

foo = {a: 1}, {a: 2} # => [{:a=>1}, {:a=>2}]
foo.class  # => Array
foo.first.class # => Hash

回到foo中的哈希数组:从那开始,JSON类很容易将其序列化为可以存储在数据库中的字符串,然后检索并重新分解为Ruby对象:

require 'json'
foo.to_json

foo.to_json会产生一个类似于以下内容的序列化字符串:

[{"monday":{"start_time":9.0,"end_time":10.5}},{"tuesday":{"start_time":8.5,"end_time":10.0}},{"wednesday":{"start_time":8.5,"end_time":10.0}}]

并且,给定该字符串,JSON可以重建对象:

bar = JSON.parse(foo.to_json)
# => [{"monday"=>{"start_time"=>9.0, "end_time"=>10.5}},
#     {"tuesday"=>{"start_time"=>8.5, "end_time"=>10.0}},
#     {"wednesday"=>{"start_time"=>8.5, "end_time"=>10.0}}]

请注意,不必使用parseto_json。该类足够聪明,可以识别参数是String,Array还是Hash,并分别解析或序列化:

JSON[JSON[foo]]
# => [{"monday"=>{"start_time"=>9.0, "end_time"=>10.5}},
#     {"tuesday"=>{"start_time"=>8.5, "end_time"=>10.0}},
#     {"wednesday"=>{"start_time"=>8.5, "end_time"=>10.0}}]

此时,您已准备好将值存储到数据库中的String类型字段中,然后稍后检索它们并重复使用它们。

如果您希望搜索或操作那些存储的JSON字符串,我建议您重新思考。虽然现代DBM可以在JSON内部进行搜索并生成它,但最好还是有一个单独的键和值表。它更快,更灵活。

最后针对您的问题,如何转换字符串以便获取键和值:

foo = "{\"monday\"=>\"{:start_time=>9.0, :end_time=>10.5}\"},{\"tuesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"},{\"wednesday\"=>\"{:start_time=>8.5, :end_time=>10.0}\"}"
bar = eval('[' + foo.gsub(/"([{}])/, '\1') + ']')
bar
# => [{"monday"=>{:start_time=>9.0, :end_time=>10.5}},
#     {"tuesday"=>{:start_time=>8.5, :end_time=>10.0}},
#     {"wednesday"=>{:start_time=>8.5, :end_time=>10.0}}]

bar.map { |h| [h.keys, h.values.first.keys] }
# => [[["monday"], [:start_time, :end_time]],
#     [["tuesday"], [:start_time, :end_time]],
#     [["wednesday"], [:start_time, :end_time]]]

这暗示哈希数组也不是正确的结构。一个简单的哈希就足够了:

foo = {
  "monday"=>{:start_time=>9.0, :end_time=>10.5},
  "tuesday"=>{:start_time=>8.5, :end_time=>10.0},
  "wednesday"=>{:start_time=>8.5, :end_time=>10.0}
}

require 'json'
bar = JSON[JSON[foo]] # imitate a round-trip to/from the DB
bar.keys # => ["monday", "tuesday", "wednesday"]
bar.values # => [{"start_time"=>9.0, "end_time"=>10.5}, {"start_time"=>8.5, "end_time"=>10.0}, {"start_time"=>8.5, "end_time"=>10.0}]