How to modify Apps Script for Google Sheets to move specific cell from a row into another Sheet - google-apps-script

The script, that I will post below, allows me to use a check box to move an entire row from one sheet to another in the same workbook. Everything works (I was following a YouTube tutorial), but it moves the entire row of data. Instead, I need it to move only data from specific columns, i.e. move the info from column A to C or move the info from column A and column K. I know this might be a simple question, but I just don't know which line to modify and how to modify it so that the script only pulls the info from specific columns within the row to move to the other sheet.
function sendMail() {
var client = 3;
var date = 0;
var email = 19;
var dnr = 18;
var emailTemp = HtmlService.createTemplateFromFile("email");
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Promos");
var data = ws.getRange("A3:T" + ws.getLastRow()).getValues();
data = data.filter(function(r){return r[1] == true })
data.forEach(function(row){
emailTemp.client = row[client];
emailTemp.date = row[date];
emailTemp.dnr = row[dnr];
var htmlMessage = emailTemp.evaluate().getContent();
GmailApp.sendEmail(row[email],
"Promo Reminder",
"Your email does not support html",
{name: "JV Ops", htmlBody: htmlMessage}
);
});
}
I tried to modify the line "var row = range.getRow()" by putting column letters in the parenthesis but it didn't do anything, not only did it not move the info from those specific columns but it no longer moved the entire row. It just no longer did anything.

You have a spreadsheet in which a "checked" checkbox identified the row(s) that are to be sent an email. However you can't identify the actual row number of the checked checkbox.
There are two issues in your script:
data = data.filter(function(r){return r[1] == true }) replaces the array that contains ALL the data on the sheet with just those rows that contain checked checkboxes.
you need to compare the new filtered array with the original array in order to get the row numbers. I adapted the method described in StackOverflow compare rows on google spreadsheets
the array equalRows contains the row numbers/indexes that contain checked checkboxes.
the OP can use the row number together with the mailData array to copy data on the relevant row.
function myFunction() {
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Promos")
var data = ws.getRange("A3:T" + ws.getLastRow()).getValues();
// Logger.log(data) // DEBUG
var mailData = data.filter(function(r){return r[1] == true })
// Logger.log(mailData) // DEBUG
// compare rows on google spreadsheets
// https://stackoverflow.com/a/34960630/1330560
var equalRows = [];
for(var i in data){
var anEqualRow = false;
for(var j in mailData){
if(data[i].join() == mailData[j].join()){
anEqualRow = true;
}
}
if(anEqualRow){
var row = +i+3
equalRows.push(row);
}
}
// Logger.log(equalRows) // DEBUG
}

Related

Google Appscript: Copy Data from a Form Sheet to Another Sheet in Last Row and Clear Form

