如何在红宝石中转置哈希数组

时间:2019-11-28 12:33:55

标签: ruby hashtable

我有以下一组哈希作为输入:-

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(包含在输入哈希中定义的键/值对)创建唯一的哈希数组。

5 个答案:

答案 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"}
]

感谢大家的投入