Daily Refactor #5: Remove iteration by using Enumerable

Today is another simple refactoring to the BulkTimeEntry plugin’s helper. A common function is needing to sort one set of data into two different sets. The quick and dirty way is to just loop over each item in the set and build up the two sets yourself. But Ruby’s Arrays have another way…

The Refactoring

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# bulk_time_entries_helper.rb
  def grouped_options_for_issues(issues)
    open_issues = []
    closed_issues = []
    issues.each do |issue|
      if issue.closed?
        closed_issues << issue
      else
        open_issues << issue
      end
    end
 
    html = ''
    unless open_issues.empty?
      html << labeled_option_group_from_collection_for_select(:label_open_issues, open_issues)
    end
 
    unless closed_issues.empty?
      html << labeled_option_group_from_collection_for_select(:label_closed_issues, closed_issues)
    end
    html
  end

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# bulk_time_entries_helper.rb
  def grouped_options_for_issues(issues)
    closed_issues, open_issues = *issues.partition {|issue| issue.closed?}
 
    html = ''
    unless open_issues.empty?
      html << labeled_option_group_from_collection_for_select(:label_open_issues, open_issues)
    end
 
    unless closed_issues.empty?
      html << labeled_option_group_from_collection_for_select(:label_closed_issues, closed_issues)
    end
    html
  end

Review

Ruby’s Array implements the partition method (from Enumerable). This method takes a block and will return an Array of two Arrays:

  1. one with the elements that evaluated the block to true
  2. one with the elements that evaluated the block to false

Combining that with the splat (*), both sets can be assigned to the new variables. If you haven’t looked at Enumerable recently, check it out. Many classes implement it in both Ruby and Rails, and it has methods for just about any operation you want to do on a set of data.

Reference commit