1

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

  • this is not the line number but error code – Marcin Brach Apr 24 '22 at 21:31
  • Thanks for our reply. Please add the textual error message (they usually include the file name and line number). – Rubén Apr 24 '22 at 21:37
  • How can I check it? Because in AppSript this is all I've got: HttpResponseException: Response Code: 413. Message: response too large. – Marcin Brach Apr 24 '22 at 21:48
  • Use the Logger to narrow down where the problem is. Share your data in a table so it can be copied – Cooper Apr 24 '22 at 21:55
  • Possible duplicate: [Undocumented Sheet API limitation problems](https://stackoverflow.com/q/52900410/1595451) – Rubén Apr 24 '22 at 22:07
  • @Cooper I've tried to use Logger but it is truncating output. Where I can find full log? – Marcin Brach Apr 25 '22 at 07:08
  • Can you provide a copy of the spreadsheet so this can be tested? – Iamblichus Apr 25 '22 at 07:14
  • @Iamblichus Sure but I need a little bit of time to prepare spreadsheet – Marcin Brach Apr 25 '22 at 13:41
  • That's hard to say please provide a specific reproducible example. We call them [mcve] – Cooper Apr 25 '22 at 14:33
  • @Cooper I've edited question, there is a sample data sheet – Marcin Brach Apr 25 '22 at 15:41
  • @Iamblichus Sample data shhet is added at the end of question post. – Marcin Brach Apr 25 '22 at 15:42
  • Sorry but I don't follow links to spreadsheets – Cooper Apr 25 '22 at 16:32
  • @Cooper Ok I understand it. So please create your own spreadsheed with column with values 0 and 1. Where values 0 are headers of the groups, value 1 are grouped elements. Use function groupRows() from my code. It will recreate my problem but please make around 3000 rows to test. – Marcin Brach Apr 25 '22 at 21:45
  • Hi, I'm able to execute `Sortowanie()` successfully, using the sample spreadsheet you provided. Can you provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)? – Iamblichus Apr 26 '22 at 08:13
  • @Iamblichus it is working because there is to small amount of rows. Problem comes out when there is over 1000 rows. I'll prepare more data later. – Marcin Brach Apr 26 '22 at 08:18
  • @Iamblichus I've simplified all spreadsheed to only necesery column. Removes sorting from macro because it is working propertly. Right now it is minimal reproducible example. – Marcin Brach Apr 26 '22 at 12:10
  • Thanks, is there a specific reason why you want to create a group for every single row? – Iamblichus Apr 26 '22 at 13:16
  • As you could seen in spreadsheet before it got simplified rows are grouped by order number. When I mark all positions of one order (group) I want to collapse this group. So if in column is a 0 this is row fora a header of next group. Not every row is in one group. – Marcin Brach Apr 26 '22 at 13:31
  • Precisely, in your code you are trying to create a different group for every row, and that's most likely what's causing the problem (trying to create too many groups at once), so I don't understand how this is consistent with `Not every row is in one group`. – Iamblichus Apr 27 '22 at 07:31
  • @Iamblichus so at least it looks like it is in the same group, when I sort propertly rows by for example order id. I've tried to do this in loop but it takes forever to execute. Do you have a better method to do this? – Marcin Brach Apr 29 '22 at 15:01
  • `Do you have a better method to do this?` I'm not sure how you want to group the rows, so I cannot propose an alternative. Would you consider clarifying this? – Iamblichus May 02 '22 at 09:28
  • my algoritm should work this way: 1. removing all existed groups of rows 2. multisorting all rows by selected columns (order id, and dead line date) 3. recreate groups for all items with the same order id To be prcise all item rows have cell with indicator 1 and header rows have 0 in this cell. When all is sorted propertly function grouprows should group all rows in range pushing group depth by indicator cell value. Like here [link](https://stackoverflow.com/questions/69887042/group-rows-with-appscript) _italic_ – Marcin Brach May 04 '22 at 20:16

2 Answers2

0

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()
Cooper
  • 59,616
  • 6
  • 23
  • 54
  • Thanks but I've tried this and it is the same. To prevent of generating nulls, I've used getLastRowSpecial() function which is returning last filled row. – Marcin Brach Apr 24 '22 at 21:30
0

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

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.

Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • It is working almost perfect. Thank you very much!!! Only really small thing is, it is not creating last one group. But I've bypassed this puting empty header at the end. – Marcin Brach May 05 '22 at 17:17
  • @MarcinBrach Hi, I updated the sample so that it creates the last group. I hope it is useful to you. – Iamblichus May 06 '22 at 07:28