【问题标题】:How do I calculate the average values in objects located in an array?如何计算位于数组中的对象的平均值?
【发布时间】:2012-03-09 18:38:29
【问题描述】:

假设我有一个这样的数组:

[
  {
    "player_id"         => 1,
    "number_of_matches" => 2,
    "goals"             => 5
  },
  {
    "player_id"         => 2,
    "number_of_matches" => 4,
    "goals"             => 10
  }
]

我希望得到所有球员每场比赛的平均进球数,而不是每个球员的平均数,而是总平均数。

我想用.each 来做这件事并存储每个单独的平均值,最后将它们全部相加并除以我拥有的玩家数量。但是,我正在寻找一种 Ruby/单行方式来做到这一点。

【问题讨论】:

  • 您可能想要修复您的数组/哈希,以便它实际上是有效的 Ruby。
  • 抱歉,我得到一个 JSON 并将其映射到哈希。让我编辑一下。
  • 单线很有趣,但经常被高估,IMO。我认为要求优雅干净解决方案比要求单线更好。
  • @Andrew:同意,尤其是因为这似乎不是一个可以优雅地解决的问题。
  • @AndrewMarshall 你是对的。在提出此类问题时,我会考虑到这一点,以避免答案混淆。

标签: ruby average


【解决方案1】:

根据要求,单行:

avg = xs.map { |x| x["goals"].to_f / x["number_of_matches"] }.reduce(:+) / xs.size

一个更易读的sn-p:

goals, matches = xs.map { |x| [x["goals"], x["number_of_matches"]] }.transpose 
avg = goals.reduce(:+).to_f / matches.reduce(:+) if goals

【讨论】:

  • Kyle:在一行中执行此操作将需要代码重复或不准确的结果。
  • Niklas:代码重复?请解释我的解决方案如何重复代码?它对数组进行一次传递并产生结果。
  • @Kyle:我说的是或不准确的结果,就像你的解决方案,它总结了各个除法错误。如果有的话,你必须使用Rational
  • 单线解决方案会导致大数字结果不准确,请查看我的变体
  • @Kyle:只需用分号替换每个换行符。瞧:单线。
【解决方案2】:

对 tokland 的回答稍作修改。

items.map{|e| e.values_at("goals", "number_of_matches")}.transpose.map{|e| e.inject(:+)}.instance_eval{|goals, matches| goals.to_f/matches}

【讨论】:

  • 嘿,instance_eval 的好把戏 :) 不过我不想在生产代码中看到它:P
【解决方案3】:
a = [{player_id:1 , match_num:2, goals: 5}, {player_id:2 , match_num:4, goals: 10}]

a.reduce(0){|avg, p| avg += p[:goals].to_f/p[:match_num]}/a.size

编辑:重命名键和块参数以减少字符数。对于那些关心的人。

首先,如果您要使用字符串作为键,您的键需要使用=>

reduce 将遍历数组并对每个玩家的个人平均值求和,最后我们将结果除以玩家总数。括号中的“0”是reduce 的起始编号。

【讨论】:

  • arr.map { |p| p[:goals].to_f / p[:number_of_matches] }.reduce(:+) / arr.size 会短一些(并且不会溢出代码 div)。
  • 单行中的 93 个字符中,只有 3 个是空格,并且在运算符周围多加几个字符会使其更具可读性。
  • Niklas:您正在映射然后减少,因此当只需要一次通过时迭代数组两次。
  • @Kyle:是的,但是不适合屏幕的单线不是单线。通常代码应该在第 80 列左右换行。如果性能是一个问题,人们可能不会选择 Ruby 来完成这项工作。此外,两种解决方案都是O(n),所以这不是“真正的”算法差异。
【解决方案4】:

为了使字符串更短,我们将"number_of_matches" 重命名为"matches"

a = [
  {"player_id":1 , "matches":2, "goals": 5}, 
  {"player_id":2 , "matches":4, "goals": 10}
]

a.reduce([0,0]){|sum,h|[sum.first+h["goals"],sum.last+h["matches"]]}.reduce{|sum,m|sum.to_f/m}
#=> 2.5

【讨论】:

    猜你喜欢
    • 2020-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-05
    • 2013-10-25
    • 2014-05-14
    相关资源
    最近更新 更多