質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.22%

RubyでYAMLから値を取り出す際、YAMLに値が追加されても変更が必要ないコードを作成したい

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,642

fuzisan

score 38

伺いたいこと

作成したコードでは、下記YAMLにさらに深いrevelのコードが追加された場合、書き換える必要があります。
YAMLにさらに深いrevelのコードが追加されても書き換える必要がないコードの書き方を教えていただきたいです。

 やりたいこと

下記のYAMLから値を取り出して出力するプログラムを作りたい。

revel1-1: foo
revel1-2:
  revel2-1: bar
  revel2-2:
    revel3: baz
revel1-3:
  revel2:
    revel3:
      revel4: qux

   

期待する出力

revel1-1はfooです
revel1-2のrevel2-1はbarです
revel1-2のrevel2-2のrevel3はbazです
revel1-3のrevel2のrevel3のrevel4はquxです

作成したコード

上記のYAMLから値を取り出すために下記のコードを作成しました.

自分の考え

yamlを取り込むとhashとして取り扱うので、

  1. keyとvalueに分割する
  2. valueがhashかどうか調べる、
  • hashであれば1に戻る
  • hashでなければこれまでのkeyとvalueを出力する
require 'yaml'
revel = YAML.load_file("revel.yml")
revel.each do |a,b|
  if b.instance_of?(Hash)
    b.each do|c,d|
      if d.instance_of?(Hash)
        d.each do |e,f|
          if f.instance_of?(Hash)
            f.each do|g,h|
              p "#{a}#{c}#{e}#{g}#{h}です"
            end
          else
            p "#{a}#{c}#{e}#{f}です"
          end
        end
      else
        p "#{a}#{c}#{d}です"
      end
    end
  else
    p "#{a}#{b}です"
  end
end

上記ご指導のほどよろしくお願いいたします.

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+3

revel.yml

revel1-1: foo
revel1-2:
  revel2-1: bar
  revel2-2:
    revel3: baz
revel1-3:
  revel2:
    revel3:
      revel4: qux

a.rb

require 'yaml'

def print_revel(revel, parent = nil)
  revel.each do |key, val|
    label = parent.nil? ? "#{key}" : "#{parent}#{key}"
    if val.is_a?(Hash)
      print_revel(val, label)
    else
      puts "#{label}#{val}です。"
    end
  end
end

revel = YAML.load_file("revel.yml")
print_revel(revel)

実行結果

$ ruby a.rb
revel1-1はfooです。
revel1-2のrevel2-1はbarです。
revel1-2のrevel2-2のrevel3はbazです。
revel1-3のrevel2のrevel3のrevel4はquxです

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/09 00:11

    勉強になります。

    キャンセル

  • 2017/03/09 11:24

    ##回答

    なるほど!

    rubyのメソッドの引数にはデフォルト値を設定できるんですね!
    また、プログラムを考える際には変化を管理したいものを変数に置くんですね。
    今回の場合は、子レベルによって親レベルの長さが変わるので、親レベルの変化をlabel変数にして管理する。
    ありがとうございます!とても勉強になりました!!


    >通常引数は呼び出し側とメソッド定義側で数が一致している必要がありますが、メソッド定義側では引数にデフォルト値を設定することが可能です。書式は次の通りです http://www.rubylife.jp/ini/method/index4.html

    例えば

    ```
    revel1-2"=>{"revel2-1"=>"bar", "revel2-2"=>{"revel3"=>"baz"}}
    ```

    の処理

    ```ruby
    key = revel1-2
    val = {"revel2-1"=>"bar","revel2-2"=>{"revel3"=>"baz"}}
    parent = nil

    #parentがnilなので
    label = revel1-2
    ```

    valがハッシュなのでもう一度分割する

    ```ruby
    key = revel2-1
    val = bar
    parent = revel1-2

    #parentがnilでないので現在のkeyを繋げる
    lavel = revel1-2のrevel2-1

    #valがハッシュではないので出力して次のkeyへ
    #=>revel1-2のrevel2-1はbarです。
    ```

    ```ruby
    key = revel2-2
    val = {"revel3"=>"baz"}
    parent = revel1-2

    #parentがnilでないので現在のkeyを繋げる
    lavel = revel1-2のrevel2-2
    ```
    valがハッシュでなのでもう一度分割

    ```ruby
    key = revel3
    val = baz
    parent = revel1-2のrevel2-2

    #parentがnilでないので現在のkeyを繋げる
    label = revel1-2のrevel2-2のrevel3

    #valがハッシュではないので出力
    #=>revel1-2のrevel2-2のrevel3はbazです。
    ```


    キャンセル

checkベストアンサー

+2

僕はrubyが苦手でして…生理的に受け付けない言語といいますか。
なので具体的なコードは書けませんが、デザインパターンからアドバイスだけします。

構造はComposite(コンポジット)パターンと言います。
簡単にいうと、コンポジットパターンとはディレクトリとファイルの構造のことです。

