我有以下復雜的哈希結構(其中很多),如下所示:
hash = {"en-us"=>
{:learn_more=>"Learn more",
:non_apple_desktop=>"To redeem, open this link.",
:value_prop_safety=>"",
:storage_size=>
{:apple_tv_1_month_tms=>
{:cta=>"Offer",
:details=>"Get a 1-month subscription!.",
:disclaimer=>"This offer expires on December 10, 2021.",
:header=>"Watch The Morning Show ",
:normal_price=>"$2.99"}
}
}
}
我想做的是有一個函式,它將根據哈希結構產生以下字串輸出:
en-us.storage_size.apple_tv_1_month_tms.cta: Offer
en-us.storage_size.apple_tv_1_month_tms.details: Get a 1-month subscription!.
en-us.storage_size.apple_tv_1_month_tms.disclaimer: This offer expires on December 10, 2021.
en-us.storage_size.apple_tv_1_month_tms.header: Watch The Morning Show
en-us.storage_size.apple_tv_1_month_tms.normal_price: $2.99
en-us.learn_more: Learn more
en-us.non_apple_desktop: To redeem, open this link.
en-us.value_prop_safety:
我從另一個stackoverflow問題中使用了這個遞回函式,它在某種程度上實作了這一點:
def show(hash, current_path = '')
string = ''
hash.each do |k,v|
if v.respond_to?(:each)
current_path = "#{k}."
show v, current_path
else
string = "#{current_path}#{k}: #{v}" "\n"
end
end
string
end
如果我puts在方法的主體中放置一個陳述句,我可以看到所需的結果,但它是逐行的。我需要的是獲取完整的輸出,因為我會將它寫入 csv。我似乎無法讓它以目前的形式作業。
如果我將 aputs show(hash)放入我的 irb,那么我將不會得到任何輸出。總而言之,我正在嘗試執行以下操作:
show(hash) ----->
en-us.storage_size.apple_tv_1_month_tms.cta: Offer
en-us.storage_size.apple_tv_1_month_tms.details: Get a 1-month subscription!.
en-us.storage_size.apple_tv_1_month_tms.disclaimer: This offer expires on December 10, 2021.
en-us.storage_size.apple_tv_1_month_tms.header: Watch The Morning Show
en-us.storage_size.apple_tv_1_month_tms.normal_price: $2.99
en-us.learn_more: Learn more
en-us.non_apple_desktop: To redeem, open this link.
en-us.value_prop_safety:
這應該是一個簡單的遞回任務,但我無法確定我到底做錯了什么。幫助將不勝感激。謝謝你。
uj5u.com熱心網友回復:
i18n在我看來,使用gem更方便
它有I18n::Backend::Flatten#flatten_translations方法。它接收翻譯的散列(其中鍵是語言環境,值是另一個散列)并回傳所有翻譯展平的散列,就像您需要的那樣
只需將生成的哈希轉換為字串即可
require "i18n/backend/flatten"
include I18n::Backend::Flatten
locale_hash = {"en-us"=>
{:learn_more=>"Learn more",
:non_apple_desktop=>"To redeem, open this link.",
:value_prop_safety=>"",
:storage_size=>
{:apple_tv_1_month_tms=>
{:cta=>"Offer",
:details=>"Get a 1-month subscription!.",
:disclaimer=>"This offer expires on December 10, 2021.",
:header=>"Watch The Morning Show ",
:normal_price=>"$2.99"}
}
}
}
puts flatten_translations(nil, locale_hash, nil, nil).
map { |k, v| "#{k}: #{v}" }.
join("\n")
# will print
# en-us.learn_more: Learn more
# en-us.non_apple_desktop: To redeem, open this link.
# en-us.value_prop_safety:
# en-us.storage_size.apple_tv_1_month_tms.cta: Offer
# en-us.storage_size.apple_tv_1_month_tms.details: Get a 1-month subscription!.
# en-us.storage_size.apple_tv_1_month_tms.disclaimer: This offer expires on December 10, 2021.
# en-us.storage_size.apple_tv_1_month_tms.header: Watch The Morning Show
# en-us.storage_size.apple_tv_1_month_tms.normal_price: $2.99
當然最好不要包含在main物件中,而是包含在某些服務物件中
require "i18n/backend/flatten"
class StringifyLocaleHash
include I18n::Backend::Flatten
attr_reader :locale_hash
def self.call(locale_hash)
new(locale_hash).call
end
def initialize(locale_hash)
@locale_hash = locale_hash
end
def call
flatten_translations(nil, locale_hash, nil, nil).
map { |k, v| "#{k}: #{v}" }.
join("\n")
end
end
# To get string call such way
StringifyLocaleHash.(locale_hash)
uj5u.com熱心網友回復:
對方法的考慮
首先,一些問題:
- Ruby 不是 JavaScript。哈希沒有屬性,因此除非您在事后做大量作業來拆分和連接資料的路徑,否則鏈接點不會起作用。果汁可能不值得擠壓。
en-us不是有效的 LANG 值。您可以將其用作鍵,但如果語言環境很重要,您可能想要技術上更準確的東西en_US或en_US.UTF-8作為鍵。
其次,如果您已經知道JSON 的結構,那么您應該將每個 JSON 鍵設定為列值并填充它。這可能要容易得多。
第三,雖然 Ruby 可以進行遞回,但它的大部分內置方法都是為迭代而設計的。對于很多事情來說,迭代更快更容易,所以除非你出于其他原因這樣做,否則只需迭代你需要的資訊。
從具有預定義格式的結構化資料構建 CSV
最后,如果您只想將資料填充到 CSV 或創建字串,那么您可能只想提取資料,然后使用您已經知道的資訊進行處理。例如:
require "hashie"
# assume your hash is already defined
offer_values =
hash.extend(Hashie::Extensions::DeepFind)
.deep_find(:apple_tv_1_month_tms).values
#=>
# ["Offer",
# "Get a 1-month subscription!.",
# "This offer expires on December 10, 2021.",
# "Watch The Morning Show ",
# "$2.99"]
要獲取頂級鍵的值,您可以這樣做:
linking = hash["en-us"].keys[0..-2].map { hash.dig "en-us", _1 }
#=> ["Learn more", "To redeem, open this link.", ""]
通過更多的作業,您可以使用 ActiveSupport 或通過使用帶有查找模式的 Ruby 3.1 模式匹配來做類似的事情,但這也適用于較舊的 Ruby 版本。唯一的缺點是它依賴于 Hashie gem 的 Hashie::DeepFind,但這是 Ruby 沒有簡單的內置方法來查找模式匹配以外的嵌套結構的情況之一,我認為這是值得的.
您仍然需要做一些作業才能將提取的資料轉換為您想要的確切格式,但這會將您所追求的所有值轉換為一對變數,offer_values和linking。然后,您可以對它們做任何您想做的事情。
也可以看看
除了 Hashie::DeepFind 和 Ruby 3 模式匹配之外,您還有許多查詢語言可能有助于從 JSON 中提取資料,其中一些具有 Ruby gems 或可以輕松集成到您的 Ruby 應用程式中:
- JSON路徑
- https://github.com/joshbuddy/jsonpath
- https://www.ruby-toolbox.com/projects/remap
- XPath 3.1
- XQuery 3.1
uj5u.com熱心網友回復:
要回答您的字面問題:
show v, current_path
應該
string = show v, current_path
否則你會丟失遞回呼叫所做的任何作業。
請注意,a = b替換a為新字串a b。它沒有改變a。因此,保留 的回傳值show至關重要。
如果你想依賴可變字串,這里有一個可變字串版本;但是,請注意它可能并不總是有效,因為字串不變性是添加到 Ruby 中的一個選項。如果frozen_string_literals打開,可變連接運算子<<將失敗。在可變字串版本中,您不能string在每次迭代中進行初始化,因為您將丟棄呼叫者所做的作業;因此它作為另一個引數傳遞,并且僅在其初始呼叫時由默認值初始化。
def show(hash, current_path = '', string = '')
hash.each do |k,v|
if v.respond_to?(:each)
current_path = "#{k}."
show v, current_path, string
else
string << "#{current_path}#{k}: #{v}" "\n"
end
end
string
end
uj5u.com熱心網友回復:
這是一個相對簡單的遞回,因為每個值都是字串或散列。可以寫成如下。
def recurse(h)
h.each_with_object([]) do |(k,v),a|
s = k.to_s
case v
when Hash
recurse(v).each { |ss| a << s '.' ss }
else
a << s ': ' v
end
end
end
recurse hash
#=> ["en-us.learn_more: Learn more",
# "en-us.non_apple_desktop: To redeem, open this link.",
# "en-us.value_prop_safety: ",
# "en-us.storage_size.apple_tv_1_month_tms.cta: Offer",
# "en-us.storage_size.apple_tv_1_month_tms.details: Get a 1-month subscription!.",
# "en-us.storage_size.apple_tv_1_month_tms.disclaimer: This offer expires on December 10, 2021.",
# "en-us.storage_size.apple_tv_1_month_tms.header: Watch The Morning Show ",
# "en-us.storage_size.apple_tv_1_month_tms.normal_price: $2.99"]
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/510028.html
標籤:红宝石递归
