组合两个each_with_object迭代器

时间:2018-06-12 11:36:59

标签: ruby-on-rails ruby

我有两种方法几乎完全相同。我想将它们组合成一种方法,使它们变干。

  def build_training_organization_filters
    @dive_centers.each_with_object(Hash.new(0)) { |obj, counts| counts[obj.training_organizations.first.short_name] += 1 }
  end

  def build_dive_center_type_filters
    @dive_centers.each_with_object(Hash.new(0)) { |obj, counts| counts[obj.dive_center_type] += 1 }
  end

输出最终将是JSON输出,如下所示:

{ training_org_filter: <data>, dive_center_filter: <data> }

3 个答案:

答案 0 :(得分:6)

共同部分似乎是:

@dive_centers.each_with_object(Hash.new(0)) { |obj, counts| counts[...] += 1 }

...

obj.training_organizations.first.short_name

obj.dive_center_type

以上两者都取决于obj,因此我们可以将公共部分提取到单独的方法中,并使用yield从调用者处获取密钥:

def count_by
  @dive_centers.each_with_object(Hash.new(0)) { |o, h| h[yield(o)] += 1 }
end

即。我们通过传递一个块来提供具体部分:

def build_training_organization_filters
  count_by { |center| center.training_organizations.first.short_name }
end

def build_dive_center_type_filters
  count_by { |center| center.dive_center_type }
end

答案 1 :(得分:4)

def build(*properties)
  @dive_centers.each_with_object(Hash.new(0)) do |obj, counts|
    index = properties.reduce(obj) { |o, m| o.public_send(m) }
    counts[index] += 1
  end
end

并称之为:

build(:dive_center_type)
build(:training_organizations, :first, :short_name)

答案 2 :(得分:0)

虽然这不使用each_with_object,但如果您使用ruby&gt; = 2.4,它似乎是另一个可行的选项。这使用Enumberable#group_byHash#transform_values,但它确实需要多个循环,这是不太理想的。

 def count_by(*chain)
    @dive_centers.group_by do |obj| 
      chain.reduce(obj) {|o,m| o.public_send(m)}
    end.transform_values(&:count)
 end

用法

count_by(:dive_center_type)
count_by(:training_organizations, :first, :short_name)

用法与@ mudasobwa的回答相同(主要是因为签名和减少也被盗了。)