Automatically Find&Replace - google-apps-script

I work with surveymonkey and when someone doesn't answer my question, my cell is filled with "Not Answered".
The goal is to merge multiple rows into one.
Possible solutions are:
I need a script to automatically find and replace. Which will replace (more precisely delete "Not Answered") with an empty cell. So my TEXTJOIN will work. I've tried some code but it doesn't work for the whole sheet.
The problem with the TEXTJOIN function (delimiter, ignore_empty, text1, [text2, ...]) is that the second parameter igrone_empty is boolean and only works with TRUE or FALSE and cannot write "Not Answered" there. So with this I have to find another solution to ignore or delete the word "Not Answered".
Alternative to TEXTJOIN where the word "Not Answered" is ignored.
Or if it is possible to delete this option in SurveyMonkey?
I have tried this solution but doesn't work for entire sheet/table.
var result = ui.alert(
"Will update " + to_replace + " to " + replace_with + " ",
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
spread.toast("Will update " + to_replace + " to " + replace_with + " ", "ALERT");
var data = range.getValues();
var oldValue="";
var newValue="";
var cellsChanged = 0;
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
oldValue = data[row][item];
newValue = data[row][item].replace(to_replace, replace_with);
if (oldValue!=newValue)
{
cellsChanged++;
data[row][item] = newValue;
}
}
}
range.setValues(data);
spread.toast(cellsChanged + " cells changed", "STATUS");
}
else {
// User clicked "No" or X in the title bar.
spread.toast("No action taken", "ABANDONED");
}
}

Try this:
function replaceNotAnswerWithBlank(e) {
var ss=e.source;
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var tf=sh.createTextFinder('Not Answered').findAll();
for(var i=0;i<tf.length;i++) {
sh.getRange(tf[i].getRow(),tf[i].getColumn()).setValue('');
}
}
How about this:
function onOpen(e) {
replaceNotAnswerWithBlank(e);
}

I needed a trigger so this is how I solved the problem. If there is a simpler functional solution write it down. Thank you Cooper.
function onOpen() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var tf=sh.createTextFinder('Not Answered').findAll();
replaceNotAnswerWithBlank(ss, sh, rg, tf);
}
function replaceNotAnswerWithBlank(ss, sh, rg, tf) {
for(var i=0;i<tf.length;i++) {
sh.getRange(tf[i].getRow(),tf[i].getColumn()).setValue('');
}
}

Related

searching and replacing does not record in google sheets

How I record an action for this function?
Basically I have to do this and others actions everyday and always is the same.
searching and replacing does not record in google sheets
That is right, the find and replace function is not accessible via Google App Scripts and is not therefore able to be recorded. You need to create your own code to accomplish a behavior similar to the behavior you want manually.
Here is one of many example ways of doing it: https://webapps.stackexchange.com/questions/104157/auto-find-and-replace-in-google-sheets-with-scripts
Thanks i used that code and i edited a litle bit.
function Herramienta() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A1:A69').activate();
spreadsheet.getRange('A1:A69').splitTextToColumns();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("a2:a69");
var to_replace = "HH-";
var replace_with = "";
replaceInSheet(sheet,range, to_replace, replace_with);
function replaceInSheet(sheet, range, to_replace, replace_with) {
//Confirm
var ui = SpreadsheetApp.getUi();
var spread = SpreadsheetApp.getActiveSpreadsheet();
var result = ui.alert(
"Will update " + to_replace + " to " + replace_with + " ",
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
spread.toast("Will update " + to_replace + " to " + replace_with + " ", "ALERT");
var data = range.getValues();
var oldValue="";
var newValue="";
var cellsChanged = 0;
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
oldValue = data[row][item];
newValue = data[row][item].replace(to_replace, replace_with);
if (oldValue!=newValue)
{
cellsChanged++;
data[row][item] = newValue;
}
}
}
range.setValues(data);
spread.toast(cellsChanged + " cells changed", "STATUS");
}
else {
// User clicked "No" or X in the title bar.
spread.toast("No action taken", "ABANDONED");
}
}
}

I want to write a script to read and log values of a specific row and copy them to a new sheet

