Techioz Blog

ハッシュから再帰的に連結文字列を生成するにはどうすればよいですか?

概要

次のような複雑なハッシュ構造があります (数多くあります)。

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: 

私はこれをある程度達成する別のスタックオーバーフローの質問からこの再帰関数を使用しました。

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 ステートメントを配置すると、目的の結果が 1 行ずつ表示されます。必要なのは、出力を CSV に書き込むため、出力全体を取得することです。現在のバージョンでは機能しないようです。

irb に puts show(hash) を配置した場合、出力は得られません。要約すると、私は次のことをしようとしています:

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: 

これは簡単な再帰的なタスクのはずですが、正確にどこが間違っているのかを特定できません。ご協力をよろしくお願いいたします。ありがとう。

解決策

私の意見では、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

もちろん、メインオブジェクトではなく、サービスオブジェクトに含める方が良いです

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)