组合两个each_with_object迭代器



我有两个方法做几乎完全相同的事情。我想将它们组合成一种方法来使它们干燥。

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> }

共同的部分似乎是:

@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
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)

虽然这不使用each_with_object但如果您使用的是 ruby>= 2.4,这似乎是另一个可行的选择。这使用Enumerable#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的答案相同(主要是因为签名和缩小也被盗了。

最新更新