给定 google 工作表中的任务和所有者列表,如何有效地提取个人计划?
How to efficiently extract Individual Plannings given a list of Tasks and Owners in google sheets?
基于以下 table 个任务:
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;
INPUT!$D:$D>=B;
INPUT!$E:$E=$A3)
))
;"")
;""))
有没有有效的方法来计算这个?
我有一个关于此的通用示例 link :
我不确定您是否可以只使用公式高效地完成它,但是您可以使用 Google App Script 在 javascript 中最佳地实现您的功能。
您可以先收集一组所有者和所有日期的列表,然后通过遍历任务构建结果 table。
这是一个示例实现:
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, string, Date, 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;
});
}
基于以下 table 个任务:
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;
INPUT!$D:$D>=B;
INPUT!$E:$E=$A3)
))
;"")
;""))
有没有有效的方法来计算这个?
我有一个关于此的通用示例 link :
我不确定您是否可以只使用公式高效地完成它,但是您可以使用 Google App Script 在 javascript 中最佳地实现您的功能。
您可以先收集一组所有者和所有日期的列表,然后通过遍历任务构建结果 table。
这是一个示例实现:
和 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, string, Date, 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;
});
}