上下文和代码示例
我有一个名为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
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对象的数组,“对”具有相同日期的对象进行“分组”。
答案 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}}