[英]ruby array of array with repeated values to hash of hash
我是ruby的新手,我很難搞清楚如何將數組數組轉換為數組哈希的哈希值。
例如,說我有:
[ [38, "s", "hum"],
[38, "t", "foo"],
[38, "t", "bar"],
[45, "s", "hum"],
[45, "t", "ram"],
[52, "s", "hum"],
[52, "t", "cat"],
[52, "t", "dog"]
]
我最終想要的是:
{38 => {"s" => ["hum"],
"t" => ["foo", "bar"]
},
45 => {"s" => ["hum"],
"t" => ["ram"]
},
52 => {"s" => ["hum"],
"t" => ["cat", "dog"]
}
}
我嘗試過group_by和Hash,但兩個都沒有給我我正在尋找的東西。
也許有一種更簡潔的方法可以做到這一點,但我決定采用直截了當的方式:
input = [ [38, "s", "hum"],
[38, "t", "foo"],
[38, "t", "bar"],
[45, "s", "hum"],
[45, "t", "ram"],
[52, "s", "hum"],
[52, "t", "cat"],
[52, "t", "dog"]
]
output = {}
# I'll talk through the first iteration in the comments.
input.each do |outer_key, inner_key, value|
# Set output[38] to a new hash, since output[38] isn't set yet.
# If it were already set, this line would do nothing, so
# output[38] would keep its previous data.
output[outer_key] ||= {}
# Set output[38]["s"] to a new array, since output[38]["s"] isn't set yet.
# If it were already set, this line would do nothing, so
# output[38]["s"] would keep its previous data.
output[outer_key][inner_key] ||= []
# Add "hum" to the array at output[38]["s"].
output[outer_key][inner_key] << value
end
所以,你實際使用的部分,全部整理:
output = {}
input.each do |outer_key, inner_key, value|
output[outer_key] ||= {}
output[outer_key][inner_key] ||= []
output[outer_key][inner_key] << value
end
在這種情況下, inject
(在1.9中reduce
)是一個很好的工具:
input.inject({}) do |acc, (a, b, c)|
acc[a] ||= {}
acc[a][b] ||= []
acc[a][b] << c
acc
end
它將通過累加器和項目為input
每個項目調用塊一次。 它第一次將參數作為累加器傳遞,后續調用將最后一次調用的返回值作為累加器。
根據您的敏感度,這可能被視為可怕或優雅:
input.inject(Hash.new {|h1,k1| h1[k1] = Hash.new {|h2,k2| h2[k2] = Array.new}}) {|hash,elem| hash[elem[0]][elem[1]].push(elem[2]); hash}
=> {38=>{"s"=>["hum"], "t"=>["foo", "bar"]}, 45=>{"s"=>["hum"], "t"=>["ram"]}, 52=>{"s"=>["hum"], "t"=>["cat", "dog"]}}
理想情況下,更易讀的版本是:
input.inject(Hash.new(Hash.new(Array.new))) {|hash,elem| hash[elem[0]][elem[1]].push(elem[2]); hash}
也就是說,從空哈希開始,默認值等於空哈希值,默認值等於空數組。 然后迭代輸入,將元素存儲在適當的位置。
后一種語法的問題是Hash.new(Hash.new(Array.new))將導致所有散列和數組在內存中具有相同的位置,因此值將被覆蓋。 前一種語法每次都會創建一個新對象,從而得到所需的結果。
問題中給出的示例對於每個元素數組的長度為3,但下面的方法使用遞歸,並且可以用於任意長度。
a = [ [38, "s", "hum", 1],
[38, "t", "foo", 2],
[38, "t", "bar", 3],
[45, "s", "hum", 1],
[45, "t", "ram", 1],
[52, "s", "hum", 3],
[52, "t", "cat", 3],
[52, "t", "dog", 2]
]
class Array
def rep
group_by{|k, _| k}.
each_value{|v| v.map!{|_, *args| args}}.
tap{|h| h.each{|k, v| h[k] = (v.first.length > 1 ? v.rep : v.flatten(1))}}
end
end
p a.rep
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.