如何在不使用循环的情况下按数组进行分组

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} 

在类似的情况下,

  • 当起始元素是一个可变对象,如ArrayHashString ,可以使用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]}] 
  1. 按每个元素的值进行分组。
  2. 将分组映射到[数值,计数器]对的数组。
  3. 将巴黎数组转换为哈希内的键值,即可通过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}