我有一个像这样的数组
[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
我可以像这样设置初始值,还是我错了?
答案 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)
我喜欢将reduce
与hash.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|}
(在上面的一个解决方案中使用),并且正在寻找返回接收器或其值的方法m
。 to_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}