Concatenation in Column X of a Google Sheet, based in Column Y - google-apps-script

I have a Google Sheet with addresses(talking about real life, street addresses) on one column, and status on those addresses (like VACANT, EVICTION, OCCUPIED) on another. I'm trying to create a formula that will allow me to make a new column with a concatenation of the address + a specific tag based on the status. For example, if I have address "11423 Whisper Sound Drive, FL", with the status "OCCUPIED", I wanna have another column that says "11423 Whisper Sound Drive, FL < green-dot >"
My current approach isn't working, I'm getting a parse error:
= function letsDoThis() {
var addressValue = getCell(D2);
var statusValue = getCell(G2);
if (statusValue == "OCCUPIED")
{
var newValue = addressValue + " <green-dot>";
getCell(O2).setValue(newValue);
}
}

You can try the following formula based solution:
Try the following formula in cell O2:
=if(G2="OCCUPIED";D2&" <green-dot>";)

Related

Trying to generate a unique code for every form reply

I am having some trouble getting a script that can generate a unique code for every form reply entry to work. Now, when I first tried writing it, I used the function name onFormReply(e) since I had read it somewhere, but turn out it didn't really work, so I'm trying to use onEdit(e), but it marks most values of the variables I wrote as undefined, even the argument (e) of the function itself (which is theoretically suposed to be custom and made to resemble the cell/s where the edit took place). Here is the code:
function onEdit(e) {
const ss = SpreadsheetApp.getActiveSheet();
const row = e.getRow();
var date = ss.getRange(row,1).getValue();
var department = ss.getRange(row,6).getValue();
department = department[0] + department[1]
var uniqueNumber = ss.getLastRow()
var finalCode = department + finalDate + uniqueNumber
ss.getRange(row,15).setValue(finalCode)
}
If you are dealing with Google Form responses, you can use the timestamp in column A as a unique ID. These timestamps are accurate down to the millisecond and will for all practical purposes always be unique.
The great benefit of this is that you do not need a script to get those unique IDs. Instead, use this array formula in row 1 of a free column in the right in the form responses sheet:
=arrayformula(
{
"Unique ID";
iferror( text( 1 / A2:A ^ -1, "yyyy-MM-dd_HH-mm-ss-000") )
}
)
The formula will fill the whole column automatically and will continue giving more results as new form responses are submitted.

Using for and if loops in Google Apps Script

Dear programming Community,
at first I need to state, that I am not quite experienced in VBA and programming in general.
What is my problem? I have created a topic list in google sheets in order to collect topics for our monthly meeting among members in a little dance club. That list has a few columns (A: date of creation of topic; B: topic; C: Name of creator; ...). Since it is hard to force all the people to use the same format for the date (column A; some use the year, others not, ...), I decided to lock the entire column A (read-only) and put a formular there in all cells that looks in the adjacent cell in column B and sets the current date, if someone types in a new topic (=if(B2="";"";Now()). Here the problem is, that google sheets (and excel) does then always update the date, when you open the file a few days later again. I tried to overcome this problem by using a circular reference, but that doesn't work either. So now I am thinking of creating a little function (macro) that gets triggered when the file is closed.
Every cell in Column B (Topic) in the range from row 2 to 1000 (row 1 is headline) shall be checked if someone created a new topic (whether or not its empty). If it is not empty, the Date in the adjacent cell (Column A) shall be copied and reinserted just as the value (to get rid of the formular in that cell). Since it also can happen, that someone has created a topic, but a few days later decides to delete it again, in that case the formular for the date shall be inserted again. I thought to solve this with an If-Then-Else loop (If B is not empty, then copy/paste A, else insert formula in A) in a For loop (checking rows 1 - 1000). This is what I have so far, but unfortunately does not work. Could someone help me out here?
Thanks in advance and best regards,
Harry
function NeuerTest () {
var ss=SpreadsheetApp.getActive();
var s=ss.getSheetByName('Themenspeicher');
var thema = s.getCell(i,2);
var datum = s.getCell(i,1);
for (i=2;i<=100;i++) {
if(thema.isBlank){
}
else {
datum.copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}}
}
The suggested approach is to limit the calls to the Spreadsheet API, therefore instead of getting every cell, get all the data at once.
// this gets all the data in the Sheet
const allRows = s.getDataRange().getValues()
// here we will store what is written back into the sheet
const output = []
// now go through each row
allRows.forEach( (row, ind) => {
const currentRowNumber = ind+1
// check if column b is empty
if( !row[1] || row[1]= "" ){
// it is, therefore add a row with a formula
output.push( ["=YOUR_FORMULA_HERE"] )
} else {
// keep the existing value
output.push( [row[0]] )
}
})
Basically it could be something like this:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Themenspeicher');
var range = sheet.getRange('A2:B1000');
var data = range.getValues(); // <---- or: range.getDisplayValues();
for (let row in data) {
var formula = '=if(B' + (+row+2) + '="";"";Now())';
if (data[row][1] == '') data[row][0] = formula;
}
range.setValues(data);
}
But actual answer depends on what exactly you have, how your formula looks like, etc. It would be better if you show a sample of your sheet (a couple of screenshots would be enough) 'before the script' and 'after the script'.

Service Invoked too many times for one day: geocode ARRAY FORMULA

