So let's say I have this MWE code:
# see below for full code - not important here
class RealEstate; attr_accessor :name; attr_accessor :living_space; attr_accessor :devices; def initialize(name, living_space, devices); @name = name; @living_space = living_space; @devices = devices; end; end
real_estates = [ RealEstate.new("ceiling", 30, [1]), # name, living_space, devices
RealEstate.new("1st floor", 50, [2,3]),
RealEstate.new("Ground floor", 70, [4,5]) ]
(A) Now i would like to use the array methods, especially the pretzel colon like e.g. this:
real_estates.map(&:living_space).inject(:+) # get the sum of all the available living space
real_estates.map(&:devices).map!(&:first) # get the first device of each floor
(B) In my understanding, this seems to be inefficient. The array is processed twice (or multiple times), which has implications in a huge real-world example. I could however write each of this in an own (single) loop:
real_estate.inject(0) do |sum, o|
sum + o.living_space
end
real_estate.map {|o| o.devices.first}
I would really prefer syntax like in block A over B, but YMMV. I am aware of filter_map
or flat_map
, which already help in some cases, allegedly improving performance around a factor of 4.5
Especially, when these statements do a lot and get huge, (daisy?) chaining them together seems like a pattern that makes the code readable. Reference: Method Chaining (Idiom): "Train wreck is clean code"
So finally my question: How do you prevent having intermediate results (arrays) and multiple iterations over the same array? Or: how do you do chaining on array methods efficiently?
Rails would be applicable here, but I think there could also be a variant for pure ruby. I imagine something like this:
real_estates.map_inject(&:living_space,:+) # tbh you would need a combination for each of map, select, reject, each, etc.
real_estates.map(&:devices.first)
real_estates.map([&:devices,&:first])
I don't only use map and inject, but also filter, uniq, select, reject (all Enumerable), flatten (Array), etc., also often with a bang
The whole MWE class code:
class RealEstate
attr_accessor :name
attr_accessor :living_space
attr_accessor :devices
def initialize(name, living_space, devices)
@name = name
@living_space = living_space
@devices = devices
end
end