如何在不使用循环的情况下按数组进行分组
arr = [1,2,1,3,5,2,4]
我如何可以按sorting值sorting数组? 我需要以下输出:
x[1] = 2 x[2] = 2 x[3] = 1 x[4] = 1 x[5] = 1
x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
只有在Ruby 1.9下才可用
基本上和迈克尔的答案一样 ,但稍微短一些:
x = arr.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
在类似的情况下,
- 当起始元素是一个可变对象,如
Array
,Hash
,String
,可以使用each_with_object
,如上例所示。 -
当起始元素是一个不可变的对象,如
Numeric
,你必须使用inject
如下。sum = (1..10).inject(0) {|sum, n| sum + n} # => 55
x = Hash[arr.uniq.map{ |i| [i, arr.count(i)] }]
每当你发现有人认为在这种原始程序上某事是最快的时候,我总是觉得很有意思,因为没有确认,我们大多数人都只是在猜测。 所以我把这里所有的方法都拿来做了基准testing。
我从网页中提取了120个链接,我需要按计数进行分组,并使用秒= Benchmark.realtime do循环来实现所有这些链接,并获得所有时间。
假设链接是我需要计数的数组的名称:
#0.00077 seconds = Benchmark.realtime do counted_links = {} links.each { |e| counted_links[e] = links.count(e) if counted_links[e].nil?} end seconds #0.000232 seconds = Benchmark.realtime do counted_links = {} links.sort.group_by {|x|x}.each{|x,y| counted_links[x] = y.size} end #0.00076 seconds = Benchmark.realtime do Hash[links.uniq.map{ |i| [i, links.count(i)] }] end #0.000107 seconds = Benchmark.realtime do links.inject(Hash.new(0)) {|h, v| h[v] += 1; h} end #0.000109 seconds = Benchmark.realtime do links.each_with_object(Hash.new(0)) {|e, h| h[e] += 1} end #0.000143 seconds = Benchmark.realtime do links.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h } end
然后有一点ruby来找出答案:
times = [0.00077, 0.000232, 0.00076, 0.000107, 0.000109, 0.000143].min ==> 0.000107
所以实际上最快的方法,ymmv当然是:
links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
另一个类似于其他的方法是:
result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
- 按每个元素的值进行分组。
- 将分组映射到[数值,计数器]对的数组。
- 将巴黎数组转换为哈希内的键值,即可通过
result[1]=2 ...
。
我相信有更好的方法,
>> arr.sort.group_by {|x|x}.each{|x,y| print "#{x} #{y.size}\n"} 1 2 2 2 3 1 4 1 5 1
根据需要将x和y值分配给哈希值。
这应该做到这一点
arr = [1,2,1,3,5,2,4] puts arr.inject(Hash.new(0)) {|h, v| h[v] += 1; h} #=> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}
只是为了logging,我最近读了关于Object#tap
这里 。 我的解决scheme是:
Hash.new(0).tap{|h| arr.each{|i| h[i] += 1}}
#tap
方法将调用者传递给该块,然后将其返回。 当你必须增量构build一个数组/散列时,这非常方便。
arr = [1,2,1,3,5,2,4] r = {} arr.each { |e| r[e] = arr.count(e) if r[e].nil?}
输出
pr #==> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}