在减少数组时使用Hash.new作为初始值

时间:2013-10-05 18:18:16

标签: ruby hash reduce

我有一个像这样的数组

[1,1,2,3,3,3,4,5,5]

我想计算每个号码的出现次数,我正试图这样做

[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |hash,number| hash[number] += 1 }

问题是当我尝试运行它时出现以下错误

NoMethodError: undefined method `[]=' for 1:Fixnum
    from (irb):6:in `block in irb_binding'
    from (irb):6:in `each'
    from (irb):6:in `reduce'
    from (irb):6

我可以像这样设置初始值,还是我错了?

5 个答案:

答案 0 :(得分:7)

您可以使用each_with_object获得更清晰的语法

[1,1,2,3,3,3,4,5,5].each_with_object(Hash.new(0)) { |number, hash| hash[number] += 1 }

请注意,参数的顺序是反向的,即|number, hash|

答案 1 :(得分:6)

您可以使用reduce,但如果您愿意,则必须再次返回哈希值:

[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) do |hash,number|
  hash[number] += 1 
  hash
end

如果不这样,将返回hash[number]的值。哈希赋值的值是值本身。该块基于您之前返回的值。

这意味着,在第一次之后它会尝试这样的事情:1[1] += 1,这当然不起作用,因为Fixnums没有实现方法[]=

答案 2 :(得分:4)

我喜欢将reducehash.update一起使用。它返回数组,我不需要; hash部分:

[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |h, n| h.update(n => h[n].next) }

答案 3 :(得分:2)

您的问题已得到解答,但这是另一种让猫皮肤化的方法:

[编辑采纳@David的建议。]

a = [1,1,2,3,3,3,4,5,5]
Hash[a.group_by(&:to_i).map {|g| [g.first, g.last.size]}]

这也有效:

a.group_by{|e| e}.inject({}) {|h, (k,v)| h[k] = v.size; h}

并且可以通过采用@ spickermann使用update(或它的同义词,merge!)来改进,以最终消除那些恼人的; h

a.group_by{|e| e}.inject({}) {|h, (k,v)| h.merge!(k => v.size)}

以前我曾经:

Hash[*a.group_by(&:to_i).to_a.map {|g| [g.first, g.last.size]}.flatten]

我不喜欢to_i。我想使用to_proc代替...group_by {|x| x|}(在上面的一个解决方案中使用),并且正在寻找返回接收器或其值的方法mto_i是我能做的最好的事情(例如2.to_i => 2),但仅适用于整数。任何人都可以建议一种方法,返回接收器的各种对象,其使用to_proc使其目的明显吗?

答案 4 :(得分:1)

这很简单:

[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}
#=> [[1, 2], [2, 1], [3, 3], [4, 1], [5, 2]]

如果Hash对象需要结果

Hash[[1,1,2,3,3,3,4,5,5].group_by {|e| e}.collect {|k,v| [k,v.count]}]
#=> {1=>2, 2=>1, 3=>3, 4=>1, 5=>2}