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

Mr.*_*ack 47 ruby

arr = [1,2,1,3,5,2,4]
Run Code Online (Sandbox Code Playgroud)

如何通过排序按组值计算数组?我需要以下输出:

x[1] = 2  
x[2] = 2  
x[3] = 1  
x[4] = 1  
x[5] = 1
Run Code Online (Sandbox Code Playgroud)

Mic*_*ohl 106

x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
Run Code Online (Sandbox Code Playgroud)

  • 建立哈希与算术时,更喜欢`.each_with_object`而不是`inject`.请参阅下面的@ sawa的答案. (3认同)
  • `inject`"将一个累加器"注入一个Enumerable,在我们的例子中是一个默认值为0的Hash.在每次迭代中,我们使用当前元素的键(`e`)将值加1.最后我们返回累加器.http://www.ruby-doc.org/core/classes/Enumerable.html#M001494 (2认同)

saw*_*awa 32

仅适用于红宝石1.9

基本上与迈克尔的答案相同,但稍微短一些:

x = arr.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
Run Code Online (Sandbox Code Playgroud)

在类似的情况下,

  • 当起始元素是可变对象(如Array,, Hash)时String,可以使用each_with_object,如上例所示.
  • 当起始元素是一个不可变对象时Numeric,你必须使用inject如下.

    sum = (1..10).inject(0) {|sum, n| sum + n} # => 55

  • 在角色方面,它更长.在令牌方面,它更短.感谢您的评论. (4认同)

lll*_*lll 15

另一个 - 与其他人类似 - 接近:

result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
Run Code Online (Sandbox Code Playgroud)
  1. 按每个元素的值分组.
  2. 将分组映射到[value,counter]对的数组.
  3. 将paris数组转换为Hash中的键值,即可通过result[1]=2 ....


Eli*_*adL 13

arr.group_by(&:itself).transform_values(&:size)
#=> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}
Run Code Online (Sandbox Code Playgroud)


rub*_*nce 10

x = Hash[arr.uniq.map{ |i| [i, arr.count(i)] }]
Run Code Online (Sandbox Code Playgroud)


Mr.*_*ack 10

中有一个简短的版本ruby 2.7 => Enumerable#tally

[1,2,1,3,5,2,4].tally  #=> { 1=>2, 2=>2, 3=>1, 5=>1, 4=>1 }

# Other possible usage

(1..6).tally { |i| i%3 }   #=> { 0=>2, 1=>2, 2=>2 }

Run Code Online (Sandbox Code Playgroud)


fuz*_*oup 9

每当你发现有人声称某种东西在这种原始程序中是最快的时候,我总是觉得有趣的是要确认,因为没有确认我们大多数人真的只是在猜测.所以我在这里采用了所有方法并对它们进行了基准测试.

我从一个网页中提取了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
Run Code Online (Sandbox Code Playgroud)

然后一点点红宝石来找出答案:

times = [0.00077, 0.000232, 0.00076, 0.000107, 0.000109, 0.000143].min
==> 0.000107
Run Code Online (Sandbox Code Playgroud)

所以实际上最快的方法,当然是ymmv:

links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
Run Code Online (Sandbox Code Playgroud)


kur*_*umi 5

我相信有更好的方法,

>> arr.sort.group_by {|x|x}.each{|x,y| print "#{x} #{y.size}\n"}
1 2
2 2
3 1
4 1
5 1
Run Code Online (Sandbox Code Playgroud)

根据需要将x和y值分配给哈希.


Tho*_*Kra 5

这应该做

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}
Run Code Online (Sandbox Code Playgroud)


小智 5

仅供记录,我最近在这里阅读.我的解决方案是:Object#tap

Hash.new(0).tap{|h| arr.each{|i| h[i] += 1}}

#tap方法将调用者传递给块然后返回它.当你必须逐步构建数组/哈希时,这非常方便.