7

たとえば、ネストされたハッシュを含むハッシュが与えられた場合:

hash = {"some_key" => "value",
        "nested" => {"key1" => "val1", 
                     "key2" => "val2"}}

および文字列形式のキーへのパス:

path = "nested.key2"

key2 エントリの前に新しいキーと値のペアを追加するにはどうすればよいですか? したがって、予想される出力は次のようになります。

hash = {"some_key" => "value",
        "nested" => {"key1" => "val1",
                    "new_key" => "new_value"},
                    "key2" => "val2"}}

編集済み

私の目標は、ハッシュをYamlテキストとしてダンプし、テキストを後処理して追加されたキー/値をYamlコメントに置き換えるために、キーの前に一種のラベルを追加することです。私の知る限り、プログラムで YAML の特定のキーの前にコメントを追加する方法は他にありません。

4

4 に答える 4

2

私はよく、アプリの大規模な構成用に YAML ジェネレーターを作成します。メンテナンスのために、フィールドをソートする必要があります。

ソートされた順序で YAML を生成するときに使用する解決策は、必要に応じてキーを追加して、正しいハッシュまたはサブハッシュでキーを取得することです。次に、キーと値のペアを並べ替えて新しいハッシュを作成し、to_yamlそれに対して実行します。

ハッシュをソートする意味はありませんが、YAML に機能させる前に一時ハッシュを出力するようにソートすると、ファイルの管理が容易になります。

require 'yaml'

some_hash = {
    'z' => 1,
    'a' => 3
}

puts some_hash.to_yaml

どの出力:

---
z: 1
a: 3

YAML 出力を作成する前に並べ替えます。

puts Hash[some_hash.merge('x' => 2).sort_by{ |k, v| k }].to_yaml

出力:

---
a: 3
x: 2
z: 1

の代わりに、に渡されるブロックに行をputs使用または埋め込みます。File.writeFile.open


YAML ファイルのコメントについて: YAML は、発行された出力にプログラムでコメントを追加することをサポートしていません。コメントは人間用であり#、Ruby 変数またはオブジェクトにマップできません。このように考えてください: というファイルでこの YAML から始めるとtest.yaml:

---
# string
a: 'fish'
# array
b: 
  - 1
  - 2
# hash
c: 
  d: 'foo'
  e: 'bar'
# integer
z: 1

そしてそれをロードします:

require 'pp'
require 'yaml'

obj = YAML.load_file('test.yaml')

pp obj

私は次のobjようになります:

{"a"=>"fish", "b"=>[1, 2], "c"=>{"d"=>"foo", "e"=>"bar"}, "z"=>1}

返される「コメント」オブジェクトはなく、YAML 仕様に存在するハッシュに適合する Ruby には存在しません。Comment と呼ばれるクラスを任意に作り、それをオブジェクトにキーとして埋め込むこともできますが、YAML はそれをコメントとして受け入れません。これは、仕様で許可されていないためです。それを Ruby クラスとして定義し、そのクラスとして再作成しますが、#コメントとして表示されません。

require 'yaml'

class Comment
  def initialize(some_text)
    @comment = "# #{some_text}"
  end
end

some_hash = {
  'a' => 1,
  Comment.new('foo') => 'bar',
  'z' => 'z'
}

puts some_hash.to_yaml

出力:

---
a: 1
? !ruby/object:Comment
  comment: ! '# foo'
: bar
z: z

発行された YAML 構成にコメントが必要な場合は、手動で微調整して後で追加します。手動で微調整するのではなく、やりたいことのために、文書内でスキャンできる、より記憶に残る名前または一意の変数名を使用することをお勧めします。プレースホルダーとして機能すること以外に価値のあるものを提供しないダミーエントリを入れることもできます:

require 'yaml'

some_hash = {
  'a' => 1,
  '__we_are_here__' => '',
  'b' => 2,
  '__we_are_now_here__' => '',
  'z' => 'z'
}

puts some_hash.to_yaml

次のような YAML ファイルが生成されます。

---
a: 1
__we_are_here__: ''
b: 2
__we_are_now_here__: ''
z: z

キーをハッシュに挿入する限り、「キーチェーン」を少し再構築して、挿入するパスと新しいキーの名前を表示します。繰り返しになりますが、YAML を保存する前に、並べ替えを使用して正しい順序になっていることを確認します。

require 'pp'

# this changes the incoming hash
def insert_embedded_hash_element(hash, key_path, new_value)

  keys = key_path.split('.')
  new_key = keys.pop

  sub_hash = hash
  keys.each do |k|
    sub_hash = sub_hash[k]
  end

  sub_hash[new_key] = new_value

end

# the sub-hash to insert into + new key name
insert_key = 'nested.key2'
insert_value = 'new_value'

hash = {
  "some_key" => "value",
  "nested" => {
    "key1" => "val1", 
    "key3" => "val2"
  }
}

insert_embedded_hash_element(hash, insert_key, insert_value)

pp hash

その結果:

{"some_key"=>"value",
 "nested"=>{"key1"=>"val1", "key3"=>"val2", "key2"=>"new_value"}}
于 2013-06-21T14:43:33.953 に答える
2

これは OP の必要に応じたものですが、必要に応じていつでも変更できます。

require 'yaml'

hash = {"some_key" => "value",
        "nested" => {"key1" => "val1", 
                     "key2" => "val2"}}

new_hash = %w(nested key2).inject(hash) do |h,i|
 next h[i] unless h.has_key? "key2"
 ind = h.to_a.index{|m| m[0] == i }
 Hash[h.to_a.insert(ind,["new_key","new_value"])]
end

hash["nested"] = new_hash # this part is to be taken care of for deep hash.
puts hash.to_yaml

出力:

some_key: value
nested:
  key1: val1
  new_key: new_value
  key2: val2

アップデート:

hash["nested"] = new_hash以前のコードの行を処理するオーバーヘッドを削減する、より効率的なコードを見つけました。

require 'yaml'

hash = {"some_key" => "value",
        "nested" => {"key1" => "val1", 
                     "key2" => "val2"}}

new_hash = %w(nested key2).inject(hash) do |h,i| # !> assigned but unused variable - new_hash
 next h[i] unless h.has_key? "key2"
 ind = h.to_a.index{|m| m[0] == i }
 h1 = Hash[h.to_a.insert(ind,["new_key","new_value"])]
 h.replace(h1)
end

hash
# => {"some_key"=>"value",
#     "nested"=>{"key1"=>"val1", "new_key"=>"new_value", "key2"=>"val2"}}

puts hash.to_yaml
# >> ---
# >> some_key: value
# >> nested:
# >>   key1: val1
# >>   new_key: new_value
# >>   key2: val2
于 2013-06-21T14:48:56.020 に答える