Map checked cells from one sheet to others to summarize data - google-apps-script

I have a sheet that has customer information and another sheet where the customer information needs to go if it meets certain conditions. For example sheet one I have a customer with a unique identifier, and on sheet 2 I have several columns that could be checked off. If these columns are checked off I need those column headers to be populated into one cell in sheet 1. So
Sheet 1:
John Doe 019384388 Eats cookies
Likes Movies
Needs Help from Stack exchange
Sheet 2:
Name uniques identifier Eats cookies Likes movies Is tired Needs help
John Doe 019384388 X X X
This is just something I'm playing with at work to simplify some spreadsheets. I have worked with C, C++, and Java so I do understand condition statements and loops, but I haven't really worked with Google Apps Script / JavaScript. I am having a hard time knowing how to call to the individual cells in the spreadsheets to make condition statements, e.g.
if (box == "x" && unique id == "xxxxxx")
cell ("Ai"(sheet1) = "Ai"(sheet2))
type things. Perhaps iterating through all the cells using a for loop that changes i until all cells are checked? Any and all help is appreciated.

You could create a custom Google Apps function, and add an onedit trigger to it.:
function autoCheck(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1");
var row = e.range.getRow();
var col = e.range.getColumn();
var sheet2 = ss.getSheetByName("Sheet2");
if (e.range.getSheet().getName()=="Sheet2" && col >=3 && col<=5) {
var str = "";
for (i=3; i<=6; i++) {
if (sheet2.getRange(row, i).getValue()=="X") {
if (str.length==0) {
str = sheet2.getRange(1, i).getValue();
} else {
str =str + "\n" + sheet2.getRange(1, i).getValue();
}
}
}
sheet1.getRange(row, 3).setValue(str);
}
}

Add a row that concatenates the headers if the box is checked:
=if(B1 = 'x', $A$1, '') & if(B2 = 'x', $A$2, '') & ...
then vlookup() on the ID and return the concatenated field.
Add comma delimiters to the if statements if you want them, and hide the concatenated field if you don't want to see it in the table.

Related

Add Notes Based on Criteria

I have a google sheet that I would like to set up a script for, which looks for specific criteria such as if a cell in a range contains something that is also in another column, or contains something specific. My example sheet shows what I would like to happen: Example sheet &
screenshot
Anything in the name column (A) that contains the city name (B) as part of it would get a note ("Are you sure this is right?"). Ie if Row 1 has "Las Vegas Smith" as the name and "Las Vegas" as the city, it would get the note. If Row 5 has "Charlotte Applegate" as the name and "Applegate" as the city, it would also get the note.
I also need to add a note to other cells in particular columns that meet criteria, but they are only looking at one column. Ie anything that contains a hyphen in Column A would get a note like "Hyphen in the name?"
I have been able to highlight the name/city items using conditional formatting, using a custom formula =REGEXMATCH (LOWER($A2),LOWER($B2)). And just using 'text contains' works for the rest for conditional formatting. But I'd like to also add a note in addition to making them a different color.
I have found something to add notes,
function addNote() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Testing');
var targetCell = sheet.getRange("A2:A5");
var sourceCell = sheet.getRange("E2:E3");
var noteText = sourceCell.getValue();
targetCell.setNote(noteText);
}
(pointing to the second tab, Testing, which is just a copy of the first w/formatting removed.) This results in the text from E2 as a note to every cell in the A2:A5 range. I'm unsure how to add in that it needs to look for criteria and add the appropriate note based on that, and additionally that it needs to look for cells that contain that, not that match it exactly.
The questions linked in the comments didn't address my needs, but I did get help elsewhere creating code that would work as intended. Additionally, it ignores any blanks rows.
function addNoteToCells() {
var sheet = SpreadsheetApp.getActiveSheet();
var numRows = sheet.getLastRow();
var columnARange = sheet.getRange("A2:A" + numRows);
var columnBRange = sheet.getRange("B2:B" + numRows);
var columnAValues = columnARange.getValues();
var columnBValues = columnBRange.getValues();
for (var i = 0; i < columnAValues.length; i++) {
if (columnAValues[i][0].length > 0 && columnBValues[i][0].length > 0) {
if (columnAValues[i][0].indexOf(columnBValues[i][0]) != -1) {
var cell = columnARange.getCell(i + 1, 1);
cell.setNote("Are you sure this is right?");
}
}
}
}

How to select cells with the same value in a column and change them to a different value?

