Changing data in a Google spreadsheet - google-apps-script

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()

Related

Range.SetValues() does not insert data on one sheet, on the other works. What is the reason?

I have a GoogleSheet with basically two sheets, which are very similar in terms of data collected.
I need to calculate same values for both sheets, but source data is in different columns.
Therefore I created three files in AppsScript:
Common.gs - with common function definitions
sheet1.gs
sheet2.gs - both sheet1 and sheet2 have only definitions of proper ranges in particular columns and one function to run script, which essentially calls functions defined in Common.gs, like so in sheet1.gs:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1")
var createdColumn = sheet.getRange("E2:E").getValues()
var ackColumn = sheet.getRange("G2:G").getValues()
var resColumn = sheet.getRange("I2:I").getValues()
var timeToAckColumn = sheet.getRange(2,14,ackColumn.length,1)
var timeToResColumn = sheet.getRange(2,15,resColumn.length,1)
var yearAndWeekRange = sheet.getRange(2,16,createdColumn.length,2)
function calculateMetricsSheet1() {
calculateTimeDiff(createdColumn, ackColumn, timeToAckColumn)
calculateTimeDiff(ackColumn, resColumn, timeToResColumn)
calculateWeek(createdColumn, yearAndWeekRange)
}
example function implementation (they are basically very similar with minor differences):
function calculateWeek(createdColumn, yearAndWeekRange) {
var arrData = []
for(var i=0;i<createdColumn.length;i++) {
if(createdColumn[i][0].toString()=="") {
arrData.push(["",""])
continue
}
var createdDate = new Date(createdColumn[i][0])
var year = createdDate.getFullYear()
var week = Utilities.formatDate(createdDate, "GMT+1", "w")
arrData.push([year, week])
}
yearAndWeekRange.setValues(arrData)
}
the sheet2.gs is basically different column definitions, the functions called within calculateMetricsSheet2() are the same.
So what is the problem?
The script works perfectly fine for sheet2.gs, but for sheet1.gs it does collect proper data, calculates proper data, but the data does not appear in proper columns after Range.setValues() call.
No exceptions or errors appear in the console.
Documentation does not provide any kind of information what could be the problem.
I have really ran out of ideas what could be the cause of the issue.
Does anyone have any idea what is going on?
edit: It may be useful to put emphasis on the fact that each script runs function calling 3 other functions -> all of them end with Range.setValues({values}). And for one sheet all of them work, and for the other - none.
That's the reason I assume there is something wrong with the sheet itself, maybe some permissions/protection? But I couldn't find anything :(
edit2: I modified my code to iterate through the sheet 10 rows at a time, because I thought maybe when I get a whole column, something bad happens with data and breaks setValues() function.
Unfortunately - even if my code iterated 1 row at a time, it still did not work on sheet1, but worked on sheet2. So not a data problem.
The code you show always puts values in yearAndWeekRange which is always in the 'sheet1' sheet. To put the data into another sheet, you need to change the target range appropriately.
The dimensions of the range must match the dimensions of the array you put there. Use this pattern:
yearAndWeekRange.offset(0, 0, arrData.length, arrData[0].length).setValues(arrData);
I found out what is the problem.
Two scripts were pretty identical, even with naming of variables - ie ackColumn, resColumn etc.
Those were stored as a global variables, so even if I was running script1.gs, it used global variables from script2.gs, effectively writing proper data to wrong sheet.
separating global variables names fixed the issue.
Perhaps a rookie mistake, but I missed the fact, that if I have a variable defined outside any function, it becomes global and could be overwritten from other file

Snapshot data and append at a specific location in Google Sheets

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.

Google app-script .setValues() method is not writing to the Sheet

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

Append only unique rows

I am trying to read an external JSON API and write parsed values from it into google sheet. So each API call writes a new row into the sheet. The second requirement is to write the row only if it contains something else than already inserted rows - in other words append new row only if it is unique.
I've finished the first requirement. I've used JSON.parse and appendRow and it works with no problem.
Unfortunately, I cannot get thru the second requirement. I can not figure any construction nor find an example solution.
Does anybody have an advice how to append only unique rows from google apps script?
EDIT: My apologize for the above inexact post. Here are the details.
Below mentioned code is my solution for the first requirement:
function run() {
var data = UrlFetchApp.fetch("https://url/json-api").getContentText();
var json = JSON.parse(data);
var last = (json.last);
var credit = parseInt(json.credit);
var doc = SpreadsheetApp.openById("googleSheetID");
var list = doc.getSheets()[0];
list.appendRow([last, credit]);
}
So it simply append new row each time I run the script. Unfortunately, the returned JSON changes only from time to time. When I scheduled the script to run every 5 minutes it leads to many redundant rows.
However I don't want to run any kind of distinct after the redundant rows are written. I'd like to check if the new parsed data is unique and if so - write, otherwise nothing.
getLastRow's value and check whether it's equal to last/credit. Then appendRow, if needed.
Script Sample Snippet:
var lastRow=list.getRange(1,list.getLastRow(),1,2).getValues(); //[[prev.last,prev.credit]]
if(lastRow[0][0]!=last && lastRow[0][1]!=credit){
list.appendRow([last, credit]);
}

Paste each line of an e-mail body variable into a row of a Spreadsheet

I have a script that successfully extracts the body of an e-mail:
var msgBody = messages[m].getBody();
This message contains 650+ lines of information, and I would like to put each line of text in a different row in the same column (the way it would work if I were to paste it into cell H1 for instance. When I run this command:
listserveSheet.getRange("H1").setValues(msgBody)
It naturally puts ALL 650+ lines of information in that one cell instead of cells H1 through H652 (or whatever the last one would be for any given e-mail).
Any idea how to alter my variable to make it able to set the values the way I desire? I was thinking maybe loop through the variable and set each value separately. But I don't know how to pull a single line out of the variable each time through the loop.
Thanks to Suhail's pointers in the comment above, I was able to find my way to an answer.
var msgBody = messages[m].getBody();
var dest = msgBody.split("\n");
Did the splitting just fine. Then I was able to set the individual values into individual cells with this.
for (var i =0; i < dest.length; i++) {
listserveSheet.getRange("H"+ (i+1)).setValue(dest[i]);
}
I had tried to use a setValues operation, but clearly, I didn't grok the 2d range business. I suspect that if I did understand 2d ranges, using setValues on a larger range, as opposed to looping setValue on cells would have been faster. Here's the bit I tried (as well as several other permutations that didn't work:
listserveSheet.getRange(1,8,dest.length,dest[0].length).setValues(dest);
Thanks again for the help