I hope somebody can help me I have this problem in Google sheets. I have a column Address (I) ,a column Coordinates (J) and a Backup Coordinates column (K).
I'm using a script to create a custom formula to calculate the coordinates from the address using Google localization services. In my Coordinates column (J) I have an array formula like this
=ARRAYFORMULA(IF(LEN(K2:K) > 0,K2:K,GEOCODE_GOOGLE(I2:I)))
So, if K has a value then I copy that value to my column else I use the GEOCODE custom formula. My problem is that I'm gettis this error:
Service Invoked too many times for one day: geocode. (line 5)
I know there are limits for the use of this service but my sheet only have like 20 or 30 new rows per day, so I think maybe the problem is in my arrayformula?, my sheet have 200 rows right now, so maybe the formula is executing 200 times every time a new row is inserted?
I was using this custom function inside an array formula so on every new row on my sheet the formula was calculating coordinates for all the rows, So I had to found a way to validate if a row was already calculated. I have added a text to my address column. For example if the address is "123 Mapple Street" now it says "123 Mapple Street..DoNotLocate.." if it has already been located so my script won't geolocate it.
Here is my script with the validation:
function GEOCODE_GOOGLE(address) {
if (address.map) {
return address.map(GEOCODE_GOOGLE)
} else {
var n = address.search("…DoNotLocate…");
if (n == -1) {
var r = Maps.newGeocoder().geocode(address)
for (var i = 0; i < r.results.length; i++) {
var res = r.results[i]
return res.geometry.location.lat + ", " + res.geometry.location.lng
}
}
}
}

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
}

Writing a string of multiple date / time to a single cell

I have an array of a couple (the array is up to 10) date/time that I want to write to a spreadsheet using getRange().setValues(). I'm converting the array to a string and it looks correct in Logger.
[Mon Feb 02 14:01:00 GMT-06:00 2015, Tue Feb 02 01:00:00 GMT-06:00 2016, , , , , , , , ]
When I try to write the string to a single cell in a sheet:
target6.setValues(source_range6_values);
I get this error:
Incorrect range width, was 10 but should be 1 (line 84, file "Code")
Edited 4/28/2014 adding entire script:
/**
* Copies source range and pastes at first empty row on target sheet
*/
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
//changing the array values to a string
classDate.toString();
//Building a new variable with the string to be inserted below in the target sheet
var source_range6_values = classDate;
//source_range6_values.toString();
Logger.log(classDate[0]);
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Aadding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
To give a correct answer for your question, i guess i need to know how you get the value of source_range6_values.
One quick guess is you might want to use target6.setValue instead of target6.setValues since you want to write the data into one cell only...
A quick & dirty way would be to replace the commas(with spaces):
source = String(source_range6_values).replace("," , " ");
I've had fun with GAS and variables. Casting it as a String should let you use the string functions on it. If that doesn't work can you share a mock-up of your sheets so I can take a look?
edit:
I had to play around with it a bit, seems google's version of .replace() only replaces the first instance (and doesn't allow .replaceAll() ).
I edited your code starting on line 23:
//Getting the values of the array
var classDate = daterange.getValues().toString();
//Building a new variable with the string to be inserted below in the target sheet
//Google has bugs, .replace() seems to only replace the first instance
//-while {} loop replaces all of them
while (!classDate.equals(classDate.replace("," , " "))) { classDate = classDate.replace("," , " "); };
var source_range6_values = classDate;
All the dates are in one cell if you change only those lines (and no errors).
I appreciate the help you two have given me trying to answer this question. #swimmingwood fixed the actual capture of the data into a string, but it left commas and when I inserted it into the target sheet, it wrote it to multiple cells with an error. It did write to the sheet but the error had you use a CTRL-E (inside the taget sheet) to complete the insert and wrote them into separate cells.
#MickATX suggested the code to replace the commas in the string with a space, which would be fine, but apparently he discovered a Google scripting problem that would only allow for the first comma to be replaced and ignore the rest. Great knowledge never-the-less.
I ended up using a formula in an addition cell in the source sheet that looked like this:
=ArrayFormula(CONCATENATE(REPT(TEXT(H2:Q2,"mm/dd/yyyy hh:mm a")&CHAR(10),H2:Q2>0)))
This formula wrote all the date/time entries provided by the form entry into one cell of the source sheet and ONLY the number of entries (1-10). I then wrote that single cell to the target sheet via the script.
Thanks to #swimmingwood and #MickATX for trying to help me, both provided worthy knowledge.
I've read a couple of strange answers here...
If you write an 2D array to a sheet it will obviously be written accross multiple cells... commas are definitely not the issue but the nature of the object is.
Simply convert your array into a string using .toString() or .join() (the latter providing the advantage you can choose the separator to use) and setValue() (without S) at the place you want.
the commas you see in the logger are only typographic representation of array elements separators...
And, last point : the .join() or .toString() methods return new variables, they don't modify the original value so when you write classDate.toString(); you are not doing anything ...
you should write it like this :
classDateAsAString = classDate.toString();
finally your code :
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
var source_range6_values = classDate.join(' & ');// using & as separator for example
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Adding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
Now if you want to format the dates in a more civilized way, that should be handled a bit differently... let me know if you still need it / want it.