Given this array (generated from a file)
["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"]]
I convert it to a hash indexed by the second element (the sku) with the code below:
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
This outputs the hash in the format I want it:
{"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
I don't feel like my code is... clean. Is there a better way of accomplishing this?
EDIT: So far I was able to replace it with: (result[arr[1]] ||= []) << [arr[0], arr[2]]
With no default value for the hash
Looks like people need to learn about 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) }
Which results in:
=> {"DM1210"=>[["Yonkers", "70.00 USD"], ["Scranton", "68.76 USD"]], "DM1182"=>[["Yonkers", "19.68 AUD"], ["Nashua", "58.58 AUD"], ["Camden", "54.64 USD"]]}
It's possible to write this fairly succinctly without slice!, allowing ary to remain unchanged, and without the need to pull in any extra classes or modules:
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"]]
Several other answers are using each_with_object, which removes the need to coerce the returned array to a hash using Hash[...]. Here's how I'd use each_with_object to avoid a bunch of line-noise inside the block as they try to initialize unknown keys:
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"]]}
This takes advantage of Hash.new taking an initialization block that gets called when a key hasn't been previously defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With