I want to build a script to automate creating a form out of specific values.
What I need to do is:
Ask the user which row is relevant for the form, enter the row in a text box
Log value of column A of that row (Date)
Check that row from column C-ZZZ for values (numbers)
if there is a cell with a value, log the value. If the cell is empty ignore it
If there is a value in a cell additionally log values of row 1-10 of that column (numbers + strings)
create a new sheet
copy logged values in a specific order into that new sheet (other order than in the first sheet)
I searched for scripts that offer any resemblance of what I want to do, but I only managed to copy values of a specific row and output them into a new sheet without any formatting
Ask user for a row (NaN error message doesn't work):
function tourzettelZeile() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var rowIdxStr = Browser.inputBox("For which row do you want to make the list?");
if (rowIdxStr == 'cancel') {
return;
}
var rowIdx = parseInt(rowIdxStr);
if (rowIdx == NaN) {
Browser.msgBox("Numbers only or 'cancel'");
return;
}
}
My attempt at logging and pasting data:
function readData(spreadsheetID)
{
var spreadsheetId = '1KdZNvKgwL6NMuF0FYGB8jBhRVpbwv954D_UcBZ22eh0';
var plakatMenge = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!DK15:DK900');
var plakatFormat = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!DK4');
var plakatName = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!DK15:DK1');
var plakatiererName = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!FK66');
var plakatInfo = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!DK7');
var plakatGebiet = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!FL66');
var auftragDatum = Sheets.Spreadsheets.Values.get(spreadsheetId, 'PlakatTool2019!A66');
Logger.log(plakatMenge.values);
Logger.log(plakatFormat.values);
Logger.log(plakatName.values);
Logger.log(plakatiererName.values);
Logger.log(plakatInfo.values);
Logger.log(plakatGebiet.values);
Logger.log(auftragDatum.values);
Browser.msgBox(plakatMenge + plakatFormat + plakatName + plakatiererName + plakatInfo + plakatGebiet + auftragDatum);
//Neues Sheet erstellen
var requests = [{
'addSheet': {
'properties': {
'title': 'Tourzettel',
'gridProperties': {
'rowCount': 80,
'columnCount': 14
},
'tabColor': {
'red': 1.0,
'green': 0.3,
'blue': 0.4
}
}
}
}];
var response =
Sheets.Spreadsheets.batchUpdate({'requests': requests}, spreadsheetId);
Logger.log('Created sheet with ID: ' +
response.replies[0].addSheet.properties.sheetId);
//writeValues
// Specify some values to write to the sheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
// ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[5];
var values = [
[plakatMenge.values, "Gebiet", plakatFormat.values, plakatName.values, plakatiererName.values, plakatInfo.values, plakatGebiet.values, auftragDatum.values]
];
var range = sheet.getRange("A1:H1");
range.setValues(values);
}
Both these scripts don't do the right thing and are not combined (info on which row to check does nothing)
This function attempts to do the first five items in your list:
Feel free to use and debug it because I have not debugged it at all.
function readAndLog() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var row=SpreadsheetApp.getUi().prompt('Enter Row Number').getResponseText();
if(typeof(row)!=Number) {
throw("Invalid Response: terminating script");
return;
}else{
Logger.log(sh.getRange(row,1).getValue());
var vA=sh.getRange(row,3,1,sh.getLastColumn()-3).getValues();
var rA=[];
for(var i=0;i<vA.length;i++) {
if(vA[0][1] && typeOf(vA[0][i])==Number) {
rA.push({col:i+1,val:vA[0][i]});
Logger.log('column: %s, value: %s',rA[i].col,rA[i].val);
var cA=sh.getRange(1,rA[i].col,10,1).getValues();
for(var j=0;j<cA.length;j++) {
Logger.log('col: %s, row: %s value: %s',rA[i].col, j+1, cA[0][j]);
}
}
}
}
}
Thanks, that helped a lot. I had to get rid of this part:
if(typeof(row)!=Number) {
throw("Invalid Response: terminating script");
return;
}else{
As it gave me an error, I wasn't able to solve.
I added a bit to the code:
function Tourzettel(){
readAndLog();
newSheet();
logOutput();
}
function readAndLog() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var row=SpreadsheetApp.getUi().prompt('Gib die Zeile mit dem Datum für die Tour ein').getResponseText();
Logger.log(sh.getRange(row,1).getValue());
var vA=sh.getRange(row,3,1,sh.getLastColumn()-3).getValues();
var rA=[];
for(var i=0;i<vA.length;i++) {
if(vA[0][1] && typeOf(vA[0][i])==Number) {
rA.push({col:i+1,val:vA[0][i]});
Logger.log('column: %s, value: %s',rA[i].col,rA[i].val);
var cA=sh.getRange(1,rA[i].col,10,1).getValues();
for(var j=0;j<cA.length;j++) {
Logger.log('col: %s, row: %s value: %s',rA[i].col, j+1, cA[0][j]);
}
}
}
}
// Create new sheet
function newSheet() {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName("TourzettelV2");
if (yourNewSheet != null) {
activeSpreadsheet.deleteSheet(yourNewSheet);
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName("TourzettelV2");
}
// Log in das neue Sheet schreiben
function logOutput() {
var stringToWrite = Logger.log;
SpreadsheetApp.getActive().getSheetByName('TourzettelV2').getRange("A1").setValue(stringToWrite);
}
My problem now is the function logOutput, because Logger.log doesn't seem to work. It outputs:
function log() {
[native code, arity=0]
}
Not sure what exactly it logged there, but it doesn't look like cell values. I do want every single logged value to be put into a seperate cell.
To fix the NaN error try this:
if (isNan(rowIdx)) {
Browser.msgBox("Numbers only or 'cancel'");
return;
}
The other part is a little hard to follow. If you want to pass the row index number to the second function, you could try adding this line to the first function:
readData(rowIdx);
And the second function would then be
function readData(rowIdx) {
...
}
And it can then use the rowIdx variable in the script. I'm assuming the 2nd function does not need the spreadsheetID passed to it as it is provided in the first line of the function. (Or is that there because you were testing?)

How to read Google Form Data Using App Script

I'm new to App Script. I'm trying to read the submitted data using App Script. I tried to use the answer provided here, but I'm not able to read it.
I tried many other method, they are also not working.
function myFunction() {
var form = FormApp.openById('1_HABIBYRZ-zsEMuzMCnsrAOYFlQSKwaIhmeT1y2SY6Y');
var formResponses = form.getResponses();
MailApp.sendEmail ("test#appirio.com", "Form Submited: Foo feedback " + Date.now(), +formResponses);
}
I also want to know, how to debug any issue in App Script.
Thanks in Advance!
The other answer above may work, but it's a bit complicated. To let you keep the form responses as a variable inside your program, which would allow you to email it, you can do this:
function formResponsesToArray() {
var app = FormApp.openById(...),
responses = app.getResponses(), stringResponses = []
responses.forEach(function(r) {
var response = []
r.getItemResponses().forEach(function(i) {
response.push(i.getResponse())
})
stringResponses.push(response)
})
Logger.log(stringResponses)
}
Which will log an array of responses like this:
The way I'd recommend doing it is feed your form responses into a google sheet, and then export is as a csv via attachment in an email. like so below. It is important to make sure the correct API's are active, such as Drive API, Gmail API and that your oauthScopes are added to the manifest if need be.
editing my answer to recommend stewarts answer. much cleaner, simpler and way more efficient.
function ExportToCSVCrntMonth(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1'); //change this to sheet name
// Change the below line to get the folder where you want the CSV files stored.
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
fileName = ss.getName() + ".csv";
var csvFile = convertRangeToCsvFileCrnt_(fileName, ss);
folder.createFile(fileName, csvFile);
var csvmail = folder.getFilesByName('your sheet name + .csv');
MailApp.sendEmail({
to: "your email ere",
subject: "CSV attached",
body: "body text",
attachments: [csvmail.next()]
});
}
function convertRangeToCsvFileCrnt_(csvFileName, ss) {
var activeRange = ss.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}

Auto find and replace Google Sheet every time I run the script

I find this code but it doesn't work like expected.
function onOpen() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("'Feuille 1'!1:1137");//This line is most likely the culprit
var to_replace = ".";
var replace_with = ",";
replaceInSheet(sheet,range, to_replace, replace_with);
}
function replaceInSheet(sheet, range, to_replace, replace_with) {
//Confirm
var ui = SpreadsheetApp.getUi();
var spread = SpreadsheetApp.getActiveSpreadsheet();
var result = ui.alert(
"Will update " + to_replace + " to " + replace_with + " ",
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
spread.toast("Will update " + to_replace + " to " + replace_with + " ", "ALERT");
var data = range.getValues();//I think error is occurring here
var oldValue="";
var newValue="";
var cellsChanged = 0;
for (var row=0; row<data.length; row++) {
for (var item=0; item<data[row].length; item++) {
oldValue = data[row][item];
newValue = data[row][item].replace(to_replace, replace_with);
if (oldValue!=newValue)
{
cellsChanged++;
data[row][item] = newValue;
}
}
}
range.setValues(data);
spread.toast(cellsChanged + " cells changed", "STATUS");
}
else {
// User clicked "No" or X in the title bar.
spread.toast("No action taken", "ABANDONED");
}
}
I got this error :
TypeError: Impossible d'appeler la méthode "getValues" de undefined. (ligne 25, fichier "Code")
Could you help me I'm a noob
Regards
So apparently this line has a problem:
var range = sheet.getRange("'Feuille 1'!1:1137");
The range is the problem it should be something like 'Sheet1!A1:A1137'.
So may be this will work: var range = sheet.getRange("'Feuille 1'!A1:A1137");
Since I can't see your sheet you will have to determine the appropriate range.

