Multiple results from a single column search in a spreadsheet - google-apps-script

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;
}

Related

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

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
}

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

Absolute reference with UDF & filtering data in google sheets script

Hi I am begineer to scripts, here is the code and googlesheet for reference
What i am getting
what i want to achieve
/**
*#param quest1 Question of the note
*#param quest1 Answer of the note
*#customfunction*/
function DMNOTE(quest1,ans1,quest2,ans2,quest3,ans3,) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var result = quest1+'-'+ans1+','+quest2+'-'+ans2+','+quest3+'-'+ans3;
return result;
}
I want to achieve absolute reference for "quest" parameter and i want it to loop for rest of the coulmns till column where i enter the function.Also under "Formula Required" column i have put formla for reference thats how i want my UDF to work.
Follwing up i need to filter "Non-solicit agreement" and keep only "No" under it and Copy & Paste all colums highlited in blue to update tab.
function toFilter (){
// filter and retain "no" in non-solicit agreement
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
ss.getRange(1,1,ss.getLastRow(),ss.getLastColumn());
var createfilter = SpreadsheetApp.newFilterCriteria().setHiddenValues("Yes").build();
ss.getFilter().setColumnFilterCriteria(8, createfilter);
}
Hope i make sense. Any help is appriciated
To work with any number of arguments, you can have a function like below in your script
function DMNOTE(...arg){
let result = ''
for(let i=0;i<arg.length;i++){
result += `${arg[i]}-${arg[i+1]},`
i++;
}
return result.substring(0, result.length - 1);
}
And then form your spreadsheet you can call as =DMNOTE(A$1,A2,B$1,B2,C$1,C2) or =DMNOTE(A$1,A2,B$1,B2,C$1,C2,D$1,D2) The function will process all the arguments being passed and returns the result.
How to filter "non-solicit agreement" and take only data which has "no"
The code you provided comes already very close to what you desire, you just need to make the following adjustments:
setHiddenValues() expect an array, no a string, so change "Yes" to ["Yes"]
getFilter() only works to modify a filter that is present in the sheet already, in case there is none, it is better to delete the old filter and create a new one:
function toFilter(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Worksheet");
ss.getRange(1,1,ss.getLastRow(),ss.getLastColumn());
var createfilter = SpreadsheetApp.newFilterCriteria().setHiddenValues(["Yes"]).build();
if(ss.getFilter() != null) {
ss.getFilter().remove();
}
ss.getDataRange().createFilter().setColumnFilterCriteria(8, createfilter);
}
How to Copy & Paste all columns highlited in blue to update tab
Build a function that
calls toFilter()
checks for all rows either they are hidden
copies the non- hidden rows into an array
pastes this array to the sheet Update
function copyIfNotHidden(){
toFilter();
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = spreadsheet.getSheetByName("Worksheet");
var sheet2 = spreadsheet.getSheetByName("Update");
var data = sheet1.getDataRange().getValues();
var array = [];
for (var i = 0; i < data.length; i++){
if(sheet1.isRowHiddenByFilter(i+1)==false){
array.push(data[i]);
}
}
sheet2.getRange(sheet2.getLastRow()+1, 1, array.length, array[0].length).setValues(array);
}

Set Charts.newCategoryFilter() to be pre-filtered

