5

我有一个这样的数组

[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

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

4

5 回答 5

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|

于 2013-10-05T18:24:13.203 回答
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 没有实现该方法[]=

于 2013-10-05T18:21:46.033 回答
5

我喜欢reducehash.update. 它返回数组,我不需要该; hash部分:

[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)) { |h, n| h.update(n => h[n].next) }
于 2013-10-05T18:29:31.433 回答
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|}(如上述解决方案之一中使用的那样)并且正在寻找一种m返回接收器或其值的方法。to_i是我能做的最好的(例如,2.to_i => 2),但仅适用于整数。任何人都可以建议一种方法来返回接收器的范围广泛的对象,这些对象的使用to_proc使得它的目的很明显?

于 2013-10-05T19:00:34.623 回答
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}
于 2013-10-05T19:54:00.570 回答