I'm trying to combine a new value to an existing value in Google Sheet. So when user input the codeInput then it will find the codeInput row and add formObject.things value that user just filled to the existing thingsList value (which the same row as codeInput). I tried this code but it return blank to the Google Sheet.
Your response would be very appreciated.
Here's my code:
function update1(formObject) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ws = ss.getSheetByName("Sheet1");
var data = ws.getRange(5, 1, ws.getLastRow()-1, 1).getValues();
var codeList = data.map(function(r){return r[0].toString(); });
var thingsList = data.map(function(r){return r[8]; });
var position = codeList.indexOf(formObject.codeInput);
var array = [thingsList[position], formObject.things]
if (position > -1) {
position += 5;
ws.getRange(position,9).setValue(array);
}
}
Regarding
var array = [thingsList[position], formObject.things]
and
ws.getRange(position,9).setValue(array);
The type of array is Array
setValue doesn't allow Array objects as argument. It accepts, string, number, Date and Boolean
Instead of setValue
you could pass a Google Sheets array (i.e. ={1,2}) by using setFormula
you could pass a bidimenstional Array to Google Sheets by using setValues
you could pass an array (unidimensional Array) by using appendRow
Regarding
var data = ws.getRange(5, 1, ws.getLastRow()-1, 1).getValues();
and
var thingsList = data.map(function(r){return r[8]; });
data is and Array possible having multiple rows but it has only one column, so, r[8] returns null which cause that thinkslist be an array of null values.
So far the question doesn't include enough details to know what could be the fix needed, but here are two possibilities among others :
var data = ws.getRange(5, 1, ws.getLastRow()-1, 9).getValues();
or
var data = ws.getRange(5, 1, ws.getLastRow()-1, ws.getLastColumn()).getValues();
Related
I'm a novice at programming, let alone Google AppsScript. Hopefully this is detailed enough for you to understand my issue and help me find a solution.
I am currently pulling data from an API and parsing the response into an array, then placing that array into a sheet with defined columns and an undefined amount of rows based on the response. I am using a for loop to iterate through the list of calls and create a new tab in the spreadsheet for each call, which I want to continue to do so that the responses are separated.
What I want to do is after these calls have finished, I want to take each sheet tab and merge them into a full sheet while maintaining the headers, and currently I'm not sure how to find the range to merge because the number of rows per response is unknown prior to the calls being made, and add that range to a new array.
Please help me! Thank you!
I have tried to merge the sheets myself, but so far I end up messing it up and the sheets overwrite each other or the data is incomplete. Again I'm not sure how to find the range to merge because the number of rows per response is unknown prior to the calls being made, and my google searches are coming up with only predefined ranges.
Example Code:
//call the API for each meeting
function callAPI(tokenKey)
{
//declare the set of meeting IDs as an array
var meetingIDSets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
var meetingNames = ["Name1", "Name2", "Name3", "Name4", "Name5", "Name6", "Name7", "Name8", "Name9", "Name10", "Name11", ];
//run through each meeting ID and make an API call to generate the attendance report
for (var i = 0; i < meetingIDSets.length; i++)
{
//get the current sheet to place data into
var ss = SpreadsheetApp.getActiveSpreadsheet();
//create the new sheet to place data into
currentSheet = ss.insertSheet();
//rename sheet name to match the meeting owner
currentSheet.setName(meetingNames[i]); // + i.toString());
//call the API to get attendance report for each given meeting ID using the provided access token
getAttendanceReport(meetingIDSets[i], tokenKey, currentSheet, meetingNames);
}
}
//call the API to pull the attendance report
function getAttendanceReport(meetingID, token, sheet, hosts)
{
//declare participant join and leave time variables
var joinTime = "";
var leaveTime = "";
//declare duration of participant attendance
var duration = 0;
//declare the name of the participant
var name = "";
//declare the email of the participant
var email = "";
//set the API URL to be called
var URL_STRING = "https://apiwebsite.com/meetings/" + meetingID + "/participants?page_size=300";
//access the response and parse it into a json file
const authHead = { 'method' : 'GET', 'headers' : {'Authorization' : 'Bearer' + token}, muteHttpExceptions: true};
var response = UrlFetchApp.fetch(URL_STRING, authHead);
var json = response.getContentText();
var data = JSON.parse(json);
//declare the participants array
var participantArray = [];
//then assign the response data into that array
for (i = 0; i < data.participants.length; i++)
{
participantArray[i] = data.participants[i];
}
//title the columns in the sheet
sheet.getRange(1,1).setValue("Host");
sheet.getRange(1,2).setValue("Name");
sheet.getRange(1,3).setValue("Join Time");
sheet.getRange(1,4).setValue("Leave Time");
sheet.getRange(1,5).setValue("Duration");
sheet.getRange(1,6).setValue("Email");
//using a for loop, assign each response data point to its respective variable, then assign that value to the intended cell
for (var i = 0; i < participantArray.length; i++)
{
//assign the hostname based on the given current meetingID
switch (meetingID)
{
case 1:
hostName = "Name1";
break;
case 2:
hostName = "Name2";
break;
default:
hostName = "unknown";
}
//assign the current index variables
name = participantArray[i].name;
joinTime = participantArray[i].join_time;
leaveTime = participantArray[i].leave_time;
email = participantArray[i].user_email;
duration = participantArray[i].duration;
//assign the variables to the correct cells, starting at the second row
sheet.getRange(2+i,1).setValue(hostName);
sheet.getRange(2+i,2).setValue(name);
sheet.getRange(2+i,3).setValue(joinTime);
sheet.getRange(2+i,4).setValue(leaveTime);
sheet.getRange(2+i,5).setValue(duration);
sheet.getRange(2+i,6).setValue(email);
}
}
It looks like the answer to my question is to use the sheet function getLastRow which will return the last row of content for the given sheet, which I can then feed as input for transposing the data into an array for each sheet and then apply that array into a new sheet.
How to pull data in a range of cell and then push those values into an array of data?
In this code I tried to get those values in range E2:E97 and then push those values to an array such as [E2 value, E3 Value, etc] then set value to database sheet using dataS.getRange(dataS.getLastRow()+1).setValue(value);
but it seems I can't get it done with those code. so any idea to do this?
// Save Data
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("newform");
var dataS = ss.getSheetByName("newdb");
var dataBaseNameColIndex = 1;
var formDataRange = ['E2:E97'];
var ui = SpreadsheetApp.getUi();
var response = ui.alert(
'Save Data ?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
for (var i = 2; i < formDataRange.length; i++) {
var value = formS.getRange(formDataRange[i], 5).getValue();
dataS.getRange(dataS.getLastRow() + 1).setValue(value);
console.log(saveData);
}
}
}
Instead of trying to copy and paste data cell-by-cell, you may want to do it in one go. That would be more efficient.
So instead of:
var value = formS.getRange(formDataRange[i], 5).getValue();
you could use the following without the for loop:
var allValues = formS.getRange('E2:E97').getValues();
And then post the data to the new range like this:
dataS.getRange(dataS.getLastRow() + 1, 2, allValues.length, allValues[0].length).setValues(value);
I am assuming you want to paste the data into column 2 onwards. Adjust the 2nd value in getRange() above accordingly.
The 3rd and 4th values in getRange() above are the number of rows and columns to pasts.
allValues.length is the number of rows of data. In this case it would be 95.
and allValues[0].length would the number of columns in the top row of the data copied. And in this case it should be one.
I am suggesting this way as you won't need to keep fiddling with the number of rows and columns if you change the copy range dimensions later on.
PS:
dataS.getRange(dataS.getLastRow() + 1).setValue(value);
is missing the column number in getRange()
I am using google sheets quite a lot, but now I am trying to use google apps script to get and update dynamic data retrieved from formulas into a static table.
So, I have a sheet called 'dynamique', with formulas retrieving, filtering and sorting data from other spreadsheets.
I want to be able to work on this data, so I am trying to create a button which would copy all the values from the 'dynamique' sheet into another sheet called 'statique'. That is, I want a formula which would check if the values from the column C of the 'dynamique' sheet are in the column C of the 'statique' sheet. And if the values aren't there, I want the script to copy them. (columns A and B are empty)
I've managed to get my script to work for one column, but now, I want to copy the whole line.
For example, if the value in dynamique!C10 can't be found in statique!C:C, my script writes the value of dynamique!C10 in the first empty cell of the column statique!C:C. But I want it to write dynamique!C10:J10 into my destination sheet (say it's going to be maybe statique!C8:J8).
Here is my code, working for only one cell.
function dynamicToStatic() {
var dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
var staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
var dynLength = dynSheet.getRange("C1:C").getValues().filter(String).length;//.getLastRow();
var staLength = staSheet.getRange("C1:C").getValues().filter(String).length;
var staRange = staSheet.getRange(6,3,staLength-1);
var staValues = staRange.getValues();
var rangeToCheck = dynSheet.getRange(6,3,dynLength-1,8);
var valuesToCheck = rangeToCheck.getValues();
var numRows = rangeToCheck.getNumRows();
var staNumRows = staRange.getNumRows();
for (i = 0; i<= numRows; i++) {
var row = valuesToCheck[i];
var index = ArrayLib.indexOf(staValues , -1 , row);
if (index == -1) {
//if (staValues.indexOf(row) != -1) {
staSheet.getRange(i+6,3,1,8).setValues(row);
}
}
var timestamp = new Date();
staSheet.getRange(4,3).setValue('List updated on the: '+timestamp);
}
Now I can't manage to retrieve the whole line of the array, so as to be able to copy it using range.setValues(). I always get error messages.
Any help would be more than appreciated...
function gettingFullRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const shsr=2;//data startrow
const vA=sh.getRange(shsr,1,sh.getLastRow()-shsr+1,sh.getLastColumn()).getValues();
let html='';
vA.forEach((r,i)=>{
html+=Utilities.formatString('<br />Row:%s is %s',i+shsr,r.join(','));
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Row");
}
So i did some re-writing to your code and made some comments in there. I hope this will make some things clear.
Array's are 0 indexed. So if the value is NOT found in the .indexOf then it would return -1. Also (for speed) i first push all the result to a array and then set the array in one "action" this saves a lot of time. The calls to and from a sheet takes the most time.
For the conversion to a 1d array i used spread operator
See this link for difference in const / var / let
The timestamp string i updated with the use of Template literals
If you have some questions, shoot! (also i did not test this ofcourse)
function dynamicToStatic() {
const dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
const staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
const dynValues = dynSheet.getRange(1,3,dynSheet.getLastRow(),8).getValues();
//This is a 2d array
const staRaw = staSheet.getRange(6, 3, staSheet.getLastRow()).getValues();
//Convert to 1d array, for the indexoff later on, this way it is easier.
const staValues = [].concat(...staRaw);
//to store the output, as a 2d array, inside the if you see i push it in as array so you have the 2d array for the setValues.
const output = [];
for (let i = 0; i < dynValues.length; i++){
//i = the index of the array (row) inside the array of rows, the 0 would be the values of column C.
if (staValues.indexOf(dynValues[i][0]) >= 0){
output.push([dynValues[i]]);
}
}
//Start by the lastrow + 1, column C(3), ouput is a array of arrays(rows), then get the [0].lengt for the columns inside the row array.
staSheet.getRange(staSheet.getLastRow()+1, 3, output.length, output[0].lenght).setValues(output);
const timestamp = new Date();
staSheet.getRange(4,3).setValue(`List updated on the: ${timestamp}`);
}
I have two arrays
Ary1 has n columns and length p
its tabular data looks like
Ary2 has 5 columns and length q
Its tabular data looks like
I output the data like this
sh.getRange(1, 1, Ary1.length, Ary1[0].length).setValues(Ary1);
sh.getRange(sh.getLastRow()+1, 1, Ary2.length, Ary2[0].length).setValues(Ary2);
I want to append an Ary2 under Ary1 to get the result array of length p+q
with tabular data
So I only have to output the data once
I have tried
var result = Ary1.concat(Ary2);
sh.getRange(1,1,result.length,result[0].length).setValues(result);
and
Ary1.push(...Ary2);
sh.getRange(1,1,Ary1.length,Ary1[0].length).setValues(Ary1);
sh.getRange(1,1,result.length,result[0].length).setValues(result);
But I get
The number of columns in the data does not match the number of columns in the range. The data has 5 but the range has 25.
It would be best to mutate Ary1 or Ary2 then to create a new array
Thank you
I think that the reason of your issue is the difference of the column length of each array. In order to avoid this, it is required to be the same length for each column in the array. So I would like to propose the following modification.
Pattern 1:
In this pattern, Spreadsheet service is used.
Modified script:
var Ary1 = ,,, // Please set the value.
var Ary2 = ,,, // Please set the value.
if (Ary1[0].length > Ary2[0].length) {
var len = Ary1[0].length - Ary2[0].length;
Ary2 = Ary2.map(r => r.concat(Array(len).fill("")));
} else if (Ary1[0].length < Ary2[0].length) {
var len = Ary2[0].length - Ary1[0].length;
Ary1 = Ary1.map(r => r.concat(Array(len).fill("")));
}
var values = Ary1.concat(Ary2);
sh.getRange(1, 1, values.length, values[0].length).setValues(values);
Pattern 2:
In this pattern, Sheets API is used. When the Sheets API is used, it is not required to be the same column length in the array.
Modified script:
Before you use this script, please enable Sheets API at Advanced Google services.
var spreadsheetId = "###"; // Please set Spreadsheet ID.
var sheetName = "Sheet1"; // Please set sheet name.
var Ary1 = ,,, // Please set the value.
var Ary2 = ,,, // Please set the value.
var values = Ary1.concat(Ary2);
Sheets.Spreadsheets.Values.update({values: values}, spreadsheetId, sheetName, {valueInputOption: "USER_ENTERED"});
References:
map()
Method: spreadsheets.values.update
I have a dataset of 35 columns and 300 rows. I want to get the range that contains rows only for certain values in column 30 (names). The name for which to filter the data is based on the report file cell B6 in the report sheet that is active. So far I tried this:
var report = SpreadsheetApp.getActiveSpreadsheet();
var tsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getValues();
var criteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo(agent).build();
var trange = tsheet.getRange().createFilter().setColumnFilterCriteria(30, criteria); // ERROR
var tdata = trange.getValues();
I receive an error Exception: The parameters () don't match the method signature for SpreadsheetApp.Sheet.getRange.
The second part, I only want to get several columns, 5,6,7, 13, 15. I can't create another filter with the Spreadsheet app, so is the only way to make an array and filter out the needed data from there? I'm just trying to think ahead and reduce the amount of calculations.
Try with filter():
var report = SpreadsheetApp.getActiveSpreadsheet();
var tsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getValue();
var data = tsheet.getRange('A1:AI300').getValues();
var tdata = data.filter(function (row) {
return row[29] == agent && row[5] == 'Closed' ; // starts from 0, column A is 0.
});
To select particular columns from tdata do:
var cr_data = getCols(tdata,[5,6,7, 13, 15]);
where getCols() is defined as follows:
function getCols(arr,cols) {
return arr.map(row =>
row.filter((_,i) => cols.includes(++i)))
}
and finally you can copy cr_data to a particular place/sheet like that:
sheet.getRange(1,1,cr_data.length,cr_data[0].length).setValues(cr_data);
Regarding the second part of your question I would like to redirect you to this post:
Best method to extract selected columns from 2d array in apps script