我有以下一组哈希作为输入:-
input =[
{"ID"=>"100", "Key"=>"Field A", "Value"=>"123"},
{"ID"=>"100", "Key"=>"Field B", "Value"=>"333"},
{"ID"=>"100", "Key"=>"Field C", "Value"=>"555"},
{"ID"=>"200", "Key"=>"Field A", "Value"=>"789"},
{"ID"=>"200", "Key"=>"Field B", "Value"=>"999"},
{"ID"=>"200", "Key"=>"Field D", "Value"=>"444"}
]
我想按如下所示变换此哈希数组
output =[
{"ID"=>"100", "Field A"=>"123", "Field B"=>"333", "Field C" => "555", "Field D" => ""},
{"ID"=>"200", "Field A"=>"789", "Field B"=>"999", "Field C" => "", "Field D" => "444"}
]
我可以按以下方式获取唯一的ID和密钥
irb(main):099:0> unique_id = input.map { |p| p["ID"] }.uniq
=> ["100", "200"]
irb(main):100:0> unique_keys = input.map { |p| p["Key"] }.uniq
=> ["Field A", "Field B", "Field C", "Field D"]
但是,我无法继续为每个ID(包含在输入哈希中定义的键/值对)创建唯一的哈希数组。
答案 0 :(得分:3)
尝试关注
fields = input.map {|x| x['Key'] }.uniq
output = input.group_by { |x| x['ID'] }
.map { |k,v| ([['ID', k]] + v.map {|z| z.values_at('Key','Value') }).to_h }
output.map! { |x| {'ID' => x['ID']}.merge fields.to_h {|z| [z, x[z].to_s]} }
输出将是
[
{"ID"=>"100", "Field A"=>"123", "Field B"=>"333", "Field C"=>"555", "Field D"=>""},
{"ID"=>"200", "Field A"=>"789", "Field B"=>"999", "Field C"=>"", "Field D"=>"444"}
]
答案 1 :(得分:2)
类似的事情可能会起作用:
keys = input.map { |hash| hash['Key'] }.uniq
result = Hash.new { |result, id| result[id] = {} }
input.each { |hash| result[hash['ID']].merge!(hash['Key'] => hash['Value']) }
result.default = nil # optional: remove the default value
result.each do |id, hash|
(keys - hash.keys).each { |key| hash[key] = '' }
hash['ID'] = id
end
result.values
#=> [{"Field A"=>"123", "Field B"=>"333", "Field C"=>"555", "Field D"=>"", "ID"=>"100"},
# {"Field A"=>"789", "Field B"=>"999", "Field D"=>"444", "Field C"=>"", "ID"=>"200"}]
如果您确定某些值永远不会虚假,则可以替换:
(keys - hash.keys).each { |key| hash[key] = '' }
# with
keys.each { |key| hash[key] ||= '' }
我首先创建一个哈希result
来保存生成的哈希,然后将该值设置为默认值以生成新的哈希。然后,我根据 ID 获取正确的哈希并将键值对合并到哈希中。最后,我将缺少的键添加到哈希并将其值设置为空字符串,并添加 ID ,在该ID下将哈希保存到哈希中。
注意::如果您的
input
数组包含重复的键值对,则将使用最后一个。例如,说{"ID"=>"100", "Key"=>"Field A", "Value"=>"123"}
和{"ID"=>"100", "Key"=>"Field A", "Value"=>"456"}
都存在。然后设置"Field A" => "456"
,因为它是两者中的后者。
答案 2 :(得分:1)
我的回答分为三个步骤。
步骤1:获取"ID"
的唯一值和"Field X"
形式的唯一键
ids, keys = input.map { |h| h.values_at("ID", "Key") }.transpose.map(&:uniq)
#=> [["100", "200"], ["Field A", "Field B", "Field C", "Field D"]]
请参见Hash#values_at。计算如下:
a = input.map { |h| h.values_at("ID", "Key") }
#=> [["100", "Field A"], ["100", "Field B"], ["100", "Field C"],
# ["200", "Field A"], ["200", "Field B"], ["200", "Field D"]]
b = a.transpose
#=> [["100", "100", "100", "200", "200", "200"],
# ["Field A", "Field B", "Field C", "Field A", "Field B", "Field D"]]
ids, keys = b.map(&:uniq)
#=> [["100", "200"], ["Field A", "Field B", "Field C", "Field D"]]
ids
#=> ["100", "200"]
keys
#=> ["Field A", "Field B", "Field C", "Field D"]
步骤2:构造一个哈希,其键是"ID"
的唯一值,其值是要在步骤3中完成并提取的哈希值
h = ids.each_with_object({}) { |id,h|
h[id] = keys.each_with_object("ID"=>id) { |key,g| g[key] = "" } }
#=> {"100"=>{"ID"=>"100", "Field A"=>"", "Field B"=>"", "Field C"=>"",
# "Field D"=>""},
# "200"=>{"ID"=>"200", "Field A"=>"", "Field B"=>"", "Field C"=>"",
# "Field D"=>""}}
步骤3:循环访问input
以完成步骤2中构造的哈希值,然后作为最后一步,从该哈希值中提取值
input.each_with_object(h) { |g,h| h[g["ID"]].update(g["Key"]=>g["Value"]) }.values
#=> [{"ID"=>"100", "Field A"=>"123", "Field B"=>"333", "Field C"=>"555",
# "Field D"=>""},
# {"ID"=>"200", "Field A"=>"789", "Field B"=>"999", "Field C"=>"",
# "Field D"=>"444"}]
请参见Hash#update(又名merge!
)和Hash#values。两种计算方法如下:
h = input.each_with_object(h) { |g,h| h[g["ID"]].update(g["Key"]=>g["Value"]) }
#=> {"100"=>{"ID"=>"100", "Field A"=>"123", "Field B"=>"333","Field C"=>"555",
# "Field D"=>""},
# "200"=>{"ID"=>"200", "Field A"=>"789", "Field B"=>"999","Field C"=>"",
# "Field D"=>"444"}}
h.values
#=> <as above>
答案 3 :(得分:0)
输出结构不是我想要使用的结构,看起来输入结构正在影响所需的输出。结果是XY problem。
哈希非常有效,尤其是当您具有类似于数据库中索引字段的内容时。与散列相比,遍历数组以查找值的效率极低,因此,我建议您再来看一下这两个结构。
将输入转换为真正的哈希值并不难:
input = [
{"ID"=>"100", "Key"=>"Field A", "Value"=>"123"},
{"ID"=>"100", "Key"=>"Field B", "Value"=>"333"},
{"ID"=>"100", "Key"=>"Field C", "Value"=>"555"},
{"ID"=>"200", "Key"=>"Field A", "Value"=>"789"},
{"ID"=>"200", "Key"=>"Field B", "Value"=>"999"},
{"ID"=>"200", "Key"=>"Field D", "Value"=>"444"}
]
output = Hash.new { |h, k| h[k] = {} } # => {}
input.each { |e|
id = e['ID']
key = e['Key']
value = e['Value']
output[id][key] = value
}
这将导致:
output
# => {"100"=>{"Field A"=>"123", "Field B"=>"333", "Field C"=>"555"},
# "200"=>{"Field A"=>"789", "Field B"=>"999", "Field D"=>"444"}}
这样做的好处非常明显,如果您想要"200"
的数据,就很容易抓住:
output['200'] # => {"Field A"=>"789", "Field B"=>"999", "Field D"=>"444"}
output['200']['Field B'] # => "999"
答案 4 :(得分:-2)
keys = input.map { |hash| hash['Key'] }.uniq
output = input.group_by { |x| x['ID'] }.map { |k,v| ([['ID', k]] + v.map {|z| z.values_at('Key','Value') }).to_h }
output.map! { |x| {'ID' => x['ID']}.merge fields.map {|z| [z, x[z].to_s]}.to_h }
以下给出了如下所示的输出
[
{"ID"=>"100", "Field A"=>"123", "Field B"=>"333", "Field C"=>"555", "Field D"=>""},
{"ID"=>"200", "Field A"=>"789", "Field B"=>"999", "Field C"=>"", "Field D"=>"444"}
]
感谢大家的投入