Trying to get an Appscript to run but I can't seem to figure out the last few steps.
Background: I have a Google Sheet with two tabs, #1 is called "Data Form" which hosts a fillable form to capture Transaction information to then be input onto tab #2 called "Posted Transactions". This is a personal budget spreadsheet..
Anyways, The script below is intended to take the information input on the "Data Form", verify what the last row with data is on the "Posted Transactions" tab based on whether or not Column A has any data. (To further clarify, the "Posted Transactions" tab has formulas in columns G-I which prohibits me from using a simple "Find last Row" script.)
As it is written now, I receive an error "Exception: The number of columns in the data does not match the number of columns in the range. The data has 6 but the range has 1.
RecordNewTransaction # Code.gs:24"
Any suggestions to make this work properly?
UPDATE:
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
After many attempts at trial and error, I needed to edit the line shown above. Current & full script is performing as expected now and is shown below. Image snips of what I was trying to accomplish are also shown for reference.
function RecordNewTransaction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Data Form"); //Form Sheet
var datasheet = ss.getSheetByName("Posted Transactions"); //Data
//Input Values
var values = [[formSS.getRange("B4").getValue(),
formSS.getRange("B6").getValue(),
formSS.getRange("B8").getValue(),
formSS.getRange("B10").getValue(),
formSS.getRange("B12").getValue(),
formSS.getRange("B14").getValue()]];
var columnToCheck = datasheet.getRange("A:A").getValues();
var lastRow = getLastRowSpecial(columnToCheck);
Logger.log(lastRow);
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
Logger.log(dataRange);
var dataValues = dataRange.getValues();
Logger.log(dataValues);
dataRange.setValues(values);
Logger.log(dataRange.setValues(values))
formSS.getRange('B4:B14').clearContent();
};
function getLastRowSpecial(range){
var rowNum = 0;
var blank = false;
for(var row = 0; row < range.length; row++){
if(range[row][0] === "" && !blank){
rowNum = row;
blank = true;
}else if(range[row][0] !== ""){
blank = false;
};
};
return rowNum;
};
Try
var dataRange = datasheet.getRange(lastRow,1,values.length,values[0].length)
replace 1 as necessary (this is the firts column where the data will be stored
Reference:
getRange(row, column, numRows, numColumns)
See edited question above which contains the revised script needed.
The problem here was that columns G-I of my results sheet contain formulas (shown in grey) which required this line below to be modified to grab the last column -3, otherwise the script was looking at too many columns that it didn't need to. Also had to modify "lastRow" to "lastRow+1" because this kept overriding the very last line of data I already had input and the +1 will make new data go to the next available row.
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);

Google Apps Script - Conditionally retrieve data from other Google Sheet to Overview sheet

To explain the larger context: there are several forms which generate different sheets. I'm looking for a way to conditionally copy some of the responses sheet to a seperate "Overview" document. Code-wise, I had some ideas for the Overview document, but stranded near the start.
My method was going to be to build functions for all the information I want to retrieve, such as date of birth (example in code block below), date of submission and phone number, when I click on a button. The information may only be copied if the first and surname match the ones in the Overview. The order of the sheets in different docs are not the same and the column length is continually in flux. Furthermore, the amount of rows in the Overview doc is different than the form submission sheets.
In other words: if Anne Annenson would be the twenty-first respondent to a form, I want that information in the overview sheet where they are the first person.
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetFirstNameCheck = targetSpreadsheet.getRange("A4:A");
var targetSurnameCheck = targetSpreadsheet.getRange("B4:B");
var sourceSpreadsheetDob = SpreadsheetApp.openById("...");
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceFirstNameCheckDob = sourceSheetDob.getRange("C2:C");
var sourceSurnameCheckDob = sourceSheetDob.getRange("D2:D");
var sourceRangeDob = sourceSheetDobConsent.getRange("E2:E");
if (sourceFirstNameCheckDob==targetFirstNameCheck && sourceSurnameCheckDob==targetSurnameCheck){ //then I want to copy the data
var sourceData = sourceRangePronouns.getValues();
var targetRangeDob = targetSheet.getRange("C4:C");
}
else (//I want it to leave the cells alone, so any text or formatting that might have been put in manually is still there.){
}
}
I would like for the responses to remain in the form response sheets as well.
Any thoughts?
Cooper already explained all the things you need in the comments. And below is what your code would look like following Cooper's comments.
Code
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetLastRow = targetSheet.getLastRow();
// range equivalent to A4:B
var targetNamesCheck = targetSheet.getRange(4, 1, targetLastRow - 3, 2).getValues();
// tested in same spreadsheet, change "targetSpreadsheet" to openById on your actual script
var sourceSpreadsheetDob = targetSpreadsheet;
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceLastRow = sourceDob.getLastRow();
// range equivalent to C2:D
var sourceNamesCheckDob = sourceDob.getRange(2, 3, sourceLastRow - 1, 2).getValues();
// range for data to be copied (E2:G in my sample data)
var sourceRangeDob = sourceDob.getRange(2, 5, sourceLastRow - 1, 3).getValues();
var output = [];
targetNamesCheck.forEach(function (targetNames) {
// search sourceNamesCheckDob for targetNames
var index = searchForArray(sourceNamesCheckDob, targetNames);
// if targetNames is in sourceNamesCheckDob, save the data on that row for later
if (index > -1)
output.push(sourceRangeDob[index]);
// append blank cells if data is not found
else
output.push(new Array(sourceRangeDob[0].length));
});
// if there were names that were found, write the data beside the targetNames
if (output.length > 0) {
targetSheet.getRange(4, 3, output.length, output[0].length).setValues(output);
}
}
// function to search the array for the object
function searchForArray(haystack, needle) {
var i, j, current;
for(i = 0; i < haystack.length; ++i) {
if(needle.length === haystack[i].length) {
current = haystack[i];
for(j = 0; j < needle.length && needle[j] === current[j]; ++j);
if(j === needle.length)
return i;
}
}
return -1;
}
Overview:
Form responses 1:
Overview after running getDobs:
EDIT:
Since there are no methods that includes the apostrophe when the cell value is being fetched, easiest way is to have the sheets identify the phone number as text so it won't remove the 0 in the beginning. I've thought of 3 ways to have the 0 included in the output:
Add the apostrophe manually on the specific output column via script
Add dashes on the number so it is treated as text (09395398314 -> 093-9539-8314) (just an example, not sure if that is the right format)
Format the output column into number -> plain text instead of number -> automatic
I prefer formatting the output column as that will be the fastest and easiest thing to do.
Format:
Output:
Note:
This function will fill up rows where names in Overview are present in Form responses 1.
References:
Check whether an array exists in an array of arrays?
javascript create empty array of a given size

