在ruby中编写带有变异的迭代代码时,我经常发现自己遵循这种模式:
def build_x some_data
x = [] # or x = {}
some_data.each do |data|
x.some_in_place_update! (... data ...)
end
x
end
(x
通常与some_data
形状不同,因此简单的map
不会。{/ p>
是否有更惯用或更好的方法来编写遵循此模式的代码?
[edit]一个真实的例子:
def to_hierarchy stuff
h = {}
stuff.each do |thing|
path = thing.uri.split("/").drop(4)
sub_h = h
path.each do |segment|
sub_h[segment] ||= {}
sub_h = sub_h[segment]
end
sub_h.merge!(
data: thing.data,
)
end
h
end
这从thing
s的平面列表开始,它具有相关但不同的uri
s。它将此平面列表转换为层次结构,对与thing
共享相同segment
的相关uri
进行分组。这遵循我描述的模式:初始化h
,循环一些数据并在此过程中改变h
,然后在最后吐出h
。
[edit2]另一个相关的例子
def count_data obj
i = if obj[:data] then 1 else 0
obj.each do |k, v|
i += count_statements v unless :data == k
end
i
end
答案 0 :(得分:1)
您的to_hierarchy
示例可以使用each_with_object
完成:
def to_hierarchy stuff
stuff.each_with_object({}) do |thing, h|
#...
end
end
each_with_object
将额外对象传递给块,在迭代完成后返回该对象。
如果您更像传统主义者,可以使用inject
:
def to_hierarchy stuff
stuff.inject({}) do |h, thing|
#...
h
end
end
请注意块参数顺序更改,并且块必须返回h
,以便inject
可以将其反馈到下一个块调用中。
您的一般示例可以写成:
def build_x some_data
some_data.each_with_object([]) do |data, x|
x.some_in_place_update! (... data ...)
end
end
或:
def build_x some_data
some_data.inject({}) do |x, data|
x.some_in_place_update! (... data ...)
x
end
end
答案 1 :(得分:1)
啊!你想要each_with_object
。喜欢这个
def to_hierarchy stuff
stuff.each_with_object({}) do |thing, h|
path = thing.uri.split("/").drop(4)
sub_h = h
path.each do |segment|
sub_h[segment] ||= {}
sub_h = sub_h[segment]
end
sub_h.merge!(
data: thing.data,
)
end
end