I need some help with the Google Sheets Apps Script to do the following:
select column T
apply filter and select only values "In review" in that column
change all values "In review" to "Done"
select and copy rows with "Done" value from columns A-L and N-R (i.e. skip column M).
I have tried recording a macro (with relative and absolute values) but it only works for the specific rows that were selected for the macro. It does not apply to the entire sheet.
function myFunction() {
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
const lastRow = spreadSheet.getLastRow();
var arrayDoneRowNumber = []
for (let i = lastRow; i > 1 ; i--) {
var value = spreadSheet.getRange('T'+i).getValue();
if (value == 'InReview') {
spreadSheet.getRange("T"+i).setValue("Done");
arrayDoneRowNumber.push(i)
} else if (value == 'Done') {
arrayDoneRowNumber.push(i)
}
}
console.log(arrayDoneRowNumber)
}
This code snippet is responsible for the 1st three tasks you have mentioned. I will update the code when you have responded to my question for the 4th task.
Note :- Anyhow the array arrayDoneRowNumber has the row numbers of all the rows which have Done in column T. You can simply process further using this information of row numbers itself.

Overwriting Google sheets (for form response) rows if duplicate entered

So, I've been trying to figure out how to stop the duplicate rows appearing in my google sheets response output from a google form. If found this link which sounds like it does exactly what I want (Form Google Script Prevent Duplicates), but cannot for the life of me work out how to edit the given answer to work on my sheet.
I have included a screenshot of my workbook to give an example of the structure of the data I'd like the edited code to run on, and also below is my attempt at making the code run correctly on my data structure.
My sheet structure that I'd like to run the code on. I want to use the email address as the 'unique' identifier, so any duplicate rows can be identified using that.
My attempt at adapting the code to work on the above data structure (I have absolutely no background with this scripting language, so please go easy on me if I've made a glaringly obvious error):
function updateExisting() {
var s = SpreadsheetApp.getActiveSheet(),
// s = ss.getSheetByName(''),
lastRow = s.getLastRow(),
lastValues = s.getRange('A'+lastRow+':C'+lastRow).getValues(),
name = lastValues[0][0],
allNames = s.getRange('B2:B').getValues(),
row, len;
// TRY AND FIND EXISTING NAME
for (row = 0, len = allNames.length; row < len - 1; row++)
if (allNames[row][0] == name) {
// OVERWRITE OLD DATA
s.getRange('A2').offset(0, 0, row,
lastValues.length).setValues([lastValues]);
// DELETE THE LAST ROW
s.deleteRow(lastRow);
break;}
}
Key words: duplicates, Google, spreadsheet, Sheets, Form, submission, edit, row, unique.
This code prevents duplicates in a Google Sheet when submitting a Google Form, by overwriting an existing row with the existing unique value, if one exists.
The code searches one column in a spreadsheet and looks for a match. I tried to make it generic so that the code doesn't need to be changed depending upon what column the unique identifier is in. You need to make a couple of settings in the "User Settings" section to make it work. But that is better than needing to rewrite the code.
function updateExisting(columnWithUniqueIdentifier,sheetTabName) {
var dataFromColumnToMatch,lastColumn,lastRow,rowWithExistingUniqueValue,rowOfDataJustSaved,
sh,ss,valueToSearchFor;
// USER SETTINGS - if the values where not passed in to the function
if (!columnWithUniqueIdentifier) {//If you are not passing in the column number
columnWithUniqueIdentifier = 2;//Hard code column number if you want
}
if (!sheetTabName) {//The sheet tab name was not passed in to the function
sheetTabName = "Put your Sheet tab name here";//Hard code if needed
}
//end of user settings
ss = SpreadsheetApp.getActiveSpreadsheet();//Get the active spreadsheet - this code must be in a project bound to spreadsheet
sh = ss.getSheetByName(sheetTabName);
lastRow = sh.getLastRow();
lastColumn = sh.getLastColumn();
//Logger.log('lastRow: ' + lastRow)
rowOfDataJustSaved = sh.getRange(lastRow, 1, 1, lastColumn).getValues();//Get the values that were just saved
valueToSearchFor = rowOfDataJustSaved[0][columnWithUniqueIdentifier-1];
//Logger.log('valueToSearchFor: ' + valueToSearchFor)
dataFromColumnToMatch = sh.getRange(1, columnWithUniqueIdentifier, lastRow-1, 1).getValues();
dataFromColumnToMatch = dataFromColumnToMatch.toString().split(",");
//Logger.log('dataFromColumnToMatch: ' + dataFromColumnToMatch)
rowWithExistingUniqueValue = dataFromColumnToMatch.indexOf(valueToSearchFor);
//Logger.log('rowWithExistingUniqueValue: ' + rowWithExistingUniqueValue)
if (rowWithExistingUniqueValue === -1) {//There is no existing data with the unique identifier
return;
}
sh.getRange(rowWithExistingUniqueValue + 1, 1, 1, rowOfDataJustSaved[0].length).setValues(rowOfDataJustSaved);
sh.deleteRow(lastRow);//delete the row that was at then end
}

Copying Data Sheet1 to Sheet2 so you can sort & edit both sheets (google apps script?)

I am working in goggle sheets and think I need to use a google apps script to do what I want, but I am a psychologist at a non-profit University hospital trying to do some good and not a programmer (which probably shows) and I am desperately in need of help. I am trying to set up a series of spreadsheets to track participation in workshops for our treatment method.
1) I have a sheet “Participant_Registration” where basic information is entered
2) I want to transfer information from only the first four columns (A:D) of “Participant_Registration” to a second sheet “Learning_Sessions_Attendance”
3) I am also transferring the same information to a third sheet 'Consultation1_Attendance' – but I need to first filter and select only those people assigned to that group.
Here is a link to a copy of my spreadsheet.
https://docs.google.com/spreadsheets/d/17d0bT4LZOx5cyjSUHPRFgEZTz4y1yEL_tO3gtSJ4UJ8/edit?usp=sharing
More generically this is what I am trying to do. Is this possible in google app scripts? It seems it should be.
1) I have original data in sheet1
2) I want the first four columns (A:D) to transfer to sheet2 (it is fine if I need a trigger variable)
3) I want them to transfer in such a way that if you sort either sheet, the data are still fine (still linked to the right line).
4) Ideally if there is a change to the data in the source sheet (Sheet1) the same change will be made in Sheet2.
5) Ideally this would all happen automatically without human intervention through a script.
Any ideas?? I so need your help. I have been all over the forum, git hub, and done a ton of searches and tried following a lot of examples I saw but nothing works. I really need help.
Here are my sample scripts each with a problem:
//The following code copies a range from sheet1 to sheet2 as I wanted. A problem occurs if after if we copy the data from sheet1 we add data to other columns on sheet2. Later if we sort on some variable (which people are bound to do) if the function is deployed again it will overwrite data meaning the data from sheet1 are not connected to the right individual on sheet2
function CopyRange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Participant_Registration");
var range = sheet.getRange("A14:D");
var values = range.getValues();
var target = ss.getSheetByName("Learning_Sessions_Attendance");
var target_range = target.getRange("A10:D");
range.copyTo(target_range);
}
So I tried again. This time I tried to just copy the last edited row from sheet1 to sheet2. This function does not appear to work for me.
function CopyRow2() {
// Get Spreadsheets
var source = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
var target = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
// Set Sheets
var source_sheet = source.getSheetByName("Participant_Registration");
var target_sheet = target.getSheetByName("Learning_Sessions_Attendance");
var rowIdx = source_sheet.getActiveRange().getRowIndex();
var rowValues = source_sheet.getRange(rowIdx,1,1,source_sheet.getLastRow()).getValues();
Logger.log(rowValues);
var destValues = [];
destValues.push(rowValues[0][0]);// copy data from col A to col A
destValues.push(rowValues[0][1]);//copy data from col B to col B
destValues.push(rowValues[0][2]);//copy data from col C to col C
destValues.push(rowValues[0][3]);//copy data from col D to col D
var dest=source.getSheets()[4];
dest.getRange(dest.getLastRow()+1,1,1,destValues.length).setValues([destValues]);//update destination sheet with selected values in the right order, the brackets are there to build the 2D array needed to write to a range
}
So I tried again and again. I have lots of examples but none seem to work.
Thanks so much.
Chandra
For that to happen automatically (one sheet's change updating another sheet), you will surely need an "event/trigger" to run a script whenever you change a cell. (that is the "onEdit()" function).
But since scripts are likely to fail sometimes (even when they are perfect, that's because of some Google issues), it's not guaranteed that the sheets will always contain the same data.
But, if I could suggest another way, do not let ID be optional. If that is a real ID (like the person ID card number), create another ID exclusively for working with the sheet.
I have edited your second sheet showing a suggestion of how to do it without using scripts. The only things you must be aware of are:
Do not create two people with the same ID.
You have to insert (only) the ID manually in the second sheet.
The VLOOKUP forumla will search for that ID in the first sheet and return the data in the same line. You can sort any sheet in whatever way you like. As long as you don't change people's IDs.
So, in sheet 2, use this in the First Name, Last Name and Email address:
=vlookup(A10,Participant_Registration!$A:$D,2,false)
=vlookup(A10,Participant_Registration!$A:$D,3,false)
=vlookup(A10,Participant_Registration!$A:$D,4,false)
Just extend this formula downwards
I hope this helps. I would avoid scripting for that at any cost. It would be my last resort. (Scripts also need to be changed if you want to rearrange your sheet, and if not, they might cause trouble, write over existing data...)
I also added a button (insert - drawing) and put a script in it (right button, click down arrow, "transfer? script" -- translated from Portuguese).
If you lock all four columns in sheet2 and lock the ID column in sheet 1, people will not be able to chang IDs and cause mess. They can edit people in sheet 1 and not change the formula in sheet2. Script is not affected by sorting or empty spaces (it adds the person in the first empty row it finds).
I added "named ranges" for the four column headers. (With named ranges, the script can refer to names instead of coordinates, which enables you to rearrange the sheet inserting and deleting columns, or moving them with CUT and paste - but the VLOOKUP formula will need manual update if you rearrange columns).
Here is the code: (it could get better if you manage to create dialog boxes and ask for the person's data inside that dialog, then you could lock everything - and you would need an edit button besides the add).
function AddPerson()
{
var S1Name = "Participant_Registration";
var S2Name = "Learning_Sessions_Attendance";
var ID1Name = "regID";
var ID2Name = "learnID";
//these vars are not used in this script
var FN1Name = "regFirstName";
var FN2Name = "learnFirstName";
var LN1Name = "regLastName";
var LN2Name = "learnLastName";
var Email1Name = "regEmail";
var Email2Name = "learnEmail";
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var Sheet1 = sSheet.getSheetByName(S1Name);
var Sheet2 = sSheet.getSheetByName(S2Name);
var ID1 = getRangeByName(sSheet, Sheet1.getName(), ID1Name);
var ID2 = getRangeByName(sSheet, Sheet2.getName(), ID2Name); Logger.log("ID2: " + ID2.getValue());
var Empty1 = getFirstEmpty(ID1);
var Empty2 = getFirstEmpty(ID2);
var Biggest1 = getBiggestID(ID1); Logger.log("Biggest 1: " + Biggest1);
var Biggest2 = getBiggestID(ID2); Logger.log("Biggest 2: " + Biggest2);
if (Biggest1 !== Biggest2)
Browser.msgBox("Warning: there are IDs in one sheet that are not in the other sheet");
var Biggest;
if (Biggest1 > Biggest2) Biggest = Biggest1;
else Biggest = Biggest2;
Biggest++;
Empty1.setValue(Biggest);
Empty2.setValue(Biggest);
}
function getFirstEmpty(Header)
{
while (Header.getValue() !== "")
{
Header = Header.offset(1,0);
}
return Header;
}
function getBiggestID(Header)
{
var Sheet = Header.getSheet();
var LastRow = Sheet.getLastRow();
var Values = Sheet.getRange(Header.getRow(), Header.getColumn(), LastRow - Header.getRow() + 1).getValues();
var len = Values.length;
var MaxID = 1;
for (var i = 0; i < len; i++)
{
var val = Number(Values[i]);
if (!isNaN(val) && val > MaxID)
MaxID = val;
}
return MaxID;
}
function getRangeByName(spreadSheet, sheetName, rangeName)
{
Logger.log("Trying range: " + "'" + sheetName + "'!" + rangeName);
return spreadSheet.getRangeByName("'" + sheetName + "'!" + rangeName);
}

Incorrect Range Height - Google Script

I'm trying to create a Google sheet script that will take a list of names and resend them to the Google Sheet. I have two columns of data, the first column contains a persons name. The second column contains multiple cells that the person in the first cell is inviting to a party. This creates a problem, the name in column 1 might be on row 2, but if they invite 20 people then column one has blank spaces rows 3-21. It may sound pointless right now to most of you, but I want to be able to sort the sheet alphabetically by the name of the person who did the inviting, AND be able to sort it into a separate sheet alphabetically by the name of the guest in Column 2, while still keeping the person who invited them tracked as well. This is the only way I could think of accomplishing the task.
I'm currently stuck on writing the array back to the sheet, I keep getting "Incorrect range height, was 1 but should be 339." I've figured out how to successfully get an array of data, filled exactly how I wanted it, but can't seem to get this part. I've searched through here and tried to implement the solutions I find, but have had no luck.
This is what I have come up with so far, and it works up until the setValues(
function inviteSorter() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var current = sheet.getSheets()[0];
var lastRow = current.getLastRow();
var rangeData = current.getRange(2,1,lastRow-1,3);
var numColumns = rangeData.getNumColumns();
// Logger.log(rangeData);
var info = rangeData.getValues();
var Name = {};
// Examines the cell in the first column, if it is empty replaces it with the name from the previous cell.
for ( var i = 0; i< info.length; i++){
if (typeof(info[i][0]) == "string" && info[i][0] == ""){
Name[i] = Name[i-1];
} else{
Name[i] = info[i][0];
}
}
var data = []
for (var i = 0; i<lastRow-1; i++){
data.push(Name[i]);
}
var writeRange = current.getRange(2,1,data.length,1);
writeRange.setValues([data]);
The value you are expecting should be a 2D array, 1 column of multiple rows. What you get when using data.push(Name[i]); is a simple array of strings.
Try this way : data.push([Name[i]]); this will return an array of arrays and should satisfy the conditions for setValues(data)
( don't forget to remove the brackets in your last setValues statement )