Google Sheets appendRow and add values to specific columns - google-apps-script

I have developed a sidebar form that appends values to an existing spreadsheet.
The form works; however, the new values are added starting at column A. I would like to adapt my code to select the columns to which appended values are inserted. The backend code used to append data is:
function addNewRow(rowData) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName("Recruitment_Contacts");
ws.appendRow([rowData.LnRT,rowData.FnRT,rowData.Gn,rowData.St,rowData.Dtr,rowData.Trn,rowData.Td]);
return true;
}
The code used within HTML script to add data after the submit button is selected is:
function afterButtonClicked(){
var ln = document.getElementById("LnRT");
var fn = document.getElementById("FnRT");
var gn = document.getElementById("Gn");
var st = document.getElementById("St");
var dtr = document.getElementById("Dtr");
var trn = document.getElementById("Trn");
var td = document.getElementById("Td");
var rowData = {LnRT: ln.value, FnRT: fn.value, Gn: gn.value, St: st.value, Dtr: dtr.value, Trn: trn.value, Td: td.value};
google.script.run.withSuccessHandler(afterSubmit).addNewRow(rowData);
}
Any support that the community can provide would be greatly appreciated.
Thank you.

I think this is what you're looking for:
Selecting content by id
function addNewRow(rowData) {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName("Recruitment_Contacts");
const hA=sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];
idx={};
hA.forEach(function(h,i){idx[i]=h;});
ws.appendRow([rowData[idx[0]],rowData[idx[1]],rowData[idx[2]],rowData[idx[3]],rowData[idx[4]],rowData[idx[5]],rowData[idx[6]]]);
return true;
}
I do this a lot and I just hide the top row and it doesn't seem to bother anybody and warn them that if the screw it up I get to come and charge more for fixing it.

Solution
To achieve what you are aming for of appending a new row from the column index you want, you must use the Apps Script method setValues on the right range. Here is the code implementation with self explanatory comments to achieve this:
// you can pass a column index to this function as a new parameter (or inside rowData)
function addNewRow(rowData,column) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName("Recruitment_Contacts");
// Set as the data what we want to insert to then be able to know how many
// columns will this fill
// We need to wrap the data into [] as it is expecting a nested array for [cols] and [rows]
var data = [[rowData.LnRT,rowData.FnRT,rowData.Gn,rowData.St,rowData.Dtr,rowData.Trn,rowData.Td]];
// Set the values on the first row of the sheet (reproducing the behaviour of appendRow)
// and starting from the row index you want (5 for example).
// It will be 1 row and the length of the data
// long in terms of columns.
ws.insertRows(1,1);
ws.getRange(1, column,1,data[0].length).setValues(data);
return true;
}
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

To resolve my issue, I created a server side function called "recruitInfo" and listed all the columns in my worksheet. I then called that function in my HTML code. The video I used to walk me through this process is Web App - Google Sheets CRUD, Part 5
function addRecruit(recruitInfo){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName("sheetName");
const uniqueIds = ws.getRange(2, 1, ws.getLastRow()-1, 1).getValues();
var maxNum = 0;
uniqueIds.forEach(r => {
maxNum = r[0] > maxNum ? r[0] : maxNum
});
var newID = maxNum + 1;
ws.appendRow([newID,
recruitInfo.lastName,
recruitInfo.firstName])
}
function addRecruit(){
var recruitInfo = {};
recruitInfo.firstName = document.getElementById("firstName").value;
recruitInfo.lastName = document.getElementById("lastName").value;
google.script.run.withSuccessHandler(function(){}).addRecruit(recruitInfo);
}

Related

Google Apps Script - Better way to do a Vlookup

