给定 Google 表格中的任务和所有者列表,如何有效地提取个人计划?

问题描述

基于以下任务表:

PROJECT     TASK    START       END         IN CHARGE
P1          Task 1  16/03/2021  19/03/2021  AAA
P1          Task 2  16/03/2021  19/03/2021  BBB
P1          Task 3  31/03/2021  31/03/2021  AAA
P2          Task 4  06/04/2021  07/04/2021  
P2          Task 5  17/03/2021  07/04/2021  BBB
P2          Task 6  20/04/2021  15/04/2021  
P3          Task 7  06/04/2021  15/04/2021  CCC

我正在尝试制定以下计划:

IN CHARGE    16/03    17/03   18/03   19/03 
  AAA         P1      P1      P1      P1            
  BBB         P1      P1/P2   P1/P2   P1/P2  
  CCC                           

目前,我使用以下公式执行此操作,但我需要将其放入每个单元格中才能工作,当负责人的不同值数量很大时,这会变得非常慢。

=ARRAYFORMULA(IFERROR(
  SI($A3<>"";
    JOIN("/"; UNIQUE(

      FILTER(INPUT!$A:$A;
             INPUT!$C:$C<=B$2; 
             INPUT!$D:$D>=B$2;
             INPUT!$E:$E=$A3)
    ))
  ;"")
;""))

有没有一种有效的方法来计算这个?

我在此链接上有一个通用示例:

GSheet GANTT

解决方法

我不确定仅使用公式是否可以有效地完成此操作,但您可以使用 Google App Script 在 javascript 中以最佳方式实现您的函数。

您可以最初收集所有者集和所有天的列表,然后通过迭代任务来构建结果表。

这是一个示例实现:

sheet https://docs.google.com/spreadsheets/d/1zk01y8wwLvjJPwc3ov8f2NDUMIEcxPWMNhciFXvCaVw/edit?usp=sharing

和 javascript 代码:

class Task{
  /**
   * @param {[string,string,Date,Date][]} tasks
   */
  constructor(cells) {
    this.project = cells[0];
    this.owner = cells[1];
    this.start = cells[2];
    this.end = cells[3];
  }
}
/**
 * @param {[string,Date][]} tasks
 */
function makeTimeSheet(task_cells) {
  task_cells = remove_empty(task_cells);
  const tasks = task_cells.map(cells => new Task(cells));
  console.log(`Running makeTimeSheet on the following ${tasks.length} tasks`);
  const start = new Date(Math.min(...tasks.map(t => t.start)));
  const end = new Date(Math.max(...tasks.map(t => t.end)));
  const header = ["",...days_in_task({start,end})];
  const results = owners_dates(tasks);
  const table = final_table(results,start);
  table.unshift(header);
  console.log(`Returning a table with ${table.length} lines and ${table[0].length} columns`);
  return table;
}

/**
 * In an array of arrays,remove all the empty inner arrays.
 * This is required to work on infinite google sheet ranges
 */
function remove_empty(arr){
  return arr.filter(arr => arr.some(x => x));
}

/**
 * @param {{start:Date,end:Date}} task
 */
function* days_in_task(task) {
   const day = 24*60*60*1000;
   const duration = task.end - task.start;
   if (duration < 0 || duration > 365*day) throw new Error(`Invalid task: ${JSON.stringify(task)}`);
   let d = new Date(task.start);
   while(d <= task.end) {
     yield d;
     d = new Date(d.getTime()+day);
   }
}


// Given a map and a key,// returns the value associates with the given key in the map if it exists
// otherwise insert a value using the given default_val function and returns it
function get_or_default(map,key,default_val){
      let value = map.get(key);
      if (value === undefined) {
         value = default_val();
         map.set(key,value);
      }
      return value;
}

// Returns a mapping from owner to a mapping from dates to projects
function owners_dates(tasks){
   const result = new Map();
   for(const task of tasks) {
      const owner = get_or_default(result,task.owner,() => new Map);
      for(const date of days_in_task(task)) {
         const projects = get_or_default(owner,date.getTime(),() => new Set);
         projects.add(task.project);
      }
   }
   return result;
}

function final_table(results,start) {
   const day = 24*60*60*1000;
   return Array.from(results).map(([owner,dates])=> {
     const line = [owner];
    for([date,projects] of dates) {
      line[1 + (date - start)/day] = [...projects].join(",")
    }
    return line;
   });
}