使用Ruby的数组中对象的频率

时间:2010-03-17 13:38:20

标签: ruby arrays

如果我有一个球列表,每个球都有颜色属性。我怎样才能干净地获得最常用颜色的球列表。

[m1,m2,m3,m4]

说,

        m1.color = blue
        m2.color = blue
        m3.color = red
        m4.color = blue

[m1,m2,m4]是颜色最常见的球列表

我的方法是做:

[m1,m2,m3,m4].group_by{|ball| ball.color}.each do |samecolor|
  my_items = samecolor.count
end

其中count定义为

class Array
  def count
  k =Hash.new(0)
  self.each{|x|k[x]+=1}
  k
  end
end

my_items将是一个相同颜色组的频率哈希值。我的实施可能是错误的,我觉得必须有一个更好,更聪明的方式。 有什么想法吗?

7 个答案:

答案 0 :(得分:5)

您找到group_by但遗漏了max_by

max_color, max_balls = [m1,m2,m3,m4].group_by {|b| b.color}.max_by {|color, balls| balls.length}

答案 1 :(得分:2)

您的代码不错,但效率低下。如果我是你,我会寻求一个只在你的数组中迭代一次的解决方案,如下所示:

balls = [m1, m2, m3, m4]
most_idx = nil

groups = balls.inject({}) do |hsh, ball|
  hsh[ball.color] = [] if hsh[ball.color].nil?
  hsh[ball.color] << ball

  most_idx = ball.color if hsh[most_idx].nil? || hsh[ball.color].size > hsh[most_idx].size 
  hsh
end

groups[most_idx] # => [m1,m2,m4]

这与group_by基本相同,但同时它会对组进行计数并记录哪个组最大(most_idx)。

答案 2 :(得分:2)

怎么样:

color,balls = [m1,m2,m3,m4].group_by { |b| b.color }.max_by(&:size)

答案 3 :(得分:2)

我是这样做的。基本思想使用inject将值累积为哈希值,来自“The Ruby Cookbook”中的“12 - 构建直方图”。

#!/usr/bin/env ruby

class M
  attr_reader :color
  def initialize(c)
    @color = c
  end
end

m1 = M.new('blue')
m2 = M.new('blue')
m3 = M.new('red')
m4 = M.new('blue')

hash = [m1.color, m2.color, m3.color, m4.color].inject(Hash.new(0)){ |h, x| h[x] += 1; h } # => {"blue"=>3, "red"=>1}
hash = [m1, m2, m3, m4].inject(Hash.new(0)){ |h, x| h[x.color] += 1; h } # => {"blue"=>3, "red"=>1}

有两种不同的方法,取决于您希望inject()了解对象的知识量。

答案 4 :(得分:2)

这会按频率生成反向排序的球列表

balls.group_by { |b| b.color }
  .map { |k, v| [k, v.size] }
  .sort_by { |k, count| -count}

答案 5 :(得分:2)

两部分,我将使用你的怪球示例,但也将包括我自己的rails示例

ary = [m1,m2,m3,m4]
colors = ary.each.map(&:color) #or ary.each.map {|t| t.color }
Hash[colors.group_by(&:w).map {|w, ws| [w, ws.length] }]
#=> {"blue" => 3, "red" => 1 }

我的ActiveRecord示例

stocks = Sp500Stock.all
Hash[stocks.group_by(&:sector).map {|w, s| [w, s.length] }].sort_by { |k,v| v }
#=> {"Health Care" => 36, etc]

答案 6 :(得分:0)

myhash = {}

mylist.each do |ball|
  if myhash[ball.color]
    myhash[ball.color] += 1
  else
    myhash[ball.color] = 1    
  end
end

puts myhash.sort{|a,b| b[1] <=> a[1]}