I am doing a kind of VLOOKUP operation in a column with about 3K cells. I am using the following function to do it. I commented on what the code is doing in the function, but to summarize:
It creates a map from values to search for from a table with metadata
It iterates each value of a given range, and searches for coincidences in the previous map
If coincidences are found, it uses the index to capture the second column of the metadata table
Finally, sets the value captured in another cell
This is the code:
function questions_categories() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("data_processed");
// get metadata. This will work as the table to look into
// Column B contains the matching element
// Column C contains the string to return
var metadata = ss.getSheetByName("metadata").getRange('B2:C').getValues()
// Just get the different values from the column B
var dataList = metadata.map(x => x[0])
// Used to define the last cell where to apply the vlookup
var Avals = sheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
// define the range to apply the "vlookup"
const questions_range = sheet.getRange("Q2:Q" + Alast);
forEachRangeCell(questions_range, (cell) => {
var searchValue = cell.getValue();
// is the value to search in the dataList we defined previously?
var index = dataList.indexOf(searchValue);
if (index === -1) {
// if not, throw an error
throw new Error('Value not found')
} else {
// if the value is there, use the index in which that appears to get the value of column C
var foundValue = metadata[index][1]
// set the value in two columns to the right
cell.offset(0, 2).setValue(`${foundValue}`);
}
})
}
forEachRangeCell() is a helper function to iterate through the range.
This works very well, but it resolves 3-4 cells per second, which is not very efficient if I need to check thousands of data. I was wondering if there is a more performant way to achieve the same result.
To improve performance, use Range.setValues() instead of Range.setValue(), like this:
function questions_categories() {
const ss = SpreadsheetApp.getActive();
const source = { values: ss.getRange('metadata!B2:C').getValues() };
const target = { range: ss.getRange('data_processed!Q2:Q') };
source.keys = source.values.map(row => row[0]);
target.keys = target.range.getValues().flat();
const result = target.keys.map(key => [source.values[source.keys.indexOf(key)]?.[1]]);
target.range.offset(0, 2).setValues(result);
}
See Apps Script best practices.

How do you run the same API multiple times on Google Apps Script?

So I'm totally new at using Google Apps Script, basically just started a few hours ago. Also new to stackoverflow so excuse my formatting. For the life of me, I cant seem to find how to run Bored API to get 20 unique suggested activities.
I am also wondering how to code an If statement within the same cell column , if accessibility is less than or equal to 0.3, then it is Easy
If accessibility is between 0.3 and 0.6, then it is Medium
If it is 0.6 or more, then it is Hard. (Since google excel doesnt seem to support a cell holding two values at the same time)
Lastly, for the Day column how do I write a snippet that randomly suggests a day to do the suggested activity.
Questions:
How to run 20 Unique suggested activities using same API?
How do I code an if statement for Accessibility?
How to write a snippet that randomly suggest a day to do the suggested activity?
Pls help :'(
Here's the link to the API I used. https://www.boredapi.com/documentation
Here's my current code in Apps Script,
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheetByName("MAIN");
var URL_STRING = "http://www.boredapi.com/api/activity/";
var response = UrlFetchApp.fetch(URL_STRING);
var json = response.getContentText();
var data = JSON.parse(json);
var Activity = data.activity;
var Accessibility = data.accessibility;
var Type = data.type;
var Price = data.price;
var Link = data.link;
var Key = data.key;
mainSheet.getRange('B2').setValue([Activity]);
mainSheet.getRange('C2').setValue([Accessibility]);
mainSheet.getRange('D2').setValue([Type]);
mainSheet.getRange('E2').setValue([Price]);
mainSheet.getRange('G2').setValue([Link]);
mainSheet.getRange('H2').setValue([Key]);
}
Here's the SS of what it looks like,
Excel SS
To answer your first question, use a while loop, Array.includes(), Array.forEach(), Array.push() and finally Range.setValues(), like this:
function append20UniqueActivities() {
const sheet = SpreadsheetApp.getActive().getSheetByName('MAIN');
const URL_STRING = 'http://www.boredapi.com/api/activity/';
const numActivities = 20;
const fields = ['activity', 'accessibility', 'type', 'price', 'link', 'key'];
const result = [];
const activities = [];
while (true) {
const response = UrlFetchApp.fetch(URL_STRING);
const json = response.getContentText();
const data = JSON.parse(json);
if (activities.includes(data.activity)) {
continue;
}
activities.push(data.activity);
const row = [i];
fields.forEach(field => row.push(data[field]));
result.push(row);
if (result.length >= numActivities) {
break;
}
}
const lastRow = sheet.getLastRow();
sheet
.insertRowAfter(lastRow)
.getRange(lastRow + 1, 1, result.length, result[0].length)
.setValues(result);
}

FilterCriteria on Google Script to filter using checkboxes

