首页 > 解决方案 > 将集合与相关对象分组

问题描述

我有一组s,其中一个任务可以通过如下所示task的字段链接到另一个任务。runs_with

<Task id: 'abc', name: 'paint the fance', progressive: 1, runs_with: nil>
<Task id: 'def', name: 'mow the lawn', progressive: 2, runs_with: 1>
<Task id: 'ghi', name: 'wash the dishes', progressive: 3, runs_with: nil>
<Task id: 'xyz', name: 'take out the trash', progressive: 4, runs_with: 3>
<Task id: 'qur', name: 'wash the car', progressive: 5, runs_with: 2>
<Task id: 'gbj', name: 'walk the dog', progressive: 6, runs_with: 3>

现在我需要对它们进行分组,以便所有链接的最终都在一个组(一个数组)中。

[[<Task id: 'abc', name: 'paint the fance', progressive: 1, runs_with: nil>,
  <Task id: 'def', name: 'mow the lawn', progressive: 2, runs_with: 1>,
  <Task id: 'qur', name: 'wash the car', progressive: 5, runs_with: 2>],
 [<Task id: 'ghi', name: 'wash the dishes', progressive: 3, runs_with: nil>,
  <Task id: 'xyz', name: 'take out the trash', progressive: 4, runs_with: 3>,
  <Task id: 'gbj', name: 'walk the dog', progressive: 6, runs_with: 3>]]

我最初的想法是

  • 上的分区runs_with
  • task为每个可以单独运行的组创建一个组。
  • 遍历其他tasks 并将它们附加到包含链接的组中task

想知道是否有任何更惯用的方式来对它们进行分组。

标签: ruby-on-railsruby

解决方案


这是我将如何处理它。给定一个Task类和一组任务:

class Task
  ATTRIBUTES = [:id, :name, :progressive, :runs_with]
  attr_accessor *ATTRIBUTES
  include ActiveModel::Model

  def inspect
    '<Task %s>' % ATTRIBUTES.map { |a| '%s: %s' % [a, send(a).inspect] }.join(', ')
  end
end

tasks = [
  Task.new(id: 'abc', name: 'paint the fance', progressive: 1, runs_with: nil),
  Task.new(id: 'def', name: 'mow the lawn', progressive: 2, runs_with: 1),
  Task.new(id: 'ghi', name: 'wash the dishes', progressive: 3, runs_with: nil),
  Task.new(id: 'xyz', name: 'take out the trash', progressive: 4, runs_with: 3),
  Task.new(id: 'qur', name: 'wash the car', progressive: 5, runs_with: 2),
  Task.new(id: 'gbj', name: 'walk the dog', progressive: 6, runs_with: 3)
]

我将创建一个散列,其中每个值都是包装在数组中的任务,其编号为键:

hash = tasks.group_by(&:progressive)

{
  1 => [<Task id: "abc", name: "paint the fance", progressive: 1, runs_with: nil>],
  2 => [<Task id: "def", name: "mow the lawn", progressive: 2, runs_with: 1>],
  3 => [<Task id: "ghi", name: "wash the dishes", progressive: 3, runs_with: nil>],
  4 => [<Task id: "xyz", name: "take out the trash", progressive: 4, runs_with: 3>],
  5 => [<Task id: "qur", name: "wash the car", progressive: 5, runs_with: 2>],
  6 => [<Task id: "gbj", name: "walk the dog", progressive: 6, runs_with: 3>]
}

然后我将遍历原始tasks数组,并为每个具有runs_with属性的任务,将任务的数组与相应任务的数组合并为一个:

tasks.each do |task|
  if task.runs_with
    hash[task.runs_with].concat(hash[task.progressive])
    hash[task.progressive] = hash[task.runs_with]
  end
end

这将有效地合并数组并减少沿途的数组数量。然而,哈希将引用来自不同键的那些(相同的)数组,所以最后我们必须获取哈希的唯一值:

hash.values.uniq
#=> [[<Task id: "abc", name: "paint the fance", progressive: 1, runs_with: nil>,
#     <Task id: "def", name: "mow the lawn", progressive: 2, runs_with: 1>,
#     <Task id: "qur", name: "wash the car", progressive: 5, runs_with: 2>],
#    [<Task id: "ghi", name: "wash the dishes", progressive: 3, runs_with: nil>,
#     <Task id: "xyz", name: "take out the trash", progressive: 4, runs_with: 3>,
#     <Task id: "gbj", name: "walk the dog", progressive: 6, runs_with: 3>]]

推荐阅读