将Ruby数组元素转换为带索引的哈希计数

时间:2016-12-11 06:44:46

标签: ruby

给出Ruby中的二维数组:

[ [1, 1, 1],
  [1, 1],
  [1, 1, 1, 1],
  [1, 1]
]

我想创建一个Hash,其中键是每个内部数组的计数,值是原始数组的索引数组,其内部数组大小具有特定计数。产生的哈希将是:

{ 2 => [1, 3], 3 => [0], 4 => [2] }

如何在Ruby中简洁地表达这个功能?我正在尝试类似于Hash.new([]).tap { |h| array.each_with_index { |a, i| h[a.length] << i } }的东西,但生成的哈希是空的。

2 个答案:

答案 0 :(得分:5)

您的代码存在两个问题。第一个是当h为空并且你写h[2] << 1时,由于h没有密钥2h[2]会返回默认值,所以此表达式变为[] << 1 #=> [1],但[1]未附加到哈希,因此不会添加任何键和值。

您需要撰写h[2] = h[2] << 1 1 。如果这样做,您的代码将返回h #=> {3=>[0, 1, 2, 3], 2=>[0, 1, 2, 3], 4=>[0, 1, 2, 3]}。不幸的是,这仍然是不正确的,这使我们遇到了代码的第二个问题:您没有正确定义新创建的哈希默认值。

首先请注意

h[3].object_id
  #=> 70113420279440 
h[2].object_id
  #=> 70113420279440 
h[4].object_id
  #=> 70113420279440 

啊哈,这三个值都是同一个对象!当new没有密钥[]时,h[k]会返回h个参数k。问题是,为添加到散列的所有键k返回相同的数组,因此您将为第一个新键添加一个空值数组的键值对,然后为下一个新密钥的相同数组添加第二个键值对,依此类推。请参阅下文,了解如何定义哈希值。

通过这两个更改,您的代码可以正常工作,但我建议您按如下方式编写。

arr = [ [1, 1, 1], [1, 1], [1, 1, 1, 1], [1, 1] ]

arr.each_with_index.with_object(Hash.new {|h,k| h[k]=[]}) { |(a,i),h|
  h[a.size] << i }
  #=> {3=>[0], 2=>[1, 3], 4=>[2]} 

使用Hash::new形式使用块来计算哈希的默认值(即哈希值h[k]返回的值{ {1}}没有密钥h),

k

这两者实际上都是如下:

arr.each_with_index.with_object({}) { |(a,i),h| (h[a.size] ||= []) << i }
  #=> {3=>[0], 2=>[1, 3], 4=>[2]} 

另一种方法是在为每个内部数组获取索引后,使用Enumerable#group_by对数组大小进行分组。

h = {}
arr.each_with_index do |a,i|
  sz = a.size
  h[sz] = [] unless h.key?(sz)
  h[a.size] << i
end
h #=> {3=>[0], 2=>[1, 3], 4=>[2]} 

1表达式h = arr.each_with_index.group_by { |a,i| a.size } #=> {3=>[[[1, 1, 1], 0]], # 2=>[[[1, 1], 1], [[1, 1], 3]], # 4=>[[[1, 1, 1, 1], 2]]} h.each_key { |k| h[k] = h[k].map(&:last) } #=> {3=>[0], 2=>[1, 3], 4=>[2]} 使用方法Hash#[]=Hash#[],这就是h[2] = h[2] << 1左侧的h[2]未返回的原因默认值。或者可以将此表达式写为=

答案 1 :(得分:3)

arry = [ [1, 1, 1],
         [1, 1],
         [1, 1, 1, 1],
         [1, 1]
       ]

h = {}
arry.each_with_index do |el,i|
  c = el.count
  h.has_key?(c) ? h[c] << i : h[c] = [i]
end

p h

这会给你

{3=>[0], 2=>[1, 3], 4=>[2]}