给出一个散列,例如,嵌套散列:
hash = {"some_key" => "value",
"nested" => {"key1" => "val1",
"key2" => "val2"}}
和String格式的键的路径:
path = "nested.key2"
如何在key2条目之前添加新的键值对? 所以,预期的输出应该是这样的:
hash = {"some_key" => "value",
"nested" => {"key1" => "val1",
"new_key" => "new_value"},
"key2" => "val2"}}
的 EDITED 的
我的目标是在某个键之前添加一种标签,以便将哈希转储为Yaml文本,并对文本进行后处理以用Yaml注释替换添加的键/值。 AFAIK,没有其他方法可以通过编程方式在YAML中的特定键之前添加注释。
答案 0 :(得分:7)
使用Hash的数组表示最简单:
subhash = hash['nested'].to_a
insert_at = subhash.index(subhash.assoc('key2'))
hash['nested'] = Hash[subhash.insert(insert_at, ['new_key', 'new_value'])]
它可以包装成一个函数:
class Hash
def insert_before(key, kvpair)
arr = to_a
pos = arr.index(arr.assoc(key))
if pos
arr.insert(pos, kvpair)
else
arr << kvpair
end
replace Hash[arr]
end
end
hash['nested'].insert_before('key2', ['new_key', 'new_value'])
p hash # {"some_key"=>"value", "nested"=>{"key1"=>"val1", "new_key"=>"new_value", "key2"=>"val2"}}
答案 1 :(得分: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.write
,或将该行嵌入传递给File.open
的块中。
关于YAML文件中的注释:YAML不支持以编程方式向发出的输出添加注释。注释适用于人类,#
不能映射到Ruby变量或对象。可以这样想:如果我们在名为test.yaml
的文件中使用此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}
没有返回“comment”对象,并且Ruby中不存在任何适合哈希的东西,它存在于YAML规范中。我们可以随意拼凑一个我们称之为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"}}
答案 2 :(得分: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
答案 3 :(得分:0)
我不认为ruby免费提供此功能。您可以执行以下操作,在此处创建现有哈希键的数组,将新键插入到数组中,然后使用新排序的键创建新哈希。
keys = original_hash.keys
keys.insert(new_key_position, new_key)
new_hash = {}
keys.each do |key|
new_hash[key] = key == new_key ? new_value : original_hash[key]
end