如何将嵌套哈希提取到数据库表中?
Posted
技术标签:
【中文标题】如何将嵌套哈希提取到数据库表中?【英文标题】:How to extract nested hashes into database table? 【发布时间】:2014-01-20 12:14:40 【问题描述】:我正在尝试从 Mailchimp 中提取一些以嵌套哈希返回的数据。这是我得到的结果的精简版。对于每封电子邮件,有多个 GROUPINGS,对于每个 GROUPING,有多个 GROUPS。
我的目标是将其放入一个 mysql 表中,其布局如下:email_addr、list、grouping1_id、grouping1_name、group1_name、group1_interest、group2_name、group2_interest、grouping2_id、grouping2_name 等。所以每个订阅者只有一行,所有分组和分组信息。
"email"=>"dummy@gmail.com", "merges"=>"EMAIL"=>"dummy@gmail.com",
"GROUPINGS"=>["id"=>1, "name"=>"Grouping One", "groups"=>["name"=>"Group One",
"interested"=>false, "name"=>"Group", "interested"=>true,
"name"=>"Group Three", "interested"=>true], "id"=>2, "name"=>"Grouping Two",
"groups"=>["name"=>"Group Four", "interested"=>false,
"name"=>"Group Five", "interested"=>false]]
现在,我在下面的代码运行并将嵌套块的结果插入到表中,但每次通过 groups.each_with_index 语句都有一行。到目前为止,我的方法似乎过于复杂,但我不确定如何正确处理数据。
感谢任何帮助。
更新: 我稍微清理了逻辑并将数据库写入分离到哈希处理的每个级别。现在数据已正确插入和更新到数据库中。虽然这样还是觉得很不雅。
def organize_members_subs
@members_data = @members_subs["data"]
@members_data.each do |member|
@email_addr = member["email"]
@db.query("INSERT INTO db.details
(email_addr, list)
VALUES ('#@email_addr', '#@list' ) ")
groupings = member["merges"]["GROUPINGS"]
groupings.each_with_index do |grouping, index|
@groupings_name = grouping["name"]
@groupings_id = grouping["id"]
@groupings_label = "grp#index_"
@db.query("UPDATE db.details
SET grouping#index_id = '#@groupings_id'
, grouping#index_name = '#@groupings_name'
WHERE email_addr = '#@email_addr' ")
groups = member["merges"]["GROUPINGS"][index]["groups"]
groups.each_with_index do |group, index|
@group_name = group["name"]
@group_interested = group["interested"]
@db.query("UPDATE db.details
SET #@groupings_labelgroup#index_name = '#@group_name'
, #@groupings_labelgroup#index_int = '#@group_interested'
WHERE email_addr = '#@email_addr' ")
break if index == groups.length
end
break if index == groupings.length
end
end
end
【问题讨论】:
首先,让@groupingsXname
和@groupingsX_id
成为一个哈希数组怎么样,这样您就可以用@groupings[index] = "name" => grouping["name"], "id" => grouping["id"]
替换这六个分配,或者只是让grouping
成为一个哈希数组?另外,考虑使用:name
和:id
,而不是"name"
和"id"
作为键。最后,将"email"=>"email",...
放在多行中会很有帮助,并带有行继续字符\
,因此读者不必水平滚动即可阅读该行。
@CarySwoveland 感谢您的建议。我做了一些更改,现在确实将数据正确写入数据库。不过,它似乎仍然可以改进。你有什么建议?如果您添加答案,我会接受。
你能澄清一下,第一个"email" => "email"...
实际上是指一个电子邮件地址吗?所以在实际使用中会是"email" => "andy@example.com"....
?我知道你已经缩减了这个例子,但是如果这是实际发生的事情,你可以使用虚拟电子邮件地址吗?我不熟悉 Mailchimp。
@Beartech 是的,你是对的。我的占位符选择不佳。我在示例中添加了一个虚拟电子邮件地址。
所以"email"=>"dummy@gmail.com",
可以被剥离以移除外部哈希?列是恒定的吗?即是否会有可变数量的分组?以及分组内的可变数量的组?另外,有没有机会为此使用 Rails?
【参考方案1】:
首先,我想仔细看看你的哈希值。我没有自己重新格式化,而是这样做了:
require "awesome_print"
h = `"email"=>..., "interested"=>false]]`
ap h
向下滚动到我的答案底部以查看 ap 的哈希格式。
假设 db 结构是给定的,我将回答您的问题,但想说明几点:
如果"id"
对于每条grouping
记录都是唯一的,您能否将其设为密钥,而无需使用index
?
如果"name"
对于每条grouping
记录都是唯一的,您能否同时省略"id"
和index
?
如果"name"
对于每条group
记录都是唯一的(对于给定的grouping
),您能否只为每个组设置group["name"] => group["interested"]
?
继续您的代码,我还将假设您的哈希结构已给出。稍后,我将重新审视这个假设。
我对您的代码提出的更改相当小,有些纯粹是风格上的:
使所有实例变量成为局部变量,这意味着必须将两个附加参数传递给def organize_members_subs
。
除了两个可能的例外,消除定义后只使用一次的局部变量。例如,而不是
groupings_id = grouping["id"]
,然后是SET grouping#index_id = '#@groupings_id'
,只有SET grouping#index_id = '#grouping["id"]'
。
两个可能的例外是groupings
和groups
。例如,您可以通过编写来摆脱前者
member["merges"]["GROUPINGS"].each_with_index do |grouping, index_1|
。我会将它们保留为变量(这样我就可以轻松检查它们的值),
但这是一个风格决定。
groupings.each_with_index do |grouping, index|
中的变量index
在内部块的范围内,它使用同名的迭代器变量。
我认为后者优先,但它们应该以不同的方式命名。我已将它们分别更改为 index_out
和 index_in
。
index_out
的范围从 0
到 groupings.length-1
,因此 break if index_out == groupings.length
永远不会被执行,因此可能会被删除。 break if index_in == groups.length
同上。
我将groupings_label = "grp#index_"
向下移动以提醒人们注意这一事实,即仅在以后需要它,而不是在前面的SET
表达式中。
这些更改导致以下结果:
def organize_members_subs(db, list, @members_subs["data"])
members_data.each do |member|
email_addr = member["email"]
db.query("INSERT INTO db.details
(email_addr, list)
VALUES ('#email_addr', '#list' ) ")
groupings = member["merges"]["GROUPINGS"]
groupings.each_with_index do |grouping, index_out|
db.query("UPDATE db.details
SET grouping#index_out_id = '#grouping["id"]'
, grouping#index_out_name = '#grouping["name"]'
WHERE email_addr = '#email_addr' ")
groupings_label = "grp#index_out_"
groups = member["merges"]["GROUPINGS"][index_out]["groups"]
groups.each_with_index do |group, index_in|
db.query("UPDATE db.details
SET #groupings_labelgroup#index_in_name = '#group["name"]'
, #groupings_labelgroup#index_in_int = '#group["interested"]'
WHERE email_addr = '#email_addr' ")
end
end
end
end
查看您的哈希,我想知道您是否可以将其简化为以下内容(格式由出色的打印提供):
"email" => "dummy@gmail.com",
"merges" =>
"EMAIL" => "dummy@gmail.com",
"GROUPINGS" =>
1 =>
"name" => "Grouping One",
"groups" =>
"Group One" => false,
"Group Two" => true,
"Group Three" => true
,
2 =>
"name" => "Grouping Two",
"groups" =>
"Group Four" => false,
"Group Five" => false
甚至
"email" => "dummy@gmail.com",
"merges" =>
"EMAIL" => "dummy@gmail.com",
"GROUPINGS" =>
"Grouping One" =>
"Group One" => false,
"Group Two" => true,
"Group Three" => true
,
"Grouping Two" =>
"Group Four" => false,
"Group Five" => false
这些不是建议,而是值得深思的食物。
对您的哈希应用了很棒的打印:
ap h # =>
"email" => "dummy@gmail.com",
"merges" =>
"EMAIL" => "dummy@gmail.com",
"GROUPINGS" => [
[0]
"id" => 1,
"name" => "Grouping One",
"groups" => [
[0]
"name" => "Group One",
"interested" => false
,
[1]
"name" => "Group",
"interested" => true
,
[2]
"name" => "Group Three",
"interested" => true
]
,
[1]
"id" => 2,
"name" => "Grouping Two",
"groups" => [
[0]
"name" => "Group Four",
"interested" => false
,
[1]
"name" => "Group Five",
"interested" => false
]
]
【讨论】:
【参考方案2】:首先,也许是额外的,但我喜欢使用符号,因为我在 Rails 中做了很多工作。所以让我们从这里偷一个方法:How do I convert a Ruby hash so that all of its keys are symbols?
def recursive_symbolize_keys(h)
case h
when Hash
Hash[
h.map do |k, v|
[ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
end
]
when Enumerable
h.map |v| recursive_symbolize_keys(v)
else
h
end
end
好的,让我们构建一个类,以便随着我们的需求变化而更容易操作和扩展:
class MemberSub
attr_accessor :email, :groupings, :data_hash, :list, :data_row, :db_sql
def initialize(data_hash)
#convert all keys to symbols
@data_hash = recursive_symbolize_keys(data_hash)
@email = @data_hash[:email]
@list = 'Members'
@groupings = @data_hash[:merges][:GROUPINGS]
@data_row = data_row
@db_sql = db_insert
end
def data_row
#returns a data row for DB
row_hash =
row_hash['email'] = @email
row_hash['list'] = @list
gc = 1
#iterate through groupings
@groupings.each_with_index do |grouping, index|
row_hash["grouping#index + 1_id"] = grouping[:id]
row_hash["grouping#index + 1_name"] = grouping[:name]
#iterate through the groups
grouping[:groups].each do |group|
row_hash["group#gc_name"] = group[:name]
row_hash["group#gc_interest"] = group[:interested]
gc += 1
end
end
row_hash
end
def db_insert
"INSERT INTO db.details (#@data_row.keys) VALUES (#@data_row.values)".tr('[]','')
end
end
现在您可以使用任何迭代方法连续输入它并创建一个新对象:
row = MemberSub.new("email"=>"dummy@gmail.com", "list"=>"Members", "merges"=>
"EMAIL"=>"dummy@gmail.com", "GROUPINGS"=>["id"=>1, "name"=>"Grouping One", "groups"=>
["name"=>"Group One", "interested"=>false, "name"=>"Group Two", "interested"=>true,
"name"=>"Group Three", "interested"=>true], "id"=>2, "name"=>"Grouping Two", "groups"=>
["name"=>"Group Four", "interested"=>false, "name"=>"Group Five", "interested"=>false]])
并进行查询:
db.query(row.db_sql)
db.query(INSERT INTO db.details ("email", "list", "grouping1_id", "grouping1_name",
"group1_name", "group1_interest", "group2_name", "group2_interest", "group3_name",
"group3_interest", "grouping2_id", "grouping2_name", "group4_name", "group4_interest",
"group5_name", "group5_interest") VALUES ("dummy@gmail.com", "Members", 1, "Grouping One",
"Group One", false, "Group Two", true, "Group Three", true, 2, "Grouping Two", "Group Four",
false, "Group Five", false))
其他方法应该是不言自明的。您不必将它们都作为attar_accessor
提供,但我只是这样做了。
【讨论】:
如果你想省略recursive_symbolize_keys
方法,只需将其余代码中的符号替换为带引号的字符串即可。以上是关于如何将嵌套哈希提取到数据库表中?的主要内容,如果未能解决你的问题,请参考以下文章
当嵌套表属于记录类型时,如何将数据填充到 Oracle 中的嵌套表中