ruby - 如何 "split and group"基于对象的一个​​属性的数组

标签 ruby arrays algorithm sorting data-structures

上下文和代码示例

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

这是 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

我还有一个 SetHash包含两个元素,创建如下:

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

现在,我想用 TimesheetEntries 填充该哈希内的数组。 每个哈希数组必须仅包含一个特定日期的 TimesheetEntries。

我是这样做的:

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

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

问题

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

最佳答案

您可以通过将 Set 替换为 Hash 来解决性能问题,这是一种类似字典的数据结构。

这意味着您的内部循环all_timesheets.each do |timesheet| ...如果 entry.date ... 将简单地替换为更高效的哈希查找:all_timesheets[entry.date]

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

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

散列的一个好处是您可以在遇到不存在的键时自定义它们的行为。您可以使用 constructor这需要一个 block 来指定在这种情况下会发生什么。让我们告诉我们的散列自动添加新键并用空数组初始化它们。这允许我们从上面的代码中删除 all_timesheets[entry.date] ||= [] 行:

all_timesheets = Hash.new { |hash, key| hash[key] = [] }

timesheet_entries.each do |entry|
  all_timesheets[entry.date] << entry
end

但是,有一种更简洁的方法可以实现这种分组,即使用 Enumerable#group_by method :

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

当然,还有一种方法可以使它更加简洁,使用 another trick :

all_timesheets = timesheet_entries.group_by(&:date)

关于ruby - 如何 "split and group"基于对象的一个​​属性的数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27979135/

相关文章:

ruby-on-rails - devise gem 如何使其 app 文件夹可用于 Rails?

arrays - 如何快速调用或访问结构计算变量?

arrays - 将带有用户输入的 slice 附加到函数中

algorithm - 如何在命题逻辑中获得一个 "indirect implicant"

algorithm - BFS(广度优先搜索算法)中的优先顺序

ruby-on-rails - Rails 代码中没有路由匹配

ruby-on-rails - Ruby on Rails Sidekiq worker 执行顺序

c++ - 如何将值赋值到二维 vector 中,如二维数组

r - 有效提取数据框中每列和行的最小值和索引,然后按值排名

ruby - ruby minitest 中可能出现 `assert_equal ... or assert_equal ...` 吗?