'Dynamic grouping in AppSript
I have a Spreadsheet with about 3000 rows which are grouped by order number. I'm trying to build an macro to:
remove all groups
multisort all rows
recreate groups
collapse all groups marked as finished orders (optional - have no idea how to achieve this )
SHEET_NAME = "PLAN"; SORT_DATA_RANGE = "A2:CJ"; GROUP_DATA_RANGE = "BQ2:BQ"; SORT_ORDER = [ {column: 40, ascending: false}, {column: 2, ascending: true}, {column: 4, ascending: true} ]; function Sortowanie() { var ss = SpreadsheetApp.getActiveSpreadsheet(); removeAllGroups(); multiSortColumns(); groupRows(); ss.toast('Zakończono.'); } function multiSortColumns(){ var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheetByName(SHEET_NAME); var range = sheet.getRange(SORT_DATA_RANGE + sheet.getLastRow()); range.sort(SORT_ORDER); ss.toast('Sortowanie zakończone.'); } function removeAllGroups() { const ss = SpreadsheetApp.getActive(); const ssId = ss.getId(); const sheet = ss.getSheetByName(SHEET_NAME); const sheetId = sheet.getSheetId(); sheet.expandAllRowGroups(); const n = Sheets.Spreadsheets.get(ssId, { ranges: [SHEET_NAME] }).sheets[0].rowGroups.reduce((n, { depth }) => n < depth ? depth : n, 0); const requests = Array(n).fill("").map(_ => ({ deleteDimensionGroup: { range: { sheetId, dimension: "ROWS" } } })); Sheets.Spreadsheets.batchUpdate({ requests }, ssId); ss.toast('Usuwanie grup zakończone.'); } function groupRows() { const ss = SpreadsheetApp.getActive(); const sheet = ss.getSheetByName(SHEET_NAME); const levels = sheet.getRange(GROUP_DATA_RANGE + getLastRowSpecial()).getValues(); const sheetId = sheet.getSheetId(); const requests = levels.flatMap(([a], i) => Array(a).fill("").map(_ => ({ addDimensionGroup: { range: { sheetId, startIndex: i + 1, endIndex: i + 2, dimension: "ROWS" } } }))); Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId()); ss.toast('Ponowne grupowanie zakończone.'); } function getLastRowSpecial() { const ss = SpreadsheetApp.getActiveSpreadsheet() const sheet = ss.getSheetByName(SHEET_NAME); const lastRow = sheet.getRange(GROUP_DATA_RANGE).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow(); return lastRow };
Everything works perfectly when I limit range to 1000 rows. When I try to run this for a whole range or more than 1000 and debuging, I'm getting this:
HttpResponseException: Response Code: 413. Message: response too large.
Without debugger after a while I'm getting this:
The JavaScript runtime has unexpectedly terminated.
Most bizarre thing is that script is creating these groups for a whole range and then error comes out after a while.
I think that 3K rows is not a big range for that, maybe someone has an idea what is wrong?
Here it is sample data sheet: https://docs.google.com/spreadsheets/d/1DLXxZVyrhDxrBe1AX3iy54nQTFVJkoIpeos7M9mEaIo/edit?usp=sharing
Solution 1:[1]
Issue:
If I understand your situation correctly:
- You are able to remove existing groups and sort the rows according to the groups you want to create.
- The groups are defined by a column with
0sand1s, so that0refers to group headers and continguous1sshould be grouped under the same group. - I assume here that, after the column header (first row), all values in your column are
0or1.
Solution:
- Use slice and findIndex to find the successive group headers, looking iteratively for the values different than
1, so that all the rows in between are part of the same group. - For each iteration, use the indexes of the current and the next header to build each request.
Code sample:
const FIRST_ROW = 2;
function groupRows() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
const levels = sheet.getRange(GROUP_DATA_RANGE + getLastRowSpecial()).getValues().flat();
const sheetId = sheet.getSheetId();
const requests = [];
let currentHeader = FIRST_ROW;
while (currentHeader < levels.length) {
let groupLength = levels.slice(currentHeader-FIRST_ROW+1).findIndex(l => l !== 1);
if (groupLength < 0) groupLength = levels.length - currentHeader + 1;
const nextHeader = groupLength + currentHeader+1;
const request = { addDimensionGroup: { range: { sheetId, startIndex: currentHeader, endIndex: nextHeader-1, dimension: "ROWS" } } };
requests.push(request);
currentHeader = nextHeader;
}
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
ss.toast('Ponowne grupowanie zako?czone.');
}
Note:
Your other functions are not displayed here, but you should use them to remove existing rows and to make sure your data is sorted according to your preferences.
Solution 2:[2]
One of you problems but probably not the only problem
Range specified like this generate a lot of nulls between last row and max rows
"BQ2:BQ"
Try rewriting them like this:
"BQ2:BQ" + sheet.getLastRow()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Cooper |
