简体   繁体   English

如何在Ruby中使用哈希键进行这些字符串替换?

[英]How do I make these string substitutions using hash keys in Ruby?

I have a bunch of JSON files, processed in both Python and Ruby, that look something like this: 我有一堆用Python和Ruby处理过的JSON文件,看起来像这样:

{
    "KEY1": "foo",
    "KEY2": "bar",

    "URL": "https://{KEY2}.com/{KEY1}",
    "IMPORTANT_THING": "repos/{KEY1}",
    "NOTE": "This thing is {KEY1}{KEY2}ed",
    "PYTHON_ONLY_THING": "{}/test/{}.py"
}

Note that the order that the keys will show up is not consistent, and I'd rather not change the JSON. 请注意,密钥显示的顺序不一致,因此,我宁愿不更改JSON。

Here's my test code showing what I've tried so far: 这是我的测试代码,显示了到目前为止我已经尝试过的内容:

my_config = {"KEY1"=>"foo",
             "KEY2"=>"bar",
             "URL"=>"https://{KEY2}.com/{KEY1}",
             "IMPORTANT_THING"=>"repos/{KEY1}",
             "NOTE"=>"This thing is {KEY1}{KEY2}ed",
             "PYTHON_ONLY_THING"=>"{}/test/{}.py"}

my_config.each_key do |key|
    # Braindead, hard-coded solution that works:
    # my_config[key].gsub!("{KEY1}", my_config["KEY1"])
    # my_config[key].gsub!("{KEY2}", my_config["KEY2"])

    # More flexible (if it would work):
    # my_config[key].gsub!(/{.*}/, my_config['\0'.slice(1,-2)])
    my_config[key].gsub!(/{.*}/) {|s| my_config[s.slice(1,-2)]}
end

puts my_config

I'm using the braindead solution for now, which produces the expected output: 我现在正在使用Braindead解决方案,它可以产生预期的输出:

{"KEY1"=>"foo", "KEY2"=>"bar", "URL"=>"https://bar.com/foo", "IMPORTANT_THING"=>"repos/foo", "NOTE"=>"This thing is foobared", "PYTHON_ONLY_THING"=>"{}/test/{}.py"}

But I want to make it more flexible and maintainable. 但我想使其更加灵活和可维护。 The first "better" solution throws an error apparently because slice operates on '\\0' itself and not the match, plus I'm not sure it would match more than once. 第一个“更好”的解决方案显然会引发错误,因为slice本身在'\\ 0'上而不是在匹配项上运行,而且我不确定它会多次匹配。 The currently uncommented solution doesn't work because the second part seems to operate on one letter at a time rather than each match like I expected, so it just removes the stuff in curly braces. 当前未注释的解决方案不起作用,因为第二部分似乎一次只能处理一个字母,而不是像我期望的那样匹配每个匹配项,因此它只是删除了花括号中的内容。 Worse, it removes everything between the outer braces in the PYTHON_ONLY_THING, which is no good. 更糟糕的是,它会删除PYTHON_ONLY_THING中外部括号之间的所有内容,这是不好的。

I figure I need to change both my regex and Ruby code if this is going to work, but I'm not sure where to look for more help. 我想如果这行得通,我需要同时更改我的正则表达式和Ruby代码,但是我不确定在哪里寻求更多帮助。 Or perhaps gsub isn't the right tool for this job. 也许gsub不是适合此工作的工具。 Any ideas? 有任何想法吗?

I am using Ruby 2.3.7 on Linux x86_64. 我在Linux x86_64上使用Ruby 2.3.7。

Use String#gsub with an initial hash for replacements: 使用带有初始哈希值的String#gsub进行替换:

my_config.map do |k, v|
  [
    k,
    v.gsub(/(?<={)[^}]+(?=})/, my_config).gsub(/{(?!})|(?<!{)}/, '')
  ]
end.to_h
#⇒ {"KEY1"=>"foo",
#   "KEY2"=>"bar",
#   "URL"=>"https://bar.com/foo",
#   "IMPORTANT_THING"=>"repos/foo",
#   "NOTE"=>"This thing is foobared",
#   "PYTHON_ONLY_THING"=>"{}/test/{}.py"}

Starting with Ruby 2.4 (or using Rails) it might be done simpler using Hash#transform_values . 从Ruby 2.4(或使用Rails)开始,可以使用Hash#transform_values使其更简单。

If you dislike the second gsubbing, transform the hash upfront: 如果您不喜欢第二个gsubbing,请预先转换哈希值:

my_substs = my_config.map { |k, v| ["{#{k}}", v] }.to_h
my_config.map do |k, v|
  [k, v.gsub(/{[^}]+}/, my_substs)]
end.to_h

Here's a possible solution: 这是一个可能的解决方案:

my_config = {"KEY1"=>"foo",
             "KEY2"=>"bar",
             "URL"=>"https://{KEY2}.com/{KEY1}",
             "IMPORTANT_THING"=>"repos/{KEY1}",
             "NOTE"=>"This thing is {KEY1}{KEY2}ed",
             "PYTHON_ONLY_THING"=>"{}/test/{}.py"}

my_config.each_key do |key|
  placeholders = my_config[key].scan(/{([^}]+)}/).flatten
  placeholders.each do |placeholder|
    my_config[key].gsub!("{#{placeholder}}", my_config[placeholder]) if my_config.keys.include?(placeholder)
  end
end

puts my_config
  • By using scan , this will substitute all matches, not just the first match. 通过使用scan ,它将替换所有匹配项,而不仅仅是第一个匹配项。
  • Using [[^}]+ in the regex, rather than .* , means you won't "swallow" too much in this part of the match. 在正则表达式中使用[[^}]+而不是.* ,意味着您在比赛的这一部分不会“吞咽”太多。 For example, if the input contains "{FOO} bar {BAZ}" , then you want that pattern to only capture FOO and BAZ , not FOO} bar {BAZ . 例如,如果输入包含"{FOO} bar {BAZ}" ,那么您希望该模式仅捕获FOOBAZ ,而不捕获FOO} bar {BAZ
  • Grouping the scan result, then calling flatten , is an easy way to reject what's outside the capture group, ie in this case the { and } characters. 对扫描结果进行分组,然后调用flatten ,是拒绝捕获组之外的内容(例如,在这种情况下为{}字符)的简便方法。 (This just makes the code a little less cryptic than using indexes like slice(1,-2) ! (与使用slice(1,-2)类的索引相比,这仅会使代码的保密性降低一点!
  • my_config.keys.include?(placeholder) checks whether this is actually . my_config.keys.include?(placeholder)检查这是否实际上是。 a known value, so you don't replace things with nil . 一个已知的值,所以您不要用nil代替东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM