我有三个哈希:
db_headers = {"1"=>"first_name", "2"=>"last_name"}
csv_headers = {"1"=>"First Name", "2"=>"Last Name"}
csv_records = {"0"=>{"id"=>"11", "first_name"=>"first_0", "Last Name"=>"last_0", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC"}, "1"=>{"id"=>"12", "first_name"=>"first_1", "Last Name"=>"last_1", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC"}}
db_headers和csv_headers由其键匹配。例如,它们的键“2”值分别包含“last_name”和“Last Name”。我的目标是在密钥相同的db_headers和csv_headers之间的值不同的地方,然后我需要将csv_records中的密钥与db_headers的值交换。因此,例如,csv_records键将从“Last Name”更改为“last_name”,因为键“2”处的db_headers和csv_headers值不同。
这就是我提出的:
csv_records.each do |record_key,record_value|
csv_headers.each do |csv_key,csv_value|
if record_value.has_key? csv_value
db_headers.each do |db_key, db_value|
if csv_key == db_key
csv_records[db_value] = csv_records.delete csv_value
break
end
end
break
end
end
end
不幸的是它失败了:
RuntimeError: can't add a new key into hash during iteration
from (irb):12:in `[]='
from (irb):12:in `block (3 levels) in irb_binding'
from (irb):10:in `each'
from (irb):10:in `block (2 levels) in irb_binding'
from (irb):8:in `each'
from (irb):8:in `block in irb_binding'
from (irb):7:in `each'
from (irb):7
这使错误消失了:
csv_records.keys.each do |record_key|
csv_headers.keys.each do |csv_key|
if csv_records[record_key].has_key? csv_headers[csv_key]
db_headers.keys.each do |db_key|
if csv_key == db_key
csv_records[db_headers[db_key]] = csv_records.delete csv_headers[csv_key]
# break is needed becasue csv_key wont exist in next iteration
break
end
end
end
end
end
但是csv_records现在应该具有值last_name,但它继续具有“姓氏”。
答案 0 :(得分:6)
在hash.keys
而不是在hash
上进行迭代。 #keys
将创建一个与哈希分开的数组,因此您在修改哈希值时不会弄乱迭代。
答案 1 :(得分:2)
除非您有严重的内存限制,否则请使用reduce
来构建所需的记录。
# If you need to keep csv_headers and db_headers for another reason, you can use them to create REPLACE_KEYS.
REPLACE_KEYS = {"First Name"=>"first_name", "Last Name"=>"last_name"}
csv_records = {"0"=>{"id"=>"11", "first_name"=>"first_0", "Last Name"=>"last_0", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC"}, "1"=>{"id"=>"12", "first_name"=>"first_1", "Last Name"=>"last_1", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC"}}
def transform_record(record)
record.reduce({}) do |acc, (key, value)|
new_key = REPLACE_KEYS[key] || key
acc[new_key] = value
acc
end
end
db_records = csv_records.reduce({}) do |acc, (row, record)|
acc[row] = transform_record(record)
acc
end
答案 2 :(得分:0)
您的代码中存在错误。您正在尝试修改错误的哈希值(当您使用嵌套哈希值时很容易)。所以:
csv_records[db_value] = csv_records.delete csv_value
应改为:
record_value[db_value] = record_value.delete csv_value
仅此一项就可以解决您的问题。
另外,作为进一步的提示,您似乎可以将csv_headers和db_headers哈希合并为一个哈希:
{ "First Name" => "first_name",
"Last Name" => "last_name" }
这应该允许您简化循环的逻辑。
答案 3 :(得分:0)
我建议:
db_headers
和csv_headers
db_headers
和csv_headers
将此方法转换为代码非常简单:
(csv_records.keys & db_headers.keys & csv_headers.keys).select { |k|
db_headers[k] != csv_headers[k] }.each { |k|
db_headers[k], csv_headers[k] = csv_headers[k], db_headers[k] }
db_headers #=> {"1"=>"First Name", "2"=>"last_name"}
csv_headers #=> {"1"=>"first_name", "2"=>"Last Name"}
我们有
keys = csv_records.keys & db_headers.keys & csv_headers.keys
#=> ["1"]
selected_keys = keys.select { |k| db_headers[k] != csv_headers[k] }
#=> ["1"]
然后为每个键执行并行分配(这里只有一个):
selected_keys.each { |k|
db_headers[k], csv_headers[k] = csv_headers[k], db_headers[k] }
答案 4 :(得分:0)
首先从db_headers和csv_headers
获取替换规则map = Hash[db_headers.merge(csv_headers){|_,v1,v2| [v2,v1]}.values]
#=> {"First Name"=>"first_name", "Last Name"=>"last_name"}
然后点击以在csv_records中传输数据:
csv_records.tap {|x|
map.each {|from,to|
x.each{|k,v|
x[k][to]=x[k][from] if x[k][from]
x[k].delete(from)
}
}
}
#=> {"0"=>{"id"=>"11", "first_name"=>"first_0", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC", "last_name"=>"last_0"}, "1"=>{"id"=>"12", "first_name"=>"first_1", "created_at"=>"2014-08-12 17:02:28 UTC", "updated_at"=>"2014-08-12 17:02:28 UTC", "last_name"=>"last_1"}}
缩短一行:
csv_records.tap {|x| map.each {|from,to| x.each{|k,v| x[k][to]=x[k].delete(from) if x[k][from] }}}
<强>更新强>
对于你的逻辑,问题是:
csv_records.delete csv_headers[csv_key]
将不执行任何操作,因为csv_records只有“0”和“1”的键,您应该使用csv_records[record_key].delete csv_headers[csv_key]
。试试这个:
csv_records.keys.each do |record_key|
csv_headers.keys.each do |csv_key|
if csv_records[record_key].has_key? csv_headers[csv_key]
csv_records[record_key][db_headers[csv_key]] = csv_records[record_key].delete csv_headers[csv_key] if csv_records[record_key][csv_headers[csv_key]]
end
end
end
csv_records
答案 5 :(得分:0)
只需将此行添加到您的代码中,您必须首先克隆您的哈希。
csv_records = csv_records.clone
这一行只能解决你的问题 ruby:在迭代过程中无法将新密钥添加到哈希中
但它接近你的代码没有完成(如果你想在途中这样做)。 您的代码包含我的修补程序(添加了一行)
csv_records.each do |record_key,record_value|
csv_headers.each do |csv_key,csv_value|
if record_value.has_key? csv_value
db_headers.each do |db_key, db_value|
if csv_key == db_key
csv_records = csv_records.clone
csv_records[db_value] = csv_records.delete csv_value
break
end
end
break
end
end
end