Script for Google sheet to change a cells value based on another cells value (in a column range)

Here is the scenario:
Column E in my Googlesheet has a dropdown list of Yes and No. Everytime the user answers No, I want the corresponding cell in Column G to have the words "Not Applicable". But if user answers Yes, I want that cell in G to have another dropdown list of Yes and No.
I tried to build on the script that I got from this thread:
Google Sheets formula to change a cells value based on another cells value
It's almost perfect, but I can't make it to work for a range (an entire column, preferrably). Any advice would be appreciated :)
Since I wasn't getting any success trying to tweak it for my own use, here's a copy code from the mentioned thread:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet()
var nota=e.range.getA1Notation()
if(nota=="E10"){
var val=e.range.getValue()
if(val=="Levy"){
s.getRange("E11").setDataValidation(null)
s.getRange("E11").setValue("Monthly")
}
else{
s.getRange('E11').clearContent()
var cell = s.getRange('E11');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['Triannual', 'Quarterly']).build();
cell.setDataValidation(rule);
}}}
And here's my dumb attempt:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet()
var nota=e.range.getA1Notation()
if(nota=="E2:E500"){
var val=e.range.getValue()
if(val=="No"){
s.getRange("G2:G500").setDataValidation(null)
s.getRange("G2:G500").setValue("Not Applicable")
}
else{
s.getRange('G2:G500').clearContent()
var cell = s.getRange('G2:G500');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['Yes','No']).build();
cell.setDataValidation(rule);
}}}
You need to loop a range of cells, which is why the original script isn't working. .getValue() returns a single cell, you can't have a range (as in your edited script).
This script will look at the entire page and loop it each time. This is preferable because you don't have to keep data in order. In other words, you can jump around Column E and mark things "Yes" or "No" as they come up. Blank cells are ignored.
function addValidation() {
// Get the spreadsheet and active sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Get the data as an Array you can iterate and manipulate
var data = sheet.getDataRange().getValues();
// Store a rule to use for the Data Validation to be added if ColE == "Yes"
var rule = SpreadsheetApp.newDataValidation().requireValueInList(["Yes", "No"]).build();
// Loop the sheet
for(var i=0; i<data.length; i++) {
// Test ColE. Note that the array is 0-indexed, so A=0, B=2, etc...
// To change which columns you're testing, change the second value.
if(data[i][4] == "Yes") {
// If it's "Yes," add the Data Validation rule to Col G for that row.
// Note that .getRange() is _not_ 0 indexed, which is why you need `i+1` to get the correct row
sheet.getRange(i+1, 7).clear().setDataValidation(rule);
// If ColE == "No," mark ColG as "Not Applicable"
} else if(data[i][4] == "No") {
sheet.getRange(i+1, 7).clearDataValidations().setValue("Not Applicable");
}
}
}
Also note that this will change values as you change Col E. So, if you change a "Yes" to a "No," Col G will be changed to "Not Applicable."
for (var i = 0; i < 5000; i++) {
function checkData() {
if(SpreadsheetApp.getActiveSheet().getRange("E" + i).getValue() == "No"){
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Not Applicable');
}
else{
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Applicable');
}
}
}
Hope this works for you:)
Btw getting the range of 5000 rows will be VERY slow! Here is another way you can do it faster!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tsSheet = ss.getSheetByName("YOUR SHEET NAME AT THE BOTTOM OF THE PAGE");
var tsRows = parseInt(tsSheet.getLastRow());
for (var i = 0; i < tsRows; i++) {
function checkData() {
if(SpreadsheetApp.getActiveSheet().getRange("E" + i).getValue() == "No"){
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Not Applicable');
}
else{
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Applicable');
}
}
}
EDIT:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tsSheet = ss.getSheetByName("YOUR SHEET NAME AT THE BOTTOM OF THE PAGE");
var tsRows = parseInt(tsSheet.getLastRow());
for (var i = 0; i < tsRows + 1; i++) {
function checkData() {
if(SpreadsheetApp.getActiveSheet().getRange("E" + i).getValue() == "No"){
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Not Applicable');
}
else{
SpreadsheetApp.getActiveSheet().getRange("G" + i).setValue('Applicable');
}
}
}