We have the following datastructures:
我们有以下数据结构:
{:a => ["val1", "val2"], :b => ["valb1", "valb2"], ...}
And I want to turn that into
我想把它变成。
[{:a => "val1", :b => "valb1"}, {:a => "val2", :b => "valb2"}, ...]
And then back into the first form. Anybody with a nice looking implementation?
然后回到第一种形式。有人有好看的实现吗?
7 个解决方案
#1
12
This solution works with arbitrary numbers of values (val1, val2...valN):
该解决方案适用于任意数量的值(val1, val2…valN):
{:a => ["val1", "val2"], :b => ["valb1", "valb2"]}.inject([]){|a, (k,vs)|
vs.each_with_index{|v,i| (a[i] ||= {})[k] = v}
a
}
# => [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}]
[{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}].inject({}){|a, h|
h.each_pair{|k,v| (a[k] ||= []) << v}
a
}
# => {:a=>["val1", "val2"], :b=>["valb1", "valb2"]}
#2
7
Using a functional approach (see Enumerable):
使用功能方法(见可列举的):
hs = h.values.transpose.map { |vs| h.keys.zip(vs).to_h }
#=> [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}]
And back:
并返回:
h_again = hs.first.keys.zip(hs.map(&:values).transpose).to_h
#=> {:a=>["val1", "val2"], :b=>["valb1", "valb2"]}
#3
1
Let's look closely what the data structure we are trying to convert between:
让我们仔细看看我们要转换的数据结构:
#Format A
[
["val1", "val2"], :a
["valb1", "valb2"], :b
["valc1", "valc2"] :c
]
#Format B
[ :a :b :c
["val1", "valb1", "valc1"],
["val2", "valb2", "valc3"]
]
It is not diffculty to find Format B
is the transpose of Format A
in essential , then we can come up with this solution:
找到格式B本质上是格式A的转置并不是一件难事,那么我们就可以找到这样的解决方案:
h={:a => ["vala1", "vala2"], :b => ["valb1", "valb2"], :c => ["valc1", "valc2"]}
sorted_keys = h.keys.sort_by {|a,b| a.to_s <=> b.to_s}
puts sorted_keys.inject([]) {|s,e| s << h[e]}.transpose.inject([]) {|r, a| r << Hash[*sorted_keys.zip(a).flatten]}.inspect
#[{:b=>"valb1", :c=>"valc1", :a=>"vala1"}, {:b=>"valb2", :c=>"valc2", :a=>"vala2"}]
#4
1
m = {}
a,b = Array(h).transpose
b.transpose.map { |y| [a, y].transpose.inject(m) { |m,x| m.merge Hash[*x] }}
#5
1
My attempt, perhaps slightly more compact.
我的尝试,可能更紧凑一点。
h = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] }
h.values.transpose.map { |s| Hash[h.keys.zip(s)] }
Should work in Ruby 1.9.3 or later.
应该在Ruby 1.9.3或更高版本中工作。
Explanation:
First, 'combine' the corresponding values into 'rows'
首先,“合并”相应的值到“行”中
h.values.transpose
# => [["val1", "valb1"], ["val2", "valb2"]]
Each iteration in the map
block will produce one of these:
map块中的每一次迭代都会产生这样的结果:
h.keys.zip(s)
# => [[:a, "val1"], [:b, "valb1"]]
and Hash[]
will turn them into hashes:
哈希会把它们变成哈希:
Hash[h.keys.zip(s)]
# => {:a=>"val1", :b=>"valb1"} (for each iteration)
#6
0
This will work assuming all the arrays in the original hash are the same size:
假设原始散列中的所有数组大小相同,则该方法将有效:
hash_array = hash.first[1].map { {} }
hash.each do |key,arr|
hash_array.zip(arr).each {|inner_hash, val| inner_hash[key] = val}
end
#7
0
You could use inject
to build an array of hashes.
您可以使用注入来构建一个散列数组。
hash = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] }
array = hash.inject([]) do |pairs, pair|
pairs << { pair[0] => pair[1] }
pairs
end
array.inspect # => "[{:a=>["val1", "val2"]}, {:b=>["valb1", "valb2"]}]"
Ruby documentation has a few more examples of working with inject
.
Ruby文档中还有一些使用注入的例子。
#1
12
This solution works with arbitrary numbers of values (val1, val2...valN):
该解决方案适用于任意数量的值(val1, val2…valN):
{:a => ["val1", "val2"], :b => ["valb1", "valb2"]}.inject([]){|a, (k,vs)|
vs.each_with_index{|v,i| (a[i] ||= {})[k] = v}
a
}
# => [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}]
[{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}].inject({}){|a, h|
h.each_pair{|k,v| (a[k] ||= []) << v}
a
}
# => {:a=>["val1", "val2"], :b=>["valb1", "valb2"]}
#2
7
Using a functional approach (see Enumerable):
使用功能方法(见可列举的):
hs = h.values.transpose.map { |vs| h.keys.zip(vs).to_h }
#=> [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}]
And back:
并返回:
h_again = hs.first.keys.zip(hs.map(&:values).transpose).to_h
#=> {:a=>["val1", "val2"], :b=>["valb1", "valb2"]}
#3
1
Let's look closely what the data structure we are trying to convert between:
让我们仔细看看我们要转换的数据结构:
#Format A
[
["val1", "val2"], :a
["valb1", "valb2"], :b
["valc1", "valc2"] :c
]
#Format B
[ :a :b :c
["val1", "valb1", "valc1"],
["val2", "valb2", "valc3"]
]
It is not diffculty to find Format B
is the transpose of Format A
in essential , then we can come up with this solution:
找到格式B本质上是格式A的转置并不是一件难事,那么我们就可以找到这样的解决方案:
h={:a => ["vala1", "vala2"], :b => ["valb1", "valb2"], :c => ["valc1", "valc2"]}
sorted_keys = h.keys.sort_by {|a,b| a.to_s <=> b.to_s}
puts sorted_keys.inject([]) {|s,e| s << h[e]}.transpose.inject([]) {|r, a| r << Hash[*sorted_keys.zip(a).flatten]}.inspect
#[{:b=>"valb1", :c=>"valc1", :a=>"vala1"}, {:b=>"valb2", :c=>"valc2", :a=>"vala2"}]
#4
1
m = {}
a,b = Array(h).transpose
b.transpose.map { |y| [a, y].transpose.inject(m) { |m,x| m.merge Hash[*x] }}
#5
1
My attempt, perhaps slightly more compact.
我的尝试,可能更紧凑一点。
h = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] }
h.values.transpose.map { |s| Hash[h.keys.zip(s)] }
Should work in Ruby 1.9.3 or later.
应该在Ruby 1.9.3或更高版本中工作。
Explanation:
First, 'combine' the corresponding values into 'rows'
首先,“合并”相应的值到“行”中
h.values.transpose
# => [["val1", "valb1"], ["val2", "valb2"]]
Each iteration in the map
block will produce one of these:
map块中的每一次迭代都会产生这样的结果:
h.keys.zip(s)
# => [[:a, "val1"], [:b, "valb1"]]
and Hash[]
will turn them into hashes:
哈希会把它们变成哈希:
Hash[h.keys.zip(s)]
# => {:a=>"val1", :b=>"valb1"} (for each iteration)
#6
0
This will work assuming all the arrays in the original hash are the same size:
假设原始散列中的所有数组大小相同,则该方法将有效:
hash_array = hash.first[1].map { {} }
hash.each do |key,arr|
hash_array.zip(arr).each {|inner_hash, val| inner_hash[key] = val}
end
#7
0
You could use inject
to build an array of hashes.
您可以使用注入来构建一个散列数组。
hash = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] }
array = hash.inject([]) do |pairs, pair|
pairs << { pair[0] => pair[1] }
pairs
end
array.inspect # => "[{:a=>["val1", "val2"]}, {:b=>["valb1", "valb2"]}]"
Ruby documentation has a few more examples of working with inject
.
Ruby文档中还有一些使用注入的例子。