2

给定这个数组(从文件生成)

["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]]

哈希没有默认值

4

6 回答 6

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> 哈希[ary.group_by{ |a| a[1] }.map{ |k, v| [k, v.map{ |a,b,c| [a,c] } ] }]
=> {“DM1210”=>[[“扬克斯”,“70.00 美元”],[“斯克兰顿”,“68.76 美元”]],“DM1182”=>[[“扬克斯”,“19.68 澳元”],[ "纳舒厄", "58.58 澳元"], ["卡姆登", "54.64 美元"]]}
irb(main):037:0> ary
=> [["Yonkers", "DM1210", "70.00 USD"], ["Yonkers", "DM1182", "19.68 AUD"], ["Nashua", "DM1182", "58.58 AUD"], ["斯克兰顿”、“DM1210”、“68.76 美元”]、[“卡姆登”、“DM1182”、“54.64 美元”]]

其他几个答案正在使用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在先前未定义键时调用的初始化块。

于 2013-05-31T07:02:54.407 回答
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(您可以在其中选择分组键要累积的值)。

于 2013-05-31T09:19:50.697 回答
3

关于什么

result = trans_data.each_with_object({}) do |arr, hash|
  (hash[arr[1]] ||= []) << [arr[0], arr[2]]
end
于 2013-05-31T06:37:36.450 回答
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]}

为 each_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:
) == []

请注意,在某些情况下,您可以将括号视为隐式: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 问题,也是我第一次想到,更不用说能够将评论变成代码了,哦,我喜欢它吗. 谢谢你的拼图!

于 2013-05-31T06:32:29.953 回答
0

或多或少从方面库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"]]}
于 2013-05-31T10:28:47.580 回答
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"]]}
于 2013-05-31T06:32:00.630 回答