如何根据其中一个属性“拆分和分组”对象数组

时间:2015-01-16 07:28:47

标签: ruby arrays algorithm sorting data-structures

上下文和代码示例

我有一个名为TimesheetEntry的类的实例Array

这是TimesheetEntry的构造函数:

  def initialize(parameters = {})
    @date       = parameters.fetch(:date)
    @project_id = parameters.fetch(:project_id)
    @article_id = parameters.fetch(:article_id)
    @hours      = parameters.fetch(:hours)
    @comment    = parameters.fetch(:comment)
  end

我使用.csv文件中的数据创建了一个TimesheetEntry对象数组:

  timesheet_entries = []
  CSV.parse(source_file, csv_parse_options).each do |row|
    timesheet_entries.push(TimesheetEntry.new(
      :date       => Date.parse(row['Date']),
      :project_id => row['Project'].to_i,
      :article_id => row['Article'].to_i,
      :hours      => row['Hours'].gsub(',', '.').to_f,
      :comment    => row['Comment'].to_s.empty? ? "N/A" : row['Comment']
    ))
  end

我还有一个Set Hash包含两个元素,就像这样创建:

  all_timesheets = Set.new []
  timesheet_entries.each do |entry|
    all_timesheets << { 'date' => entry.date, 'entries' => [] }
  end

现在,我想使用TimesheetEntries填充该Hash内部的Array。 每个Hash数组必须只包含一个特定日期的TimesheetEntries。

我这样做了:

  timesheet_entries.each do |entry|
    all_timesheets.each do |timesheet|
      if entry.date == timesheet['date']
        timesheet['entries'].push entry
      end
    end
  end

虽然这种方法可以完成工作,但效率并不高(我对此很新)。

问题

实现相同最终结果的更有效方法是什么?本质上,我想“拆分”TimesheetEntry对象的数组,“对”具有相同日期的对象进行“分组”。

1 个答案:

答案 0 :(得分:1)

您可以通过将Set替换为Hash来修复性能问题,all_timesheets.each do |timesheet| ... if entry.date ...是类似字典的数据结构。

这意味着您的内部循环all_timesheets[entry.date]将被更有效的哈希查找替换:all_timesheets = {} timesheet_entries.each do |entry| all_timesheets[entry.date] ||= [] # create the key if it's not already there all_timesheets[entry.date] << entry end

此外,无需提前创建密钥,然后填充日期组。这些都可以一次完成:

all_timesheets[entry.date] ||= []

哈希的一个好处是,您可以在遇到不存在的密钥时自定义其行为。您可以使用带有块的constructor来指定在这种情况下发生的情况。让我们告诉我们的哈希自动添加新密钥并用空数组初始化它们。这允许我们从上面的代码中删除all_timesheets = Hash.new { |hash, key| hash[key] = [] } timesheet_entries.each do |entry| all_timesheets[entry.date] << entry end 行:

all_timesheets = timesheet_entries.group_by { |e| e.date }

然而,使用Enumerable#group_by method有一种更简洁的方法来实现这种分组:

all_timesheets = timesheet_entries.group_by(&:date)

当然,有一种方法可以使这更简洁,使用another trick

{{1}}