如何根据使用< =>订购一组对象?在数组属性?

时间:2011-06-09 15:24:18

标签: ruby-on-rails ruby

我有一组2个或更多我想订购的物品。我一直这样做:

card.max_by{|strength| strength.score

分数是一个整数分数,我根据一些任意规则计算出来。我知道这将是我要重构的东西,所以现在我正在这样做。给手分数的“干净”方法是给它一系列值,比如

foo.score = [9,3,nil,4]

将它与另一只可能有像

这样的数组的手相比较
bar.score = [5,10,12,12]

和foo< => bar会告诉我foo是更大的数组,所以它应该由max_by返回。问题是max_by显然不会对数组进行比较。有没有其他方法可以按数组值排序?

2 个答案:

答案 0 :(得分:2)

max_by可以正常使用数组“属性”:

# Phrogz's example
Hand = Struct.new(:score)
hands = [
 Hand.new([9,3,0,4]),
 Hand.new([8,8,8,8]),
 Hand.new([5,10,12,12]),
 Hand.new([1,99,99,99])
]
#
hands.max_by(&:score)    # => #<struct Hand score=[9, 3, 0, 4]>

但是,如果数组可以包含不相互比较的nils或其他值,<=>可能会返回nil而max_by可能会失败。

答案 1 :(得分:1)

如果它只是您想要的基于数组的排序(您确实需要太空船运营商)并且您希望通过排序找到“最大”,那么:

Hand = Struct.new(:score)

hands = [
 Hand.new([9,3,0,4]),
 Hand.new([8,8,8,8]),
 Hand.new([5,10,12,12]),
 Hand.new([1,99,99,99])
]

biggest = hands.sort_by(&:score).last
p biggest
#=> #<struct Hand score=[9, 3, 0, 4]>

但是,如果你真的只需要找到最大的手,那么以下将比订购整个阵列更有效:

biggest = hands.inject do |max,hand|
  if (max.score <=> hand.score) == -1
    hand
  else
    max
  end
end
p biggest
#=> #<struct Hand score=[9, 3, 0, 4]>

编辑:阅读您的评论,如果您确实需要多个匹配的值,我会这样做:

Hand = Struct.new(:name,:score) do 
  MAX_SCORE_PART = 13 # 13 ranks in a suit
  def numeric_score
    value = 0
    score.each_with_index do |part,i|
      value += part.to_i * MAX_SCORE_PART**(score.length-i-1)
    end
    value
  end
end

hands = [
  Hand.new('Bob', [9,3,nil,4] ),
  Hand.new('Jim', [8,8,8,8]   ),
  Hand.new('Foo', [5,10,12,12]),
  Hand.new('Sam', [1,13,13,13]),
  Hand.new('Zak', [9,3,0,4]   ),
]

require 'pp'
by_score = hands.group_by(&:numeric_score)
pp by_score
#=> {20284=>
#=>   [#<struct Hand name="Bob", score=[9, 3, nil, 4]>,
#=>    #<struct Hand name="Zak", score=[9, 3, 0, 4]>],
#=>  19040=>[#<struct Hand name="Jim", score=[8, 8, 8, 8]>],
#=>  12843=>[#<struct Hand name="Foo", score=[5, 10, 12, 12]>],
#=>  4576=>[#<struct Hand name="Sam", score=[1, 13, 13, 13]>]}

pp by_score[by_score.keys.max]
#=> [#<struct Hand name="Bob", score=[9, 3, nil, 4]>,
#=>  #<struct Hand name="Zak", score=[9, 3, 0, 4]>]

对于基于inject的实施:

def numeric_score
  score.enum_for(:inject,0).with_index do |(val,part),i|
    val += part.to_i * MAX_SCORE_PART**(score.length-i-1)
  end
end