如何迭代集合的散列

How to iterate over a hash of sets

我有一个以游戏玩家的名字为键的散列,以及一个包含每个玩家被击败的其他玩家的名字作为值的集合对象。集合初始化为空,然后随机挑选两名玩家并互相战斗,获胜者将另一个玩家密钥添加到其集合中,代表战胜他们。胜利一旦确立,玩家就不能再互相争斗。最后,一个打败另一个的玩家也应该在他的设置上有被另一个打败的玩家,以及被他们打败的其他人,等等。

在给定的游戏情况下,有 5 名玩家,Claudia、Rosa、Bob、Carlos 和 Tim。假设 Rosa 打败了 Bob,然后 Bob 打败了 Carlos,Claudia 打败了 Tim,此时给定的数据将如下所示:

@match_data = {"claudia"=>#<Set: {"tim">,
               "rosa"=>#<Set: {"bob", "carlos"}>,
               "bob"=>#<Set: {"carlos"},
               "carlos"=>#<Set: {},
               "tim"=>#<Set: {}}

所以在这一点上,克劳迪娅战胜了蒂姆,所以他们不会再有一场战斗,罗莎战胜了鲍勃和卡洛斯,因此她永远不需要再和他们战斗,鲍勃战胜了卡洛斯,他们赢了战斗也一样。想象一下,在这之后,Bob打败了Claudia,此时想要的数据应该是:

@match_data = {"claudia"=>#<Set: {"tim"}>,
               "rosa"=>#<Set: {"bob", "carlos", "claudia", "tim"}>,
               "bob"=>#<Set: {"carlos", "claudia", "tim"},
               "carlos"=>#<Set: {},
               "tim"=>#<Set: {}}

当Bob打败Claudia时,他不仅获得了对她的胜利,还获得了对之前击败过的Tim的胜利,更重要的是,Rosa获得了对Claudia和Tim的胜利,因为她已经获得了对Bob的胜利.所以在这种情况下,Rosa 赢得了比赛,Bob 是第二名,其他人仍在比赛中。这可能会变得更复杂,因为玩家数量不受限制。

我试图克服的问题是创建一个函数来更新游戏状态。每次比赛结束时,此代码都会查看 match_data 并找出比赛结果获得了哪些胜利。这段代码是我的尝试之一:

def update_set(key)
  store = Set.new
  @match_data[key].each { |value| store.merge @match_data[value] }
  if store.size > 0
    store.each { |value| update_set(value) }
  end
  @match_data[key].merge store
end

@match_data.sort.map do |key, set|
  update_set(key)
end

我的其他尝试得到一个错误,说我走得太深,或者我不能在循环中迭代散列。或者,我可以尝试使用另一种数据结构,但我不知道是哪一个。

编辑:为了清楚起见,我编辑了原始问题,因为在 运行 编译我的代码时不清楚我想要的输出是什么。尽管如此,@ddubs 给出的答案非常适合我的代码。

此外,我想指出我给出的示例中的一个错误,在我的程序中,数据永远无法真正完全按照第一个代码片段所示进行转换,因为所需的函数 运行 总是在每个代码片段之间战斗,刷新词典状态。我想我不想写太多无用的信息,让我的问题变得比需要的更复杂,但我最终压制了重要信息。

如果需要我可以post整个程序逻辑,但我不认为这是必要的。

EDIT2:添加了整个游戏逻辑,因为我发现它仍然不够清晰,@ddubs 给出的解决方案仍然有效,但也许有人可以提供更好的东西,现在已经提供了所有信息.

Set 看起来有点奇怪,或者可能只是 @match_data 对象的布局。无论如何,这个功能必须应用于每个玩家及其相应的集合。它还需要您将匹配数据也传递给它:

def compile_sets(set, data, results = Set.new)
  set.each do |s|
    results << s
    results << compile_sets(data[s], data, results)
  end
  results.flatten
end

用法示例:

results = @match_data.map { |player,set| { player => compile_sets(set, @match_data) } }

p results

Returns:

[
  {"carlos"=>#<Set: {}>},
  {"bob"=>#<Set: {"carlos", "claudia"}>},
  {"lisa"=>#<Set: {"bob", "carlos", "claudia"}>},
  {"tim"=>#<Set: {"lisa", "bob", "carlos", "claudia"}>},
  {"mary"=>#<Set: {"lisa", "bob", "carlos", "claudia"}>},
  {"rosa"=>#<Set: {"tim", "lisa", "bob", "carlos", "claudia"}>}, 
  {"claudia"=>#<Set: {}>}
]