ruby-如何在不使用循环的情况下按数组中的计数分组

arr = [1,2,1,3,5,2,4]

如何按组值对数组进行排序? 我需要以下输出:

x[1] = 2  
x[2] = 2  
x[3] = 1  
x[4] = 1  
x[5] = 1
Mr. Black asked 2020-02-13T05:40:45Z
10个解决方案
107 votes
x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
Michael Kohl answered 2020-02-13T05:41:00Z
32 votes

仅在Ruby 1.9下可用

基本上与迈克尔的答案相同,但方法略短一些:

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

在类似情况下,

  • 当起始元素是可变对象(例如sum = (1..10).inject(0) {|sum, n| sum + n} # => 55injectString)时,可以像上面一样使用each_with_object
  • 当起始元素是诸如sum = (1..10).inject(0) {|sum, n| sum + n} # => 55之类的不可变对象时,必须使用以下inject

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

sawa answered 2020-02-13T05:41:42Z
13 votes

另一种-与其他类似-方法:

result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
  1. 按每个元素的值分组。
  2. 将分组映射到[value,counter]对数组。
  3. 将巴黎的数组转换为哈希中的键值,即可以通过result[1]=2 ...访问。
lllllll answered 2020-02-13T05:42:15Z
10 votes
x = Hash[arr.uniq.map{ |i| [i, arr.count(i)] }]
rubyprince answered 2020-02-13T05:42:30Z
9 votes

每当您发现有人断言某种类型的原始例程中最快的事物时,我总是会发现它很有趣,可以确认这一点,因为如果没有确认,我们大多数人实际上只是在猜测。 因此,我在这里采用了所有方法并对它们进行了基准测试。

我从一个网页中提取了120个链接,这些链接需要按计数进行分组,并使用秒= Benchmark.realtime do loop来实现所有这些链接,并且获得了所有的时间。

假设链接是我需要计算的数组的名称:

#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

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

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}
fuzzygroup answered 2020-02-13T05:43:09Z
9 votes

有一个简短的版本是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 }

Mr. Black answered 2020-02-13T05:43:30Z
5 votes

我相信有更好的方法,

>> 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值分配给哈希。

kurumi answered 2020-02-13T05:43:54Z
5 votes

仅作记录,我最近在这里阅读了大约#tap。 我的解决方案是:

#tap

#tap方法将调用者传递给该块,然后将其返回。 当您必须增量构建阵列/哈希时,这非常方便。

erasing answered 2020-02-13T05:44:23Z
4 votes

这应该做

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}
ThoKra answered 2020-02-13T05:44:42Z
1 votes
arr = [1,2,1,3,5,2,4]
r = {}
arr.each { |e| r[e] = arr.count(e) if r[e].nil?}

产出

p r
#==> {1=>2, 2=>2, 3=>1, 5=>1, 4=>1}
thebugfinder answered 2020-02-13T05:45:02Z
translate from https://stackoverflow.com:/questions/5470725/how-to-group-by-count-in-array-without-using-loop