[英]How do I make these string substitutions using hash keys in Ruby?
我有一堆用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"
}
請注意,密鑰顯示的順序不一致,因此,我寧願不更改JSON。
這是我的測試代碼,顯示了到目前為止我已經嘗試過的內容:
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
我現在正在使用Braindead解決方案,它可以產生預期的輸出:
{"KEY1"=>"foo", "KEY2"=>"bar", "URL"=>"https://bar.com/foo", "IMPORTANT_THING"=>"repos/foo", "NOTE"=>"This thing is foobared", "PYTHON_ONLY_THING"=>"{}/test/{}.py"}
但我想使其更加靈活和可維護。 第一個“更好”的解決方案顯然會引發錯誤,因為slice本身在'\\ 0'上而不是在匹配項上運行,而且我不確定它會多次匹配。 當前未注釋的解決方案不起作用,因為第二部分似乎一次只能處理一個字母,而不是像我期望的那樣匹配每個匹配項,因此它只是刪除了花括號中的內容。 更糟糕的是,它會刪除PYTHON_ONLY_THING中外部括號之間的所有內容,這是不好的。
我想如果這行得通,我需要同時更改我的正則表達式和Ruby代碼,但是我不確定在哪里尋求更多幫助。 也許gsub不是適合此工作的工具。 有任何想法嗎?
我在Linux x86_64上使用Ruby 2.3.7。
使用帶有初始哈希值的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"}
從Ruby 2.4(或使用Rails)開始,可以使用Hash#transform_values
使其更簡單。
如果您不喜歡第二個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
這是一個可能的解決方案:
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
scan
,它將替換所有匹配項,而不僅僅是第一個匹配項。 [[^}]+
而不是.*
,意味着您在比賽的這一部分不會“吞咽”太多。 例如,如果輸入包含"{FOO} bar {BAZ}"
,那么您希望該模式僅捕獲FOO
和BAZ
,而不捕獲FOO} bar {BAZ
。 flatten
,是拒絕捕獲組之外的內容(例如,在這種情況下為{
和}
字符)的簡便方法。 (與使用slice(1,-2)
類的索引相比,這僅會使代碼的保密性降低一點! my_config.keys.include?(placeholder)
檢查這是否實際上是。 一個已知的值,所以您不要用nil
代替東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.