Refactoring iterating over array

I’m trying to find the best way to refactor iterating over this array and converting it into an array of new objects. The beans_to_build array is an array of objects that has a property of count which tells you how many new instances of the object to create with a set of properties. I’m looking to return an array of all the new JellyBean objects preferably without having to store them in the new_beans array

def new_jelly_beans
  new_beans = []
  beans_to_build.each {|btb|
      btb.count.times { new_beans << JellyBean.new(prop1: btb.prop1, prop2: btb.prop2)  }
  }
  new_beans
end

Thanks in advance for any suggestions.

Written in a more functional way:

def new_jelly_beans
  beans_to_build.map do |btb|
    Array.new(btb.count) { JellyBean.new(prop1: btb.prop1, prop2: btb.prop2) }
  end.flatten
end

You should try #inject:

def new_jelly_beans
  beans_to_build.inject([]) do |memo, bean_to_build|
    memo << JellyBean.new(prop1: bean_to_build.prop1, prop2: btb.prop2)
  end
end

``

If you give the class that a “bean to build” is a method that can return a set of parameters (prop1, prop2) as a hash, then you could make that code even more straightforward.

You can use Enumerable’s map to return a new array. Your code can be rewritten as:

def new_jelly_beans
  new_beans = beans_to_build.map do |btb|
    btb.count.times.map do
      JellyBean.new(prop1: btb.prop1, prop2: btb.prop2) 
    end
  end
  new_beans.flatten
end

There’s a lot being done here… after a refactor:

def new_jelly_beans
  # we want an array of all the beans, not an array of arrays
  new_beans(beans_to_build).flatten
end

private

def new_beans(beans_to_build)
  # map over each bean_building_template and build an array of beans
  beans_to_build.map do |bean_building_template|
    build_beans(bean_building_template)
  end
end

def build_beans(bean_building_template)
  # build an array of beans from a bean building template
  bean_building_template.count.times.map do
    JellyBean.new(prop1: bean_building_template.prop1, prop2: bean_building_template.prop2) 
  end
end

I’ve changes some of the names to be clearer on what they do… ideally you don’t want any comments in your code, it should be clear what your doing from the names.

Hope that helps. There is also a collect method that is identical to map. I tend to always use map but I know some developers prefer to use collect when building an array.

Another way using inject:

def new_jelly_beans
  beans_to_build.inject([]) do |result, bean_building_template|
    result + bean_array(bean_building_template)
  end
end

def bean_array(bean_building_template)
  Array.new(bean_building_template.count) { JellyBean.new(prop1: bean_building_template.prop1, prop2: bean_building_template.prop2 }
end

Enumerable’s inject is worth reading.

Ruby’s Array can take length of the array and a block with the object to instantiate each value with.

When using that approach all objects of the final array will be identical, though. This might lead to surprising behavior once you start mutating these objects.

@timhabermaas oh, your absolutely right. I’ve updated my answer.