Replace Last row from each column into G-Slides Using Appscript

I have a g-sheet where the last row is the sum of the each column rows.
Wish to Achieve : I want to now populate a template Google slide using the last values of each column into placeholders.
Template slide structure is like this
My efforts as of now while writing an app script function is:
function replace_template_with_text_charts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
// sheet for handlebar placeholders = dataSheetName
var rows = sheet.getDataRange().getDisplayValues();
// Append column for link to generated slide decks at the end of the row
var resultColumn = sheet.getLastColumn() + 1;
sheet.getRange(1, resultColumn).setValue("Generated slide deck");
// Make name2index store a mapping from column names to indexes.
var name2index = {};
var header = rows[0];
for (var i = 0; i < header.length; i++) {
name2index[header[i]] = i;
}
Logger.log(name2index);
Logger.log(rows[sheet.getRange(sheet.getLastRow(), name2index['A'])])
// Get the presentation template file name based on id that was indicated in set up
var p1 = SlidesApp.openById('1X3tAszOcjo-QkrRNj3TWFm-_--GKPxdLaoJYp6oHJ7M')
for (var i = 1; i < rows.length; ++i) {
var row = rows[i];
sheet.getRange(i+1, resultColumn).setValue(SlidesApp.openById('1X3tAszOcjo-QkrRNj3TWFm-_--GKPxdLaoJYp6oHJ7M').getUrl());
// Logger.log(i+1, resultColumn)
// Create the text merge (replaceAllText) requests for this presentation (does not make changes yet).
var requests = [];
for (var colName in name2index) {
requests.push({
replaceAllText: {
containsText: {
text: '{{' + colName + '}}',
matchCase: true
},
replaceText: row[sheet.getRange(sheet.getLastRow(), name2index[colName])]
}
});
}
// Execute the requests for this presentation.
var result = Slides.Presentations.batchUpdate({
requests: requests
}, '1X3tAszOcjo-QkrRNj3TWFm-_--GKPxdLaoJYp6oHJ7M');
}
Unfortunately it throws NULL for each column.
Any help is appreciated.
Modification points:
In your question, it seems that you want to use the values of the last row of "Sheet1". But in your script, it seems that you want to use all rows including the last row.
From Wish to Achieve : I want to now populate a template Google slide using the last values of each column into placeholders., I believe you wanted to use the values of the last row of "Sheet1".
When your goal is to use the values of the last row of "Sheet1", and to replace {{A}},{{B}},{{C}},{{D}},{{E}} in the Slides with the values, I think that your for loop is required to be modified. In the current loop, all rows are used and Slides.Presentations.batchUpdate is used in the loop.
In your situation, I thought that replaceAllText method of Class Slide instead of Slides.Presentations.batchUpdate method of Slides API.
In order to achieve your goal, I would like to propose the following modified script.
Modified script:
function replace_template_with_text_charts() {
// 1. Retrieve values of header row and last row from Spreadsheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var [header, ...rows] = sheet.getDataRange().getDisplayValues();
var lastRow = rows.pop();
// 2. Put the values of "Generated slide deck" and URL of Slides.
var resultColumn = sheet.getLastColumn() + 1;
sheet.getRange(1, resultColumn).setValue("Generated slide deck");
var slide = SlidesApp.openById('1X3tAszOcjo-QkrRNj3TWFm-_--GKPxdLaoJYp6oHJ7M');
sheet.getRange(sheet.getLastRow(), resultColumn).setValue(slide.getUrl());
// 3. Replace `{{A}},{{B}},{{C}},{{D}},{{E}}` with the values of last row of Spreadsheet.
header.forEach((e, i) => slide.replaceAllText(`{{${e}}}`, lastRow[i]));
}
Note:
Please use above script with enabling V8 runtime.
Reference:
replaceAllText(findText, replaceText)