コンポジットパターンに適用できる各要素にアクセスするために使えるパターンが2つあります。

各要素にアクセスする手段を要素自身に任せるのがvisitor(ビジター)パターンです。
考え方としては、ルート要素にファンクタを渡します。
要素は自身にファンクタを適用し、自身の子供にファンクタをパスします。
子供は子供自身にファンクタを適用し、子供がいればその子供にファンクタをパスします。
この再帰構造を使えば全要素の値がファンクタに入る=参照できる、という寸法です。
そのためのメソッドを要素に共通で1つだけ持ちます。
Visitorパターンはちょっと分かりづらいですけどね…(良い説明サイトがないといつも感じる。)

Iteratorパターンにするという方法もあります。
Iteratorパターンは、自身の要素を並べるパターンのことです。
応用して、自分自身と、自分自身の子供を返すということを繰り返してネストされた構造を1層構造に見せることができれば全要素にアクセスできるようになります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/08 11:50

    早速の回答ありがとうございます!
    デザインパターンの勉強が足りないことがわかりました!

    早速、コンポジットパターンを調べます!

    ありがとうございました!

    キャンセル

0

#encoding : utf-8
data = open("yamlテスト.txt", &:read).chomp.split("\n").map{|e| e.split(":")}
data.map!{|e| [e[0].count("\s"), e]}

data.each_with_index do |element, i|
  unless element[1][1].nil?
    unless element[0].zero?
      num = element[0] - 2
      array = []
      loop{
        deco = data[i -= 1]
        if deco[0] == num
          array << deco[1][0]
          num -= 2
          if deco[0].zero?
            break
          end
        end
      }
      array.reverse.each{|e| print e.strip + "の"}
    end
    puts "#{element[1][0].strip}#{element[1][1].strip}です"
  end
end


質問に書いてあったyaml?をメモ帳に書いて作ってみました。
スペースの増やし方を変更されると正常に動きません。

yamlテスト.txt

revel1-1: 100
revel1-2:
  revel2-1: 200
  revel2-2:
    revel3: 212
revel1-3:
  revel2:
    revel3:
      revel4: 31
        reverl5: 431
          reverl6: 211
            revel7: 543
revel1-4: 300
revel1-5: 327
  revel2-1: 816
  revel2-2:
    revel3: 938
revel1-6:
  revel2:
    revel3-1:
      revel4: 721
    revel3-2:
      revel5-1: 642
revel1-7:
  revel2: 4
    revel3: 87
      revel4:
        reverl5: 966
          reverl6: 71
            revel7:
              revel8: 99
                revel9: 901
                  revel10: 206
                    revel11:
                      revel12: 12

実行結果例

revel1-1100です
revel1-2のrevel2-1200です
revel1-2のrevel2-2のrevel3は212です
revel1-3のrevel2のrevel3のrevel4は31です
revel1-3のrevel2のrevel3のrevel4のreverl5は431です
revel1-3のrevel2のrevel3のrevel4のreverl5のreverl6は211です
revel1-3のrevel2のrevel3のrevel4のreverl5のreverl6のrevel7は543です
revel1-4300です
revel1-5327です
revel1-5のrevel2-1816です
revel1-5のrevel2-2のrevel3は938です
revel1-6のrevel2のrevel3-1のrevel4は721です
revel1-6のrevel2のrevel3-2のrevel5-1642です
revel1-7のrevel2は4です
revel1-7のrevel2のrevel3は87です
revel1-7のrevel2のrevel3のrevel4のreverl5は966です
revel1-7のrevel2のrevel3のrevel4のreverl5のreverl6は71です
revel1-7のrevel2のrevel3のrevel4のreverl5のreverl6のrevel7のrevel8は99です
revel1-7のrevel2のrevel3のrevel4のreverl5のreverl6のrevel7のrevel8のrevel9は901です
revel1-7のrevel2のrevel3のrevel4のreverl5のreverl6のrevel7のrevel8のrevel9のrevel10は206です
revel1-7のrevel2のrevel3のrevel4のreverl5のreverl6のrevel7のrevel8のrevel9のrevel10のrevel11のrevel12は12です


私はyamlを詳しく知らないので、見当違いだったらすみません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/03/08 18:03


    回答ありがとうございます!
    スペースの数に注目するとこういう書き方ができるんですね!

    ちなみに
    YAMLとは構造化されたデータを表現するフォーマットのことです。
    拡張子はymlです。

    YAMLの書き方はこちらに詳しく記載されております。
    http://magazine.rubyist.net/?0009-YAML

    rubyのyamlライブラリ
    https://docs.ruby-lang.org/ja/latest/library/yaml.html

    キャンセル

  • 2017/03/08 20:44

    情報ありがとうございます。
    面白そうですね(*'▽')

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • トップ
  • Rubyに関する質問
  • RubyでYAMLから値を取り出す際、YAMLに値が追加されても変更が必要ないコードを作成したい