'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
0s
and1s
, so that0
refers to group headers and continguous1s
should be grouped under the same group. - I assume here that, after the column header (first row), all values in your column are
0
or1
.
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 |