Im currently trying to move in bulk all the rows from a sheet where the checkbox is selected, I was trying to use "newFilterCriteria" but the one I use (found online) check on specific words while I want to use checkboxes.
The code so far does:
Get the source page, declare the filter criteria
function create_filter(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Fridge"); // DECLARE THE SOURCE PAGE
var range = sheet1.getRange("A5:J"); // DECLARE THE RANGE TO BE FILTERED LATER
var filter = range.createFilter();
var Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().withCriteria(true); // HERE IS THE PROBLEM, THE ORIGINAL CODE SAYS "newFilterCriteria().whenNumberGreaterThan(1000);" BUT INSTEAD OF A NUMBER, I NEED A FILTER BASED ON CHECKBOXES BEING EITHER TRUE OR FALSE
var add_filter1 = filter.setColumnFilterCriteria(1,Filter_Criteria1);
Logger.log("Filter has been added.");
var range = sheet1.getDataRange();
var new_sheet = ss.insertSheet(); // CREATE THE DESTINATION TAB
new_sheet.setName("TEST"); // NAME THE DESTINATION TAB AS "TEST"
range.copyTo(new_sheet.getRange(1,1));
filter.remove();
}
Any suggestions or help? Thank you! I tried looking around but havent get to find the right way to filter with the checkboxes.
Something else: Not sure if I can avoid an iteration since there are many rows to copy and it would be a slow process I think, is there a way to say something like a query such as "select all rows where column 1 is true"?
The image is just an example of the table.
Thanks!
Move Checked
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
let a = [];
let d = 0;
vs.forEach((r, i) => {
if (r[0] == true) {
a.push(r)
sh.deleteRow(i + 2 - d++);
}
});
if (a) {
ss.insertSheet('Test').getRange(1, 1, a.length, a[0].length).setValues(a);
}
}
Use whenTextEqualTo true, for filtering in checkboxes:
const Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('TRUE').build()

Copy a specific row, that mets a condition, form one sheet to another

I made a similar request a few days ago using the following link:
How to copy a specific row from one sheet to another (Google Apps Script)
Now I have new requirements for which I need your help:
the script should copy specific rows that have value "No" in column T from one sheet to another sheet. It is important that it copies the values and the formatting.
the complete row should be copied.
after successful copying, the row should be deleted in the sourcesheet.
Can you please help me? Thank you very much!
Below you can find #Marios solution so far. At this point again many thanks!
const ss = SpreadsheetApp.getActiveSpreadsheet();
const srcSheet = ss.getSheetByName("[Overview] All_Cases");
const tarSheet = ss.getSheetByName("OPS_FUNNEL");
const data = srcSheet.getDataRange().getValues().filter(r=>r[21]=='No').map(r => [r[0]]);
var tarlast = tarSheet.getRange("A:A").getValues().filter(String).length;
if (data.length>0){
tarSheet.getRange(tarlast+1,1,data.length,1).setValues(data);
}
Explanation:
You can store the deleted row indexes inside the filter function itself and then use the classical approach to delete rows backwards.
Solution:
Column T has an array index of 19, not 21. Be careful with this.
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const srcSheet = ss.getSheetByName("[Overview] All_Cases");
const tarSheet = ss.getSheetByName("OPS_FUNNEL");
const rowsD = [];
const data = srcSheet.getDataRange().getValues()
.filter((r,i)=>{
let val = r[19]=='No';
if(val){
rowsD.push(i+1);
return val;
}
});
if (data.length>0){
tarSheet.getRange(tarSheet.getLastRow()+1,1,data.length,data[0].length).setValues(data);
for (var i = rowsD.length - 1; i>=0; i--) {
srcSheet.deleteRow(rowsD[i]);
}
};
}

How to apply filters of spreadsheet using google apps scripts?

I need to apply a filter on a spreadsheet and then apply the filter on the active spreadsheet.
Tried using the Filter Class but not sure what is incorrect
'''
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("main sheet");
var dataMatrix1 = activeSheet.getRange(1, 1, activeSheet.getLastRow(), activeSheet.getLastColumn());
function applyFilter(){
Logger.log("mark1");
var filteredData = dataMatrix1.createFilter(); //filter created
var a = 'a';
filteredData.sort(1, false);
filteredData.setColumnFilterCriteria(1 , a);
Logger.log("Mark2");
}
'''
The spreadsheet has 2 rows with value = 'a' in the first column. Need to apply a filter to the sheet and filter out rows with value = 'a'.
You are very close to accomplish your request; you only need to create a filter criteria instead of using the a variable. You can see exactly which methods to use on the following code. Also, the filtered string must be inside of an array, so I sightly modified your a variable.
function applyFilter() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(
"main sheet");
var dataMatrix1 = activeSheet.getRange(1, 1, activeSheet.getLastRow(),
activeSheet.getLastColumn());
var filteredData = dataMatrix1.createFilter(); //filter created
var a = ['a'];
filteredData.sort(1, false);
var filterCriteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(a)
.build();
filteredData.setColumnFilterCriteria(1, filterCriteria);
}
Please, do not hesitate to ask for more help if you keep having problems.