Update/edit google sheets row values from html input

I have an html form where the client's data is inserted in and it appends row with the values on to a google sheet.
In the form, there's a field that searches and returns the clients data when searching for a specific value (id number).
function getID(IDsearch){
var ws = SpreadsheetApp.getActiveSheet();
var data = ws.getRange(3, 1, ws.getLastRow(), 36).getValues();
var dataInput = data.map(function(r){return r[7];});
var position = dataInput.indexOf(IDsearch);
var dataArray = ws.getRange(position+3, 1, 1, 36).getValues();
if(position > -1){
return dataArray;
} else {
return position;
}
}
After this runs, all the input fields in the form are populated with the data from that row.
I need to edit the values in the form and when submit it should overwrite/update the existing row with that id number.
In google sheets documentation, I've found the spreadsheets.values.update method, but I cannot figure this out. I'm pretty new in this and any help would be appreciated.
Thanks everyone!
You want to achieve the following flow.
Input "ID" to id="insertID" and click "Search by ID".
Show the values from Spreadsheet by searching "ID".
Edit the values of id="name" and id="ID".
When "Save data" is clicked, you want to update the values on the Spreadsheet.
From your replying, shared Spreadsheet and script, I could understand like above. If my understanding is correct, how about ths following modification? Please think of this as just one of several possible answers.
Modification points:
In your case, processForm at Google Apps Script side is required to be modified.
Search the row using formObject and overwrite the values of cells.
Modified script:
When your script is modified, please modify processForm at Google Apps Script side as follows. I remove the Spreadsheet ID from the URL. So please set it, before you test the script.
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/###/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Database");
// I added and modified below script.
var ranges = ws.getRange(4, 2, ws.getLastRow() - 3, 1).createTextFinder(formObject.ID).findAll();
if (ranges.length > 0) {
for (var i = 0; i < ranges.length; i++) {
ranges[i].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);
}
} else {
ws.appendRow([formObject.name, formObject.ID]);
}
}
In this modification, when the same IDs are existing, all rows of the same IDs are overwritten. For example, if you want to modify the 1st one, please modify to ranges[0].offset(0, -1, 1, 2).setValues([[formObject.name, formObject.ID]]);.
Reference:
Class TextFinder
Try this:
function getID(IDsearch){
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();//dont know what the sheet is
var rg=sh.getRange(3,1,sh.getLastRow()-2,36);
var data=rg.getValues();
var idA=sh.getRange(3,8,sh.getLastRow()-2,1).getValues().map(function(r){return r[0];});//it looked like column 8 was your id column
var idx=idA.indexOf(IDsearch);
if(idx>-1) {
return ws.getRange(pos + 3,1,1,36).getValues()[0];//flattened the row to a 1d array
}else{
return idx;
}
}
#dianadfonseca, as #Tanaike points out, without more detail about your data structure, people will be speculating in order to answer your question. As I will be...
Please read the following answer, and tailor it to your needs if it works for you.
Example:
function getRow(id){
var ws = SpreadsheetApp.getActiveSheet();
// Number of headers to skip
var numHeaders = 2;
// the starting row
var startRow = numHeaders + 1;
// The column where the IDs are is known
var idCol = 8;
// The number of rows with data not headers
var numRows = ws.getDataRange().getLastRow() - numHeaders;
// An array with the ids to find a match in
// getRange() returns a 2D array, so you can transpose it to flatten it
var ids = ws.getRange(startRow,idCol,numRows).getValues();
ids = transpose(ids)[0];
// Get the index where id matches in ids
var row = ids.indexOf(id);
// If there's a match
if(row > -1){
// Correct row indexing
row = row + startRow;
}
return row;
}
function updateRow(row,data){
var ws = SpreadsheetApp.getActiveSheet();
// The column for each property is known
var propertyOneCol = 1;
// Update property using setValue()
ws.getRange(row,propertyOneCol).setValue(data.propertyOne);
// And so on...
}
// Transpose to avoid looping through the array
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
You can take a look the spreadsheet used for this example here with its bound script to play around.
Here is the function I used for testing
function test(){
// You are receiving this from your form
var data = {"propertyOne":"Juan","propertyTwo":20, "id":123467};
var id = data.id;
updateRow(getRow(id),data);
}

