目前,我正在尝试为Google表格创建Google Apps脚本,这样可以为即将发生的事件分批添加每周定期事件。然后,我的同事会对这些添加的事件进行微小的更改(例如,更改日期和时间,更改联系人,添加事件所需的材料等)。
到目前为止,我编写了以下脚本:
function CopyWeeklyEventRows() {
var ss = SpreadsheetApp.getActiveSheet();
var repeatingWeeks = ss.getRange(5,1).getValue(); // gets how many weeks it should repeat
var startDate = ss.getRange(6, 1).getValue(); // gets the start date
var startWeekday = startDate.getDay(); // gives the weekday of the start date
var regWeek = ss.getRange(9, 2, 4, 7).getValues(); // gets the regular week data
var regWeekdays = new Array(regWeek.length); // creates an array to store the weekdays of the regWeek
var ArrayStartDate = new Array(startDate); // helps to store the We
for (var i = 0; i < regWeek.length; i++){ // calculates the difference between startWeekday and each regWeekdays
regWeekdays[i] = regWeek[i][1].getDay() - startWeekday;
Logger.log(regWeekdays[i]);
// Add 7 to move to the next week and avoid negative values
if (regWeekdays[i] < 0) {
regWeekdays[i] = regWeekdays[i] + 7;
}
// Add days according to difference between startWeekday and each regWeekdays
regWeek[i][0] = new Date(ArrayStartDate[0].getTime() + regWeekdays[i]*3600000*24);
}
// I'm struggling with this line. The array regWeek is not sorted:
//regWeek.sort([{ column: 1, ascending: true }]);
ss.getRange(ss.getLastRow() + 1, 2, 4, 7).setValues(regWeek); // copies weekly events after the last row
}
它允许根据开始日期将一周的周期性事件添加到电子表格的概述部分。如果开始日期是星期二,则从星期二开始添加常规星期。 However, the rows are not sorted according to the dates:
在将行添加到概述之前,如何按升序日期(后跟时间)对行进行排序?
我对类似问题的搜索显示Google Script sort 2D Array by any column这是我发现的最接近的打击。使用sort
行运行脚本时会显示相同的错误消息。我不明白Range
和array
之间的差异,这可能有助于解决问题。
为了让您了解更广泛的情况,请参阅我目前正在进行的工作:
regWeek
,但卡住了。提前感谢您对排序的意见以及如何改进代码的建议。
A demo version of the spreadsheet
UpdateV01:
这里是复制和排序的代码行(首先按日期,然后按时间)
ss.getRange(ss.getLastRow()+1,2,4,7).setValues(regWeek); // copies weekly events after the last row
ss.getRange(ss.getLastRow()-3,2,4,7).sort([{column: 2, ascending: true}, {column: 4, ascending: true}]); // sorts only the copied weekly events chronologically
正如@tehhowch指出的那样,这很慢。最好在写作之前进行排序。 我将实现此方法并在此处发布。
UpdateV02:
regWeek.sort(function (r1, r2) {
// sorts ascending on the third column, which is index 2
return r1[2] - r2[2];
});
regWeek.sort(function (r1, r2) {
// r1 and r2 are elements in the regWeek array, i.e.
// they are each a row array if regWeek is an array of arrays:
// Sort ascending on the first column, which is index 0:
// if r1[0] = 1, r2[0] = 2, then 1 - 2 is -1, so r1 sorts before r2
return r1[0] - r2[0];
});
UpdateV03:
这是尝试在几周内重复重复发生的事件。不知道如何将整个&#34;周&#34;包括在内。
// Repeat week for "A5" times and add to start/end date
for (var j = 0; j < repeatingWeeks; j++){
for (var i = 0; i < numFilledRows; i++){
regWeekRepeated[i+j*6][0] = new Date(regWeek[i][0].getTime() + j*7*3600000*24); // <-This line leads to an error message
regWeekRepeated[i+j*6][3] = new Date(regWeek[i][3].getTime() + j*7*3600000*24);
}
}
我的问题得到了解答,我能够使代码按预期工作。
答案 0 :(得分:0)
您可以在设置regWeek变量之前对“Weekly Events”范围进行排序。然后,在处理之前,范围将按您想要的顺序排列。或者您可以在设置数据后对整个“概述”范围进行排序。这是一个快速功能,您可以调用以按多列对范围进行排序。您当然可以调整它以对“每周事件”范围进行排序,而不是“概述”范围。
function sortRng() {
var ss = SpreadsheetApp.getActiveSheet();
var firstRow = 22; var firstCol = 1;
var numRows = ss.getLastRow() - firstRow + 1;
var numCols = ss.getLastColumn();
var overviewRng = ss.getRange(firstRow, firstCol, numRows, numCols);
Logger.log(overviewRng.getA1Notation());
overviewRng.sort([{column: 2, ascending: true}, {column: 4, ascending: true}]);
}
至于获取每周事件部分中已填充行的数量,您需要搜索一个总是有数据的列,如果任何行有数据(如开始日期列b),循环遍历值和第一次它找到一个空白,返回该数字。这将为您提供需要复制的行数。警告:如果“每周事件”部分和“概述”部分之间的B列中没有至少一个空白值,则可能会得到不需要的结果。
function getNumFilledRows() {
var ss = SpreadsheetApp.getActiveSheet();
var eventFirstRow = 9; var numFilledRows = 0;
var colToCheck = 'B';//the StartDate col which should always have data if the row is filled
var vals = ss.getRange(colToCheck + eventFirstRow + ":" + colToCheck).getValues();
for (i = 0; i < vals.length; i++) {
if (vals[i][0] == '') {
numFilledRows = i;
break;
}
}
Logger.log(numFilledRows);
return numFilledRows;
}
编辑: 如果您只想在写入之前在javascript中对数组进行排序,并且您希望首先按开始日期排序,那么按时间排序,您可以创建一个临时数组,并为每个日期和时间组合的行添加一列。 array.sort()按字母顺序排序日期,因此您需要将该日期转换为整数。然后,您可以按新列对数组进行排序,然后从每行中删除新列。我在下面添加了一个函数。它可能更紧凑,但我认为它可能更清晰。
function sortDates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var vals = ss.getActiveSheet().getRange('B22:H34').getDisplayValues(); //get display values because getValues returns time as weird date 1899 and wrong time.
var theDate = new Date(); var newArray = []; var theHour = ''; var theMinutes = '';
var theTime = '';
//Create a new array that inserts date and time as the first column in each row
vals.forEach(function(aRow) {
theTime = aRow[2];//hardcoded - assumes time is the third column that you grabbed
//get the hours (before colon) as a number
theHour = Number(theTime.substring(0,theTime.indexOf(':')));
//get the minutes(after colon) as a number
theMinutes = Number(theTime.substring(theTime.indexOf(':')+1));
theDate = new Date(aRow[0]);//hardcoded - assumes date is the first column you grabbed.
theDate.setHours(theHour);
theDate.setMinutes(theMinutes);
aRow.unshift(theDate.getTime()); //Add the date and time as integer to the first item in the aRow array for sorting purposes.
newArray.push(aRow);
});
//Sort the newArray based on the first item of each row (date and time as number)
newArray.sort((function(index){
return function(a, b){
return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
};})(0));
//Remove the first column of each row (date and time combined) that we added in the first step
newArray.forEach(function(aRow) {
aRow.shift();
});
Logger.log(newArray);
}
答案 1 :(得分:0)
鉴于您的评论 - 您想要对写入的块进行排序 - 您有两种方法可用。一种是使用电子表格服务的Range#sort(sortObject)
方法在编写之后对书面数据进行排序。另一种是使用JavaScript Array#sort(sortFunction())
方法在写入之前对数据进行排序。
目前,您的排序代码//regWeek.sort([{ column: 1, ascending: true }]);
正在尝试使用Spreadsheet服务所需的排序对象对JavaScript数组进行排序。因此,您可以简单地将此.sort(...)
调用链接到您的写入调用,因为Range#setValues()
返回相同的Range
,允许重复Range
方法调用(例如设置值,然后应用格式等。)。
这看起来像:
ss.getRange(ss.getLastRow() + 1, 2, regWeek.length, regWeek[0].length)
.setValues(regWeek)
/* other "chainable" Range methods you want to apply to
the cells you just wrote to. */
.sort([{column: 1, ascending: true}, ...]);
在这里,我更新了您访问的范围,以引用您尝试编写的数据 - regWeek
- 以便始终保持数据的正确大小。我也在视觉上拆开了单行,这样你就可以更好地看到&#34;链接&#34;这是在电子表格服务电话之间发生的。
另一种方法 - 在写入之前进行排序 - 会更快,特别是随着排序的大小和复杂性的增加。排序范围背后的想法是你需要使用一个函数,当第一个索引的值应该在第二个索引之前时返回一个负值,当第一个索引的值应该是正值时在第二个之后,如果它们是等价的,则为零值。这意味着返回boolean
的函数不会按照人们的想法排序,因为false
和0
在Javascript中是等效的,而true
和1
也是等价的。
您的排序如下所示,假设regWeek
是一个数组数组,并且您正在对数值进行排序(或者至少会转换为数字,例如Date
s)。
regWeek.sort(function (r1, r2) {
// r1 and r2 are elements in the regWeek array, i.e.
// they are each a row array if regWeek is an array of arrays:
// Sort ascending on the first column, which is index 0:
// if r1[0] = 1, r2[0] = 2, then 1 - 2 is -1, so r1 sorts before r2
return r1[0] - r2[0];
});
我强烈建议您查看Array#sort
documentation。