what i'm trying to do is to update data in a google sheet and then write that data back to the sheet.
1. I read the data from the spreadsheet
2. Go over the data if needed i add a row and add columns, i.e. i change the existing data.
3. now i need to write the data back in the spreadhsheet
the only thing that i can think of is that the range is bigger than the old range of column and rows and this might be causing the problem
//Reading the data initially:
var workingSheet = SpreadsheetApp.getActive().getSheetByName(storeSheetName);
var existingData = workingSheet.getDataRange().getValues(); //row 0 is column names
//do some manipulations on the data (add rows and columns)
// Writing the values :
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(storeSheetName).getRange(1,1,existingData.length,storeColumns.length).setValue(existingData);
when i try running the script in debug mode , it keeps getting stuck on the .setValues() row and will not proceed
Related
I have a Google Sheet I use to track data. I have a sheet that pulls data from multiple sheets in a single row. The row has the current date for Column B and then pulls in data for columns C through AC. I am trying to create a mechanism to snapshot that data and put it on the next line below it. I want the ability to continue doing this and keep pushing the data down and dropping the current on the next line. This allows me to select data in column A to use for graphing purposes. This is what I was using:
function recordHistory() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("History");
var source = sheet.getRange("B2:AC2");
var values = source.getValues();
var now = new Date();
values[0][0] = now;
for (var col in values) {
sheet.getRange(sheet.getLastRow(),2,1,28).setValues(values[col]);
}
I used a combination of examples and I think I got my wires crossed with the translation from one to the other. Looking for help to clean this up or point me to a better option. I was originally using appendRow, but that limits me to using the first column. I want the ability to have the snapshot placed in the 2nd column and the corresponding columns after it. Hopefully, that makes sense.
In this sheet, you can see I am pulling data from the first 2 sheets into the last sheet. I am skipping the first column and using Row 2 as the exact values. The script above is supposed to take what is in Row 2, snapshot it as values only, and move the data to Row 3, moving the previous rows down. This provides me a history of the values. I will be using the triggers to run this function every night at midnight, so the data will be a daily capture of the values. Hopefully, this makes it a bit more clear.
EDIT 2: Let me try and simplify the explanation. I have a sheet that has data in cells B2 through AC2. I want to grab that data and copy it to cells B3 through AC3, moving the data down a row. So on the sheet, you should see cells B3:AC3 having yesterdays data. B4:AC4 has the day before. B5:AC5 has the day before that. Basically keeping a log of the data that is captured in B2:AC2 each day.
Is it clearer what I am trying to accomplish or should I explain it further? I really want to get this script corrected so I can schedule it to run over the weekend.
After a few hours of playing with syntax a bit and realizing where my mistake was, I noticed some issues with the way I was capturing the data and trying to apply it to a range. Here is the solution to my problem:
function recordHistory() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("History");
var source = sheet.getRange("B2:AC2");
var values = source.getValues();
sheet.insertRowBefore(3);
sheet.getRange(3, 2, 1, 28).setValues([values[0]]);
};
As you can see in the solution, I realized how the data was being stored in the array and matched it to the setValues part of the script. It is a pretty basic issue I was having, but the use case was difficult to explain. The insertRowBefore was also a vital piece to establish the structure of the sheet.
consider this situation.
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("2:2");
var values = range.getValues();
values[0][0] = "updated value"; //updating retrieved data
sheet.insertRowAfter(1); //here the problem appears
range.setValues(values); //trying to save updated row data - FAIL!
Updated row values will be written in a row number 2 (as per range selector "2:2") not in new position "3:3".
Result: It will overwrite data at row 2 instead of updating previously selected/loaded row, which is now at position "3:3" and not anymore at "2:2".
Im looking for a way how to keep "address" of a range even when the range was shifted because of added/removed rows/cells somewhere else in the sheet.
In this dummy example I can track range changes. But in parallel processing/updating sheet i can't simply track all changes from many different places.
So far I came up with a solution add new rows only at the end of SS (that doesn't change range of any rows above, but I would like to have ability add new rows on top of sheet without influencing already selected ranges.
Deleting rows is also dangerous situation - it changes position of ranges also.
LockService can't really solve the situation, because I have plenty scripts working on the same sheet (not using centralized library because of speed performance in addon).
Metadata for cell or row seems too complicated to handle for such an easy task.
From my point of view Object Range should keep its position even when its moved/shifted somewhere else. Otherwise I can't see reason to have Range as an Object - if it keeps only fixed information about from where it was picked.
Any advices are welcomed. Thank you in advance..
EDIT:
Just to add a context. I'm using Google sheets as a database for orders (10 thousands so far) - each row means one order (customer) and not all orders are in one sheet - different products have different sheets (+- 10 products/sheets)
There is an suggestion using named ranges to solve this issue - So what will happen if a spreadsheet will have ten of thousands named ranges - can that work without serious performance issues? Im thinking about to creating named range for each order row, so I can easily pick up right row by orderId and not to be afraid of moving a row when new order arrive during processing another one
You want to achieve the following flow.
Retrieve the value from "2:2" (row 2).
Modify the retrieved value.
Insert a row just below of the row 1.
By this, the initial row 2 is moved to the row 3.
Put the retrieved value to the row 3 which is the initial row 2.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several answers.
Issue and solution:
In your script, range of var range = sheet.getRange("2:2"); is the constant. By this, even when the row is inserted, this value is not changed. This is the reason of your issue.
I think that the idea using the named range will be resolved for your situation. But your comment says as follows.
I just found, that namedRange is not capable actualize its own address, if something had changed in the sheet after initialize of a range.
When the row 2 is installed as the named range, when a row is inserted to just below of the row 1, the range of the named range is also changed. But from your comment, I thought that the named range might have not been reloaded.
In order to confirm this, I would like to propose a sample script.
Sample script:
Before you run the script, please install a named range to row 2 as the name of sampleNamedRange. Then, run this script.
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Added
var sheet = ss.getActiveSheet(); // Modified
// Retrieve the range from the namedRange.
// namedRange of "sampleNamedRange" is installed for the range of "2:2" of the active sheet.
var range = ss.getRangeByName("sampleNamedRange"); // Modified
var values = range.getValues();
values[0][0] = "updated value"; //updating retrieved data
sheet.insertRowAfter(1);
// Retrieve the range from the updated namedRange.
var range = ss.getRangeByName("sampleNamedRange"); // Added
range.setValues(values);
Result:
Note:
The point is to retrieve the range from the updated named range. By this, the updated range can be used. So in above script, the updated value is put to the row 3.
References:
getRangeByName()
Class NamedRange
If I misunderstood your question and this was not the result you want, I apologize
I have a script that (gross, I know) writes to a spreadsheet. It dynamically adds and deletes rows based on input data from another sheet. I like doing this because the user has to copy and paste the resulting data, formatted into a table, into an email after (Let's skip creating an email directly from this script for now, that's part two).
I figured the easiest way to let the script know where to paste new data is to always insert a row before the very last row of the spreadsheet and paste said data.
I have been using getMaxRows(), getLastRow(), and insertRowBefore(), but all combos of these functions returns an "those rows are out of bounds" error!
I really don't understand what's going on. I use logs and see the variables getLastRow() and getMaxRows both get updated after I've done some copying of a template table to the sheet which then "inserts" rows. For some reason, even after re-calling the getMaxRows() and getLastRow() functions, they don't recognize the new rows. The script can still manipulate these new rows.
This leads me to believe getMaxRows() and getLastRow() can only get info about the number of rows in the sheet only when the script first runs, or only when the sheet is first activated? Has anyone faced this issue before? Does anyone have details about how those two functions work? The GAS 'sheet' class page didn't help.
spread = SpreadsheetApp.getActiveSpreadsheet();
emailsSheet = spread.getSheetByName("emails");
emailsMaxRows = emailsSheet.getMaxRows(); //# of rows regardless of content.
emailsSheet.deleteRows(2,emailsMaxRows-1); //I want to start from scratch each time I run the script, having just 1 row from which I append 'template' boxes where I can put data for the user to copy and paste into emails.
emailsMaxRows = emailsSheet.getMaxRows(); //now that I've deleted, Logger.log() shows that getMaxRows() correctly updates with just 1 row.
emailsSheet.insertRowsBefore(emailsMaxRows, 2);//insert two more, so there are 3 rows in the sheet.
emailsMaxRows = emailsSheet.getMaxRows(); //logger correctly shows 3
templateEmail = spread.getRangeByName("templateEmail"); //email template, A1:E5
templateEmail.copyTo(emailsSheet.getRange(emailsMaxRows,1)); //paste a table of 5 rows to row 3, so there are 7 total.
emailsMaxRows = emailsSheet.getMaxRows(); //should be 7, no?
//NO. Logger shows MaxRow is still 3, how come????? I can visibly see in the sheet there are 7 rows, with formatting and text. I simply don't understand how getMaxRows() doesn't update to return 7.
I need either getMaxRows() or getLastRow() to correctly update each time I delete or insert a row, why can't they do that for me?
SpreadsheetApp.flush() fixed the problem.
(Please note: JavaScript, Stack Overflow posting, & Google Script novice)
THE BACKGROUND: I'm attempting to use a Google Sheet Email Notification Add On to trigger notifications when a specific value is seen in a Google Sheet. The config requirements of the add on look for a TextValue, however it has trouble finding the value in a pivot table (where my data is sorted). I've tried moving the data to another sheet with a QUERY formula, however the resultant value is not seen by the add on as a TextValue but rather a cell formula.
My workaround to this is to use a script to copy & paste the data from my pivot to a separate sheet. I used this post as my guide to create my script. It works, however because it pastes all column values at once, the add on is having trouble identifying the trigger TextValue.
THE QUESTION: instead of having the entire column copied and pasted at once, how can I tweak this script to copy and paste a row at a time with a slight delay (1-2 seconds)? This delay will allow the add on to see the the target TextValue and execute the notification (I've tested this manually and it works as expected). Any help in modifying this script (or completely changing the logic if necessary) would be greatly appreciated!!
//Copy contents of inventory column daily to run notification add on
function readInventoryNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Inventory Totals");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Testing123");
// Copy from 2nd row, 7th column, all rows for one column
var valuesToCopy = sheetFrom.getRange(2, 7, sheetFrom.getLastRow(), 1).getValues();
//Paste to another sheet from first cell onwards
sheetTo.getRange(1,sheetTo.getLastColumn()+1,valuesToCopy.length,1).setValues(valuesToCopy);
}
I am changing a few cells of data in a Google spreadsheet. I would like to write back the data into the spreadsheet. The problem i have is that the sheet has say 5 columns, when i add new data into one of the cells i would like to backup the old data in the next free cell in that row. When i do this i am not able to write back the data. This is my code.
var sheet = SpreadsheetApp.openByUrl("URL");
var d = sheet.getSheetByName('Form Responses').getDataRange().getValues();
var head = sheet.getSheetByName('Form Responses').getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
d[29][2] = "A";
d[29][3] = "B";
d[29][4] = "C";
d[29][5] = "D";
sheet.getSheetByName('Form Responses').getRange(1,1,d.length,d[0].length).setValues(d);
This is the error i get. Incorrect range width, was 6 but should be 5
I have also tried modifying the the write back statement as
sheet.getSheetByName('Form Responses').getRange(29,1,1,d[29].length).setValues(d[29]);
In this case i get Cannot convert Array to Object[][].
What is the correct way to write back the data. Preferably i would like to write back data of that particular row only, instead of the whole sheet data.
The issue here is that you modify only one "row" in the array so that in the end it is not "symetric" anymore (ie every rows don't have the same length) and that causes the error you get.
possible solutions :
There are probably more than one good way to avoid that error, you could create a second array with new data (same height) and join the arrays before writing back to the sheet or, as in the example below, add a cell (or many cells) to every row in the array in a loop before assigning them a new value. Depending on the size of the array one could be better and/or easier than the other to implement... here is a simplified example that should work :
for(var n=0;n<d.length;n++){
d[n].push('');
Logger.log('row '+n+' = '+d[n]+'\n');// see the result
}
EDIT following comment :
OR you can write a single row if you change only one, just change the code to
sheet.getSheetByName('Form Responses').getRange(29,1,1,d[29].length).setValues([d[29]])
which is not far from what you tried but you were simply missing the pair of brackets to get a 2D array as required by setValues()