我正在尝试找到一种有效的方法来更新具有新值的哈希数组,但只更新与原始值不同的值。
修改添加了以下CodeBunk:http://codebunk.com/b/83947000/
例如:
h1 = { foo: "a", bar: "b" }
h2 = { foo: "a", bar: "c" }
h3 = { foo: "h", bar: "e" }
h4 = { foo: "s", bar: "b" }
h5 = { foo: "a"}
h6 = { foo: "y"}
h7 = { bar: "b"}
h8 = { bar: "z"}
hashes = [h1, h2, h3, h4, h5, h6, h7, h8]
original = { foo: "a", bar: "b" }
updated = { foo: "x", bar: "f" }
hashes.each do |h|
# magic?
end
# result:
# h1.insepct #=> { foo: "x", bar: "f" }
# Both "foo" and "bar" change because they both matched their original values;
# h2.insepct #=> { foo: "x", bar: "c" }
# Only "foo" changes; "bar" does NOT change because it has a value differnt than the original
# h3.insepct #=> { foo: "h", bar: "e" }
# Neither "foo" nor "bar" change because neither value matched the original
# h4.insepct #=> { foo: "s", bar: "f" }
# Only "bar" changes; "foo" does not change because it has a value different than the original
# h5.inspect #=> { foo: "x" }
# "foo" changes as it matched the original value; hash does not include "bar"
# h6.inspect #=> { foo: "y" }
# "foo" change; hash does not include "bar"
# h7.inspect #=> { bar: "f" }
# "bar" changes as it matched the original value; hash does not include "foo"
# h8.inspect #=> { bar: "z" }
# "bar" change; hash does not include "foo"
到目前为止,我只能通过多个嵌套循环实现此目的。我真的希望有更好的方法...
编辑2 - 这是我目前的解决方案,我希望改进:
h1 = { foo: "a", bar: "b" }
h2 = { foo: "a", bar: "c" }
h3 = { foo: "h", bar: "e" }
h4 = { foo: "s", bar: "b" }
h5 = { foo: "a"}
h6 = { foo: "y"}
h7 = { bar: "b"}
h8 = { bar: "z"}
hashes = [h1, h2, h3, h4, h5, h6, h7, h8]
legend = {}
original_attrs = { foo: "a", bar: "b" }
updated_attrs = { foo: "x", bar: "f" }
updated_attrs.each do |k, v|
legend[k] = { original_value: original_attrs[k], new_value: v }
end
hashes.each do |hsh|
legend.each do |legend_key, legend_value|
if hsh.has_key?(legend_key) && hsh[legend_key] == legend_value[:original_value]
hsh[legend_key] = legend_value[:new_value]
end
end
end
logger.debug "hashes: #{hashes}"
# hashes: [
# {:foo=>"x", :bar=>"f"},
# {:foo=>"x", :bar=>"c"},
# {:foo=>"h", :bar=>"e"},
# {:foo=>"s", :bar=>"f"},
# {:foo=>"x"},
# {:foo=>"y"},
# {:bar=>"f"},
# {:bar=>"z"}
# ]
答案 0 :(得分:0)
为了便于说明,我使用了您的示例并进行了以下更改:
h8 = { bar: "z", buz: "a"}
original_attrs = { foo: "a", bar: "b", booz: "pony" }
updated_attrs = { foo: "x", bar: "f", booz: "pig" }
第一步是构建从original_attrs
到updated_attrs
的映射。在这样做时,我假设两个哈希共享相同的键。我们获得:
attr_map = original_attrs.merge(updated_attrs) { |_,o,n| { o=>n } }
#=> {:foo=>{"a"=>"x"}, :bar=>{"b"=>"f"}}
事实上,我认为这比用于存储该信息的并行数组更好。计算使用方法Hash#merge的形式,该方法使用块(此处为{ |_,o,n| { o=>n } }
)来计算合并的两个哈希中存在的键的值。请注意,这不会改变original_attrs
或updated_attrs
。
如果hashes
(哈希)的元素的键:foo
的值为"a"
,我们希望将该值更改为attr_map[:foo]["a"] #=> "x"
中相应的哈希值我们映射hashes
的数组。如果值是其他值,比如说"r"
,我们希望保持不变。一种方法是使用方法Hash#default_proc=为attr_map
的每个值(哈希)分配默认值:
attr_map.each { |_,h| h.default_proc = ->(_,v) { v } }
结果是,如果attr_map[:foo]
没有密钥k
,attr_map[:foo][k]
将返回proc的值:
attr_map[:foo]['cat']
#=> "cat"
这是原始值,意味着:foo=>'cat'
将保持不变。对于attr_map
的其他键,情况也是如此。我们现在可以按如下方式执行映射:
hashes.map { |g| g.each_with_object({}) { |(k,v),h|
h[k] = (attr_map.key?(k) ? attr_map[k][v] : v) } }
#=> [{:foo=>"x", :bar=>"f"},
# {:foo=>"x", :bar=>"c"},
# {:foo=>"h", :bar=>"e"},
# {:foo=>"s", :bar=>"f"},
# {:foo=>"x"},
# {:foo=>"y"},
# {:bar=>"f"},
# {:bar=>"z", :buz=>"a"}]
这不会改变hashes
。
总之,代码是:
attr_map = original_attrs.merge(updated_attrs) { |_,o,n| { o=>n } }
attr_map.each { |_,h| h.default_proc = ->(_,v) { v } }
hashes.map { |g| g.each_with_object({}) { |(k,v),h|
h[k] = (attr_map.key?(k) ? attr_map[k][v] : v) } }
最后,我想谈谈each_with_object
块变量的几句话,它们显示为
|(k,v),h|
map
将hashes
(h1
)的第一个值传递到其块中,它变为块变量g
的值:
g #=> { foo: "a", bar: "b" }
each_with_object
然后将对象初始化为空哈希,然后将g
的每个元素与(更新的)哈希对象一起传递到其块。它传递给块的第一个值是:
[[:foo, "a"], {}]
并行分配和消歧用于为三个块变量赋值:
(k,v),h = [[:foo, "a"], {}]
#=> [[:foo, "a"], {}]
k #=> :foo
v #=> "a"
h #=> {}