按唯一值排序哈希

时间:2013-10-13 00:59:40

标签: ruby

我正在使用cinch IRC bot framework创建一个irc机器人,智能地从频道中踢出用户。为此,我需要了解机器人的用户模式,并将其与用户的用户模式进行比较。有4种模式很重要:q,a,o,h。

基本上:所有者(q)>管理员(a)>运营商(o)>半操作(h)> *。用户可以拥有多个用户模式。有人可以使用qao模式,但我不在乎用户是否是所有者和管理员,我只关心用户是所有者。

Cinch提供频道中所有已知用户及其用户模式的哈希值。例如:

Users{"bot" => {"a", "o"}, "fred" => {"q", "o"}, "mike" => "o", "larry" => "v"}

我想要做的是,尽可能简洁地采取一些逻辑,可以解释机器人何时,例如,“ao”,fred是“qo”,mike是“o”,然后说“好的。我是管理员,迈克是一名操作员,所以我可以踢迈克,但弗雷德是一个拥有者所以我不能踢弗雷德。“

我对实现的想法是混乱的(如果其他的话循环涉及很多......)我知道必须有更好的方法。另外,我不确定如何遍历键值并忽略某个点的值。我觉得好像我的想法会迭代,遇到“ao”并设置为管理员,然后重置为运营商,这不是我需要的。

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

@ I,@ carolclarinet有正确的想法,你可能不需要更多。但是,我想提一个更精细的方法来处理这个问题,你可能会发现它在相关的应用程序中很有用。首先,假设您的哈希看起来像这样:

h = {"bot" => ["a", "o"], "fred" => ["q", "o"], "mike" => ["o"], "larry" => ["h"]}

这就是你所拥有的,除了我已经制作了所有单个哈希值数组。显然,将它变成这种形式会很简单。下面我将展示如何将值数组重新定义为我称为UserModes的新类的实例,这是Array的子类。通过这样做,您可以以非常自然的方式比较值数组:

h["bot"]                      # => ["a", "o"]
h["fred"]                     # => ["q", "o"]
h["bot"].can_kick? h["fred"]  # => false

h["mike"]                     # => ["o"]
h["bot"].can_kick? h["mike"]  # => true

h["larry"]                    # => ["h"]
h["bot"].can_kick? h["larry"] # => true

kickees[]
h.each {|k,v| kickees << k if k!="bot" && h["bot"].can_kick?(h[k])} # final () req'd
  # kickees => ["mike", "larry"]  

如果添加其他方法:

h["bot"] < h["fred"]   # => true
h["bot"] >= h["fred"]  # => false

h["bot"] == h["mike"]  # => false
h["bot"] > h["mike"]   # => true

h["bot"] <= h["larry"] # => false
h["bot"] >= h["larry"] # => true

这是类定义:

class UserModes < Array

  # Change > to >= in in can_kick? if I've misunderstood the kicking rule
  def can_kick?(other) rating(self) > rating(other) end

  # Add any or all of the following if useful to you: 
  def <=>(other) rating(self) <=> rating(other) end
  def ==(other)  rating(self) ==  rating(other) end
  def <(other)   rating(self) <   rating(other) end
  def >(other)   rating(self) >   rating(other) end
  def <=(other)  rating(self) <=  rating(other) end
  def >=(other)  rating(self) >=  rating(other) end

  private

  def rating a
    case
    when (a.include? ?q) then 3
    when (a.include? ?a) then 2
    when (a.include? ?o) then 1
    when (a.include? ?v) then 0
    else
      # raise exception
    end
  end
end

您会看到所有公共方法都使用私有方法rating,它将数组转换为数字分数,沿着@ carolclarinet的答案。您可以将Array的实例中的每个哈希值转换为UserModes的实例,如下所示:

h.each_key {|k| h[k] = UserModes.new(h[k])}

我们可以确认这是按预期工作的:

h["fred"].class => UserModes

您可以将值h视为普通数组,但现在您还可以使用方法can_kick?,如果需要,还可以使用其他几个数组。其中一些(< <= >= >)没有为Array个对象定义;其他人(<=> ==)必须定义为覆盖Array中的同名方法。

答案 1 :(得分:0)

我认为这就像采用机器人拥有的“最佳”用户模式并将其与您想要踢的用户所使用的“最佳”用户模式进行比较。如果机器人大于或等于其他用户,那么机器人可以踢用户。

在这种情况下,您需要一个获取用户模式列表的方法,并且可以返回与最大值相对应的数值。一种实现可能是:

def greatest_usermode(array_of_usermodes)
  if array_of_usermodes.include?("q")
    4
  elsif array_of_usermodes.include?("a")
    3
  elsif array_of_usermodes.include?("o")
    2
  elsif array_of_usermodes.include?("h")
    1
  else 
    0
  end
end

然后,您可以定义一个方法,该方法仅使用数学>=运算符来比较此方法为机器人和相关用户返回的数字。

还有很多其他方法可以做到,但这是一种非常简单明了的方法。