给定此数组(从文件生成)
["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"],
["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"],
["Camden", "DM1182", "54.64 USD"]]
我将它转换为由第二个元素(sku)索引的哈希,代码如下:
result = Hash.new([])
trans_data.each do |arr|
result[arr[1]].empty? ? result[arr[1]] = [[arr[0], arr[2]]] : result[arr[1]] << [arr[0], arr[2]]
end
result
这将以我想要的格式输出哈希:
{"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
我不觉得我的代码是......干净。有没有更好的方法来实现这个目标?
编辑:到目前为止,我可以将其替换为:(result[arr[1]] ||= []) << [arr[0], arr[2]]
散列没有默认值
答案 0 :(得分:7)
看起来人们需要了解group_by
:
ary = [
["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"],
["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"],
["Camden", "DM1182", "54.64 USD"]
]
hash = ary.group_by{ |a| a.slice!(1) }
结果是:
=> {"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
可以在没有slice!
的情况下相当简洁地编写,允许ary
保持不变,并且无需引入任何额外的类或模块:
irb(main):036:0> Hash[ary.group_by{ |a| a[1] }.map{ |k, v| [k, v.map{ |a,b,c| [a,c] } ] }] => {"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]} irb(main):037:0> ary => [["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"], ["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"], ["Camden", "DM1182", "54.64 USD"]]
其他几个答案正在使用each_with_object
,这消除了使用Hash[...]
将返回的数组强制转换为哈希的需要。以下是我尝试初始化未知密钥时使用each_with_object
来避免块内部的线噪声的方法:
ary.each_with_object(Hash.new{ |h,k| h[k] = [] }) { |(a, b, c), h|
h[b] << [a, c]
}
=> {"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
这利用Hash.new
获取一个初始化块,当先前未定义一个键时,该初始化块被调用。
答案 1 :(得分:4)
使用Facets中的抽象Enumerable#map_by的功能方法:
require 'facets'
records.map_by { |name, key, price| [key, [name, price]] }
#=> {"DM1210"=>[["Yonkers", "70.00 USD"], ... }
遗憾的是Ruby不会在核心内发送map_by
,它是Enumerable#group_by
的非常有用(因为它未知)的变体(您选择分组键和积累的价值。)
答案 2 :(得分:3)
怎么样?
result = trans_data.each_with_object({}) do |arr, hash|
(hash[arr[1]] ||= []) << [arr[0], arr[2]]
end
答案 3 :(得分:2)
注意:接受的答案是最好的答案,但我真的很高兴我使用的奇怪的真棒和我如何解释它:
arr = [["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"],
["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"],
["Camden", "DM1182", "54.64 USD"]]
arr.each_with_object({}){|(a, b, c), hash| (hash[b] || hash[b]=[]).push [a,c]}
为every_with_object推荐给年长的上帝!
说明:
这里有两件古怪的事情。第一个是(a, b, c)
魔法,我觉得它的作用是这样的:
(
#This bit:
arr.collect{|(a,b,c)| "#{a}#{b}#{c}"}
) - (
#Is equivalent to this bit:
(0..arr.size).collect {|i|
(a,b,c) = arr[i] #=> (a,b,c) = ["Yonkers", "DM1210", "70.00 USD"]
"#{a}#{b}#{c}"
}
#as you can see, they generate identical arrays:
) == []
请注意,在某些情况下,您可以将parens视为隐式:arr.collect{|a, b, c| [a, b, c]} == arr
第二个古怪的事情:
(hash[b] || hash[b]=[]).push(...)
请记住,Ruby中的所有内容都是表达式和引用。
[
(hash[:a] || "foo") == (nil || "foo"),
(hash[:b]=[]) == [],
(hash[:b]=[]) === hash[:b],
(hash[:b] || "foo") == ([] || "foo"),
] == [true, true, true, true]
hash[b]
,当密钥不存在时,求值为nil
(这是假的),因此我们评估并返回后半部分:hash[b]=[]
,它返回赋值的值,这是hash[b]
现在引用的数组,因此我们可以继续使用它,hash[b]
将[仍然是]引用更新后的数组。
:d
PS - 我认为,这是我第一次回答的Ruby问题,这是我第一次想到,更不用说能够将评论转化为代码了,哦,我做的我喜欢。谢谢你的谜题!
答案 4 :(得分:0)
试试这个
arr = [["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"], ["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"], ["Camden", "DM1182", "54.64 USD"]]
hash = Hash.new{|h,k| h[k] = []}
arr.each{|a| hash[a[1]].push([a[0],a[2]])}
hash => {"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
答案 5 :(得分:0)
或多或少从facets library tokland中提取:
ary = [["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"], ["Nashua", "DM1182", "58.58 AUD"], ["Scranton", "DM1210", "68.76 USD"], ["Camden", "DM1182", "54.64 USD"]]
hash = {}
ary.each{ |a,b,c| (hash[b] ||= []) << [a,c] }
hash
# => {"Camden"=>[["DM1182", "54.64 USD"]], "Nashua"=>[["DM1182", "58.58 AUD"]], "Scranton"=>[["DM1210", "68.76 USD"]], "Yonkers"=>[["DM1210", "70.00 USD"], ["DM1182", "19.68 AUD"]]}