【问题标题】:Iterating over array of Ruby Hashes and counting the no. of times value has appeared?遍历 Ruby 哈希数组并计算编号。的倍数出现了?
【发布时间】:2015-01-20 16:58:52
【问题描述】:

我有一个带有结构的 Ruby 哈希数组

[{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}].

我想要一个最终的哈希值:

{:apple => 3,:banana=> 2,:pineapple=>1}

如何做到这一点?

【问题讨论】:

  • 您是否尝试过编写一些代码来执行任务?

标签: ruby-on-rails ruby arrays hash


【解决方案1】:

计算此类直方图的常用技巧是使用Enumerable#group_by,然后使用Enumerable#map,将生成的Arrays 转换为sizes,最后转换为Hash

ary = [{ fruit: 'apple' }, { fruit: 'apple' }, { fruit: 'apple' }, 
  { fruit: 'banana' }, { fruit: 'banana' }, { fruit: 'pineapple' }]

ary.
  group_by {|h| h.values.first }.
  map {|fruit, ary| [fruit.to_sym, ary.size]}.
  to_h
# => { apple: 3, banana: 2, pineapple: 1 }

但是有一个更好的方法:一个名为 MultiSet 的数据结构完全可以满足您的需求。不幸的是,Ruby 核心库或 stdlib 中没有一个,但您可以找到一些浮动的实现:

Multiset[*ary.map {|el| el.values.first.to_sym}]
# => #<Multiset:#3 :apple, #2 :banana, #1 :pineapple>

然而,几乎总是当你有一个数据结构时,比如一个符号哈希数组到字符串或类似的东西,那里有一个对象想要出来。毕竟,Ruby 是一种面向对象的语言,而不是一种面向符号的哈希数组到字符串的语言。

class Fruit
  attr_reader :name

  def ==(other)
    name == other.name
  end

  def eql?(other)
    name.eql?(other.name)
  end

  def hash
    name.hash
  end

  def to_s
    name
  end

  def inspect
    "#<Fruit: #{name}>"
  end

  private

  attr_writer :name

  def initialize(name)
    self.name = name
  end
end

Multiset[Fruit.new('apple'), Fruit.new('apple'), Fruit.new('apple'), 
  Fruit.new('banana'), Fruit.new('banana'), Fruit.new('pineapple')]
# => #<Multiset:#3 #<Fruit: apple>, #2 #<Fruit: banana>, #1 #<Fruit: pineapple>>

【讨论】:

    【解决方案2】:

    @Simone 说得对,你可以使用 inject 方法来计算出现次数,如下所示:

    [23] pry(main)> arr
    => [{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}]
    [24] pry(main)> arr.inject({}) { |sum, val| sum[val[:fruit]] = sum[val[:fruit]].to_i + 1; sum }
    => {"apple"=>3, "banana"=>2, "pineapple"=>1} 
    

    这在代码中不是很清楚,但是您需要在哈希上调用 .to_i 的原因是因为如果它返回 nil(即第一次遇到“apple”),您不能这样做 @ 987654323@,所以nil.to_i 变为零。从上面的代码中,我认为这不是超级清晰,所以我想我会提到它。

    虽然我的解决方案是一个“解决方案”,但 Jorg 在最佳实践方面是正确的,这将是一个了解 OO 解决方案的好机会。

    【讨论】:

      【解决方案3】:

      使用默认值创建一个哈希并遍历数组中的每个哈希:

      fruits = [{:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"apple"}, {:fruit=>"banana"}, {:fruit=>"banana"}, {:fruit=>"pineapple"}]
      h = Hash.new(0)
      fruits.each { |item| h[item[:fruit]] += 1 }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-30
        • 2014-11-21
        • 1970-01-01
        • 1970-01-01
        • 2016-09-03
        • 2013-11-25
        • 1970-01-01
        相关资源
        最近更新 更多