在一组范围内检查整数是否存在

3

我正在尝试找到确定给定整数所在范围的最佳方法。 以这个哈希为例:

score_levels = {
  1 => {'name' => 'Beginner', 'range' => 0..50}, 
  2 => {'name' => 'Intermediate', 'range' => 51..70},
  3 => {'name' => 'Pro', 'range' => 71..85},
  4 => {'name' => 'Expert', 'range' => 86..96},
  5 => {'name' => 'Master', 'range' => 97..100}
}

我想根据分数运行不同的逻辑,例如:

case score
when score_levels[1]['range']
  level_counters[1] += 1
when score_levels[2]['range']
  level_counters[2] += 1
when score_levels[3]['range']
  level_counters[3] += 1
end

有没有更通用的方法来做这件事呢?也许可以考虑以下方式:

score_levels.each |key, val| {if val['range'].member?(score) then level_counters[key] += 1 }

谢谢!


范围是否重叠,你预计会有多少个范围? - Vasfed
范围不重叠。我猜大概有5个范围,可能还有更多。 - Tzvika Mordoch
好问题。虽然这似乎是小事,但读者会感激您将一个变量(score_levels)分配给示例的哈希表。往往会省略这一点,甚至可能省略哈希表的大括号。作为一般规则,在给出示例时(这是一件好事),展示您期望或想要的输出是有帮助的。例如,在这里,您可以有一个小数组的分数,并显示您想要将它们映射到的级别。是的,在这里很明显,但它不需要太多的空间。 - Cary Swoveland
3个回答

4

由于范围不重叠且完全覆盖了0..100,因此您不需要明确的范围,而是需要类似于以下内容:

score_levels = [
  {id:1, name: 'Beginner', max_score:50},
  {id:2, name: 'Intermediate', max_score:70},
  {id:3, name: 'Pro', max_score:85},
  {id:4, name: 'Expert', max_score:96},
  {id:5, name:  'Master', max_score:100}
].sort_by{|v| v[:max_score]}

sort_by是可选的,但保留在那里以指示数组应该排序。

并且找到自己(假设得分不超过最大值且总是被发现)。

level_counters[ score_levels.find{|v| score <= v[:max_score]}[:id] ] += 1

请注意,OP可能希望数组以给定形式呈现,以便更容易地访问每个范围的两端,因此将其更改为您建议的形式可能不太方便。另外,您提出的解决方法并不像使用range.include?的解决方法那样健壮。例如,假设分数是mm,范围是('a'...'z')('aa'...'zz')。第二个范围包括mm,但是您会选择第一个,因为mm <= z#=> true`。 - Cary Swoveland
@CarySwoveland,谢谢,我肯定是想说 <=,只是打错了。 - Vasfed
是的,这个解决方案并不是很通用,但应该更加高效,因为Fixnum比较要便宜得多。 - Vasfed
我不会说“便宜很多”,因为你只考虑了一个端点而不是两个。 - Cary Swoveland

3
是的,有。
level_counters[score_levels.find{|_, h| h["range"].include?(score)}.first] += 1

2
我看到你在非数值型的情况下使用了 include? 而不是 cover?,但是当范围是数值时,这两者是相同的,所以没有效率损失。那如果 score 不在任何一个范围内呢? - Cary Swoveland
1
@Jordan,那似乎有点严厉,但是为坦白承认错误而鼓掌,这在这些地方很少见。 - Cary Swoveland
1
也许可以这样写:level_counters[level] += 1 if level = score_levels.find... - Cary Swoveland
最近我看到(或者说感觉到)了一些简短的、只有代码的答案,而仅仅靠评论似乎无法解决问题,所以我想试试给它们点踩。这个答案很显眼,因为它是一个相对复杂的单行代码(但由于是单行代码,它并没有变得更易懂),并且没有任何解释。幸运的是,它对提问者有效,但是对于 Ruby 初学者来说,阅读起来会很有挑战性,姑且这么说吧。当然,如果答案得到改进,我也非常愿意撤回点踩。 - Jordan Running
@Jordan,我喜欢嘲笑,但只有在我认为作者可能会回应时才这样做。 :-) - Cary Swoveland
显示剩余2条评论

0

如果您需要重复查找层级并且需要高效地完成此操作,请考虑构建一个单独的哈希表:

score_to_level = score_levels.each_with_object({}) { |(k,v),h|
  v['range'].each { |v| h[v] = k} }
  #=> {0=>1, 1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1,
  #    10=>1,..., 91=>4,
  #    92=>4, 93=>4, 94=>4, 95=>4, 96=>4, 97=>5, 98=>5, 99=>5, 100=>5} 

当然,这假设每个范围都包含有限数量的值(例如不是(1.1..3.3))。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接