[英]How can I refactor converting this array into a Hash
給定此數組(從文件生成)
["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]]
替換它
沒有哈希的默認值
看起來人們需要了解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!
可以相當簡潔地寫出這個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
采用初始化塊,當先前未定義某個鍵時,該初始化塊被調用。
使用抽象的功能方法來自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
變體(你選擇分組鍵和要累積的值)。
關於什么
result = trans_data.each_with_object({}) do |arr, hash|
(hash[arr[1]] ||= []) << [arr[0], arr[2]]
end
注意:接受的答案是最好的答案,但我真的很高興我使用的奇怪的真棒和我如何解釋它:
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
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問題,這是我第一次想到,更不用說能夠把評論變成代碼了,哦,我喜歡它嗎? 。 謝謝你的拼圖!
試試這個
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"]]}
或多或少從facet庫 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"]]}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.