Multiple results from a single column search in a spreadsheet

I just wanted to know if there was a way to pull multiple (only 2 at the most) results with the code provided below. All the code is doing is searching for a user's name in a spread sheet and then displaying the value in the cell next to that user's name. The slight issue I'm running into now is that their name might appear twice in a given column and I would like for both results to be displayed. Any help would be appreciated. The full explanation of what this code is used for can be found here if needed: VLOOKUP style app script using UiApp and textBox form to reference a spreadsheet
// function called when submit button is clicked
function submit(e) {
var app = UiApp.getActiveApplication();
Logger.log(Utilities.jsonStringify(e)); // Log the input parameter (temporary)
// Write the data in the text boxes back to the Spreadsheet
var cell = e.parameter.userName;
var doc = SpreadsheetApp.openById('SPREADSHEET-ID');
var ss = doc.getSheets()[0];
var lastRow = doc.getLastRow();
var data = ss.getRange(2, 1, 2, 4).getValues();
var result = "User not found";
for (nn = 0; nn < data.length; ++nn) {
if (data[nn][1] == cell) {
result = data[nn][1];
break
}; // if a match in column B is found, break the loop
}
// Make the status line visible and tell the user the possible actions
app.getElementById('status').setVisible(true).setText(result);
return app;
}
All you need to do is modify the behavior of the search loop. Perform the search, adding finds to an array, and get rid of the break that was terminating a search after finding one item. At the end of the search, join the array of finds into a string, and you're done.
// function called when submit button is clicked
function submit(e) {
var app = UiApp.getActiveApplication();
Logger.log(Utilities.jsonStringify(e)); // Log the input parameter (temporary)
// Write the data in the text boxes back to the Spreadsheet
var cell = e.parameter.userName;
var doc = SpreadsheetApp.openById('SPREADSHEET-ID');
var ss = doc.getSheets()[0];
var lastRow = doc.getLastRow();
var data = ss.getRange(2, 1, 2, 4).getValues();
var result = "User not found";
var result_array = [];
for (nn = 0; nn < data.length; ++nn) {
if (data[nn][1] == cell) {
result_array.push( data[nn][1] );
}; // if a match in column B is found, keep going, there may be more!
}
if (result_array.length > 0)
result = result_array.join(", "); // concatenate multiple results
// Make the status line visible and tell the user the possible actions
app.getElementById('status').setVisible(true).setText(result);
return app;
}