I have a script which takes all of the data from a Google Sheet and displays it in a table. I've included several category filters in this so that the user is able to select which data they want to see.
However, I am now looking to take this even further my pre-filtering one of my columns so that it only displays results that are "Closed". I would then create a copy of this file and change it to only showing "Open" results.
Is this possible to do? See a simplified version of my script below. For reference, it's the outcomeFilter that I'm wanting to pre-filter.
function doGet() {
var ss = SpreadsheetApp.openById('spreadsheetkey');
var sheet = ss.getSheetByName('Sheet1')
var data = sheet.getDataRange().getValues();
var dataTable = Charts.newDataTable();
for( var j in data[0] )
if (data[23][j] != 'Closed') {
dataTable.addColumn(Charts.ColumnType.STRING, data[0][j]);
for( var i = 1; i < data.length; ++i )
dataTable.addRow(data[i].map(String));
var stageFilter = Charts.newCategoryFilter().setFilterColumnIndex(21).build();
var outcomeFilter = Charts.newCategoryFilter().setFilterColumnIndex(23).build();
//var testFilter = Charts.newCategoryFilter().setFilterColumnIndex(24).build();
//var testFilter2 = Charts.newCategoryFilter().setFilterColumnIndex(25).build();
var tableChart = Charts.newTableChart()
.setDimensions(1900, 2000)
.setDataViewDefinition(Charts.newDataViewDefinition().setColumns([0,1,2,3,4,5,6,7,8,9,10,11/*,12*/,14,15,16,17,18,19,20,21,22,23]))
.build()
;
var dashboard = Charts.newDashboardPanel().setDataTable(dataTable)
.bind([stageFilter, outcomeFilter/*, testFilter, testFilter2*/],[tableChart])
.build();
var app = UiApp.createApplication();
var filterPanel1 = app.createHorizontalPanel();
var filterPanel2 = app.createHorizontalPanel();
var filterPanel3 = app.createHorizontalPanel();
var filterPanel4 = app.createHorizontalPanel();
//var testPanel = app.createHorizontalPanel();
var chartPanel = app.createHorizontalPanel();
filterPanel4.add(stageFilter).add(outcomeFilter).setSpacing(10);
//testPanel.add(testFilter).add(testFilter2).setSpacing(10);
chartPanel.add(tableChart).setSpacing(10);
dashboard.add(app.createVerticalPanel().add(filterPanel1).add(filterPanel2).add(filterPanel3).add(filterPanel4)/*.add(testPanel)*/.add(chartPanel));
app.add(dashboard);
return app;
}
}
This is the absolute bare bones of my script to try and reduce down the size of the question.
I am also aware I've probably not gone about my code in the best way, but I'm still getting used to working with Apps Script.
EDIT - I've managed to make myself a workaround, where I create two sheets, one which is formulated to only show "Closed" entries and the other is formulated to show only "Open" entries. I have then created a quick apps script which sorts the shown records Z-A.
It works and is a good workaround, but it's annoying that I've got to update two files every time I want to make a mistake. - EDIT
Before you get the data, you could sort the data by the column that you want to exclude values from.
Google Documentation Sort a Sheet Range
You already have a For loop iterating through every row of your data, so you could add a condition to stop the loop when it gets to a value that you don't want.
for (var j in data[0] ) {
if (data[columnYouWantToCheck][j] === valueToExclude) {
break; //End this iteration here if row is unwanted. If you sorted the data.
};
};
The break; will kill the loop, so only use that if you presorted the data. You could also set up the condition like this:
for (var j in data[0] ) {
if (data[columnYouWantToCheck][j] != valueToExclude) {
//All your code here for data in include.
};
};
You can create a filter that will by default have the value "Closed" and be impossible to change to another value using
var testFilter = Charts.newCategoryFilter().setAllowNone(false).setValues(['Closed']);

Lookup cell reference of given value

I have a spreadsheet with hundreds of rows and Column A is a unique record number.
I would like to learn to be able to find cell reference based on the inputted unique record number.
For example if in Call A1, I enter value 11, ho can I retrieve cell row (eg: 17) using google script.
Thank you for help
Taizoon
You can build a simple and small Ui that does a "search" in your first column and activates the found cell (and the whole row). An example is shown below, You have an option to use it repeatedly (like it is now) or just once by changing one line of code, see comment at the end of the handler function. :
function showInSheet() {
var app = UiApp.createApplication().setTitle('Search and Select').setWidth(250).setHeight(60);
var panel = app.createVerticalPanel();
var searchHandler = app.createServerHandler('searchRow').addCallbackElement(panel);
var Hpanel = app.createHorizontalPanel();
var search = app.createTextBox().setName('search').setId('search').setWidth('70').addKeyUpHandler(searchHandler);
var warning = app.createHTML('incomplete or invalid entry').setId('warning').setStyleAttributes({'padding-left':'10px','background':'yellow','fontSize':'8pt'}).setVisible(false);
app.add(panel.add(Hpanel.add(search).add(warning)));
SpreadsheetApp.getActive().show(app);
}
function searchRow(e){
var app = UiApp.getActiveApplication();
var item = e.parameter.search;
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getRange(1,1,sh.getLastRow(),1).getValues().join().split(',');// the 2D array is "flattened" and splitted to get a 1D array
Logger.log(data.length);
var idx = data.indexOf(item);// in a "simple" array we can use this array function more easily to seach in it ;-)
Logger.log(idx);
if(idx>data.length || idx==-1){app.getElementById('warning').setVisible(true) ; return app};
idx++;// increment because rows count from 1 instead of 0 in arrays
var itemRange = sh.getRange(idx,1,1,sh.getMaxColumns());
sh.setActiveSelection(itemRange);
// return app.close();// can be replace by next line (uncommented)
app.getElementById('search').setText('');app.getElementById('warning').setVisible(false);return app;
}