When writing iterative code with mutation in ruby, I often find myself following this pattern:
def build_x some_data
x = [] # or x = {}
some_data.each do |data|
x.some_in_place_update! (... data ...)
end
x
end
(x
often does not have the same shape as some_data
, so a simple map
will not do.)
Is there a more idiomatic or better way to write code that follows this pattern?
[edit] A real example:
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
This begins with a flat list of thing
s, which have related but distinct uri
s. It transforms this flat list into a hierarchy, grouping related thing
s that share the same segment
s of a uri
. This follows the pattern I described: initialize h
, loop over some data and mutate h
along the way, and then spit out h
at the end.
[edit2] Another related example
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
Your
to_hierarchy
example could be done witheach_with_object
:each_with_object
passes the extra object to the block and returns that object when the iteration is done.If you're more of a traditionalist, you could use
inject
:Note the block argument order change and that the block has to return
h
so thatinject
can feed it back into the next block invocation.Your general example could be written as:
or: