【问题标题】:Counting several elements inside an array计算数组中的几个元素
【发布时间】:2014-12-17 10:25:56
【问题描述】:

我刚刚写了一个我很确定写得很糟糕的方法。我不知道是否有更好的方法来用 ruby​​ 编写这个。这只是一个简单的循环计数。

当然,我可以使用 select 或类似的东西,但这需要在我的数组上循环两次。有没有办法通过循环而不在循环之前声明字段来增加几个变量?类似多选的东西,我不知道。当我有更多计数器时,情况就更糟了。

谢谢!

failed_tests = 0
passed_tests = 0
tests.each do |test|
  case test.status
  when :failed
    failed_tests += 1
  when :passed
    passed_tests +=1
  end
end

【问题讨论】:

  • Ruby 特有的 Rails+ActiveRecord 方法的新答案。独立于状态数量:)
  • 已选择的解决方案,由@vint-i-vuit 给出:Hash[tests.group_by(&:status).map{|k,v| [k,v.size]}]

标签: ruby-on-rails ruby arrays loops


【解决方案1】:

你可以像这样聪明地做一些事情:

tests.each_with_object(failed: 0, passed: 0) do |test, memo|
  memo[test.status] += 1
end
# => { failed: 1, passed: 10 }

【讨论】:

  • 这是一个不错的解决方案,虽然只适用于预定义的状态。
  • 通过将Hash.new(0) 传递给each_with_object,它也很容易使其与其他状态一起使用。但在这种情况下,它也会降低可读性。 OP 要求只讲两个,但总是在必要时改进一个:)
【解决方案2】:

您可以使用#reduce 方法:

failed, passed = tests.reduce([0, 0]) do |(failed, passed), test|
  case test.status
  when :failed
    [failed + 1, passed]
  when :passed
    [failed, passed + 1]
  else
    [failed, passed]
  end
end

或者使用带有默认值的Hash,这将适用于任何状态:

tests.reduce(Hash.new(0)) do |counter, test|
  counter[test.status] += 1
  counter
end

或者甚至用@fivedigit 的想法来增强它:

tests.each_with_object(Hash.new(0)) do |test, counter|
  counter[test.status] += 1
end

【讨论】:

  • 你不需要Hash.new { |hash, key| hash[key] = 0 }Hash.new(0)也可以! :)
【解决方案3】:

假设 Rails 4(这里使用 4.0.x)。我建议:

tests.group(:status).count
# -> {'passed' => 2, 'failed' => 34, 'anyotherstatus' => 12}

这将按任何可能的:status 值对所有记录进行分组,并计算每个单独的出现次数。

编辑:添加无 Rails 方法

Hash[tests.group_by(&:status).map{|k,v| [k,v.size]}]
  1. 按每个元素的值分组。
  2. 将分组映射到 [value, counter] 对数组。
  3. 将 paris 数组转换为 Hash 中的键值,即可通过 result[1]=2 ... 访问。

【讨论】:

  • 它不仅仅依赖于 Rails 4。它依赖于 ActiveRecord。如果它是 ActiveRecord 关系,这是最好的答案。
  • 完全同意,虽然我想知道不使用 ActiveRecord 的概率(就 Rails 设置的全局使用而言),考虑到正在使用 Rails。 注意:假设是关系数据库,可能假设太多... :S
  • 现在这是最佳答案。
  • 由于我的测试并不总是 ActiveRecords,我使用了您的第二个解决方案,它非常完美!谢谢!
【解决方案4】:
hash = test.reduce(Hash.new(0)) { |hash,element| hash[element.status] += 1; hash }

这将返回一个带有元素计数的散列。 例如:

class Test
  attr_reader :status

  def initialize
    @status = ['pass', 'failed'].sample
  end
end

array = []
5.times { array.push Test.new }

hash = array.reduce(Hash.new(0)) { |hash,element| hash[element.status] += 1; hash }

=> {"失败"=>3, "通过"=>2}

【讨论】:

    【解决方案5】:
    res_array = tests.map{|test| test.status}
    failed_tests = res_array.count :failed
    passed_tests = res_array.count :passed
    

    【讨论】:

      猜你喜欢
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-14
      • 1970-01-01
      • 2016-08-31
      • 2019-08-21
      • 1970-01-01
      相关资源
      最近更新 更多