为什么仅更改哈希中的一个值?

时间:2019-11-17 13:59:53

标签: ruby ruby-hash

我正在制作一个简单的RPG作为学习项目,并且与角色创建者的一部分有关。

此代码应确定分配给player[:caste][:skill]player[:sub][:skill]的技能字符串,然后将player[:skills]中每个技能的值增加2。无论分配了什么字符串,此代码都应起作用player[:caste][:skill]player[:sub][:skill],只要等于player[:skills].to_s

当前,它仅将更改应用于player[:skills][:endurance]而不是player[:skills][:athletics]

player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0},
}

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if skill.to_s == player[:caste][:skill])]
  [skill, (mod += 2 if skill.to_s == player[:sub][:skill])]
end.to_h

换句话说,我的代码返回了以下player[:skills]哈希:

skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 2, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0}

但我希望它返回:

skills: {acrobatics: 0, athletics: 2, engineering: 0, endurance: 2, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0}

如果有更简单的方法,请告诉我。我还尝试了以下方法:

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if skill.to_s == (player[:caste][:skill] || player[:sub][:skill]))]
end.to_h

这只会影响player[:caste][:skill]中的技能。

4 个答案:

答案 0 :(得分:1)

当我运行您的代码时,会得到此结果。

{:acrobatics=>nil, :athletics=>nil, :engineering=>nil, :endurance=>2, :heal=>nil, :history=>nil, :influence=>nil, :insight=>nil, :magicka=>nil, :perception=>nil, :riding=>nil, :stealth=>nil, :streetwise=>nil, :thievery=>nil}

那是因为map返回执行的最后一条语句。另外,实际上,您仅在与子技能匹配时才设置技能的值,否则将其设置为nil。

所以代码中发生的事情是每次迭代都返回以下内容,这是传递到map的块中最后一条语句的结果。

[:acrobatics, nil]
[:athletics, nil]
[:engineering, nil]
[:endurance, 2]
[:heal, nil]
[:history, nil]
[:influence, nil]
[:insight, nil]
[:magicka, nil]
[:perception, nil]
[:riding, nil]
[:stealth, nil]
[:streetwise, nil]
[:thievery, nil]

最终结果是一个看起来像这样的数组。

[[:acrobatics, nil], [:athletics, nil], [:engineering, nil], [:endurance, 2], [:heal, nil], [:history, nil], [:influence, nil], [:insight, nil], [:magicka, nil], [:perception, nil], [:riding, nil], [:stealth, nil], [:streetwise, nil], [:thievery, nil]]

最终映射到新的哈希

{:acrobatics=>nil, :athletics=>nil, :engineering=>nil, :endurance=>2, :heal=>nil, :history=>nil, :influence=>nil, :insight=>nil, :magicka=>nil, :perception=>nil, :riding=>nil, :stealth=>nil, :streetwise=>nil, :thievery=>nil}

得到所有这些nil的原因是因为在您的语句中,如果if语句不为真的情况下的结果为nil。 例如:

[skill (mod += 2 if skill.to_s == player[:caste][:skill])]

对于[the_skill, nil]不正确的情况,将返回skill.to_s == player[:caste][:skill]

要查看发生了什么,请在irb中尝试此操作。

x = 0
=> 0
x += 1 if false
=> nil
x += 1 if true
=> 1 

您可以使用类似的方法来克服它。

[skill, skill.to_s == player[:caste][:skill] ? mod + 2 : mod ]

或使用上面的示例:

x = 0
=> 0
x =  false ? x + 1 : x 
=> 0
x =  true ? x + 1 : x
=> 1

下面的代码修改版本应该可以使用。

player[:skills] = player[:skills].map do |skill, mod|
  [skill, skill.to_s == player[:caste][:skill] || skill.to_s == player[:sub][:skill] ? mod + 2 : mod ]
end.to_h

但是,这里有些冗长,但是希望可以更轻松地遵循该方法来完成您想要做的事情,并允许将来进行额外的修改而不会使代码变得过于混乱。

player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0},
}

player_caste_skill = player[:caste][:skill]
player_sub_skill = player[:sub][:skill]
current_skills = player[:skills]
updated_skills = {}
current_skills.each_pair do |skill, prev_value| 
  new_value = prev_value 
  case skill.to_s
    when player_caste_skill, player_sub_skill
      new_value = prev_value + 2
    when "some_other_skill"  
      new_value = prev_value + 3
  end
  updated_skills[skill] = new_value
end
puts current_skills
puts updated_skills

答案 1 :(得分:1)

我为player[:skill]哈希设置了默认值(Hash#default),只是为了避免在丢失键(它会添加键!)的情况下出错,并允许添加一个新值。键,而无需将每个技能初始化为0。

player[:skills].default = 0

然后仅用一根衬纸扫描您需要增加的键:

[:caste, :sub].each { |key| player.dig(key, :skill).to_sym.then { |skill| player[:skills][skill] += 2 } }


多亏了初始化功能,您的播放器也可以

player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {}
}

返回类似的结果

player #=> {:caste=>{:skill=>"athletics"}, :sub=>{:skill=>"endurance"}, :skills=>{:athletics=>2, :endurance=>2}}

位置:

player[:skills][:whatever] #=> 0

答案 2 :(得分:1)

我将遍历已定义的技能而不是技能值。

player.
  map { |_, h| h[:skill] }.
  compact.
  map(&:to_sym).
  each { |skill| player[:skills][skill] += 2 }

现在player得到了相应的更新,您可以使用player或类似的符号来检查p player

答案 3 :(得分:0)

将代码更改为以下形式:

player = {
 caste: {skill: "athletics"},
 sub: {skill: "endurance"},
 skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, 
 history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, 
 stealth: 0, streetwise: 0, thievery: 0},
}

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if [player[:caste][:skill], player[:sub][:skill]].include? 
  (skill.to_s))]
end.to_h

代码由于当前迭代的结果而返回最后一行,所以代码无法工作的原因,因此在athletics情况下,最后一行是

[skill, (mod += 2 if skill.to_s == player[:sub][:skill])]

为假,将为nil,这就是仅endurance大小写有效的原因。

希望有帮助。