Find matching text in sheets - google-apps-script

At a charity, volunteers are issued i.d. cards with QR codes. To clock in, a volunteer scans their i.d. card. A script automatically inserts the person's name into column 1 of a Google Sheet and inserts a timestamp into column 2 of the same row, to indicate timeIn. Next, we want to automate clocking out.
When a person's i.d. card is scanned, I know how to insert the person's name into a temporary holding cell. Next, I need code to check whether that person has already signed in (i.e., compare contents of the holding cell to each cell in column 1 until a match or an empty cell is found). If a match is found, then a timestamp should be entered in column 3, in the row where the person signed in, to indicate timeOut. Then, data in the temporary holding cell should be cleared.
I am very new to scripts for google apps, and have not yet figured out how to compare the values in two cells or to code a loop.

You can do the following:
Retrieve all contents of column 1 with getRange() and getValues() and push them into an array
Verify with indexOf() either the value in the temporary holding cell matches an entry of the array and if so - retrieve the entry position index
Use setValue() to insert a timestamp into a cell of column 3, row index
Here is a sample:
function myFunction() {
var sheet=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var startRow=2;
var lastRow=sheet.getLastRow();
var numberRows=lastRow-startRow+1
var Ids=sheet.getRange(startRow,1,numberRows,1).getValues();
var temporary=sheet.getRange(2,4).getValue();//please modify according to the position of your temporary cell
var array=[];
var timestamp=Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
for(var i=0;i<numberRows;i++){
array.push(Ids[i][0]);
}
var index=array.indexOf(temporary);
if(index!=-1){
sheet.getRange(index+startRow,3).setValue(timestamp);
}
}
You might want to incorporate in a doGet() function of a Web App deployment that will run the code automatically when new data is inserted into the temporary cell.

Related

Create Unique ID from 0 to Upward Google Sheets

I have a sheet where data is being added to the last empty row and inserting the new row between the rows which has already data.
I want to create a Unique Numeric number that will be 0 to upward in sequence. Like we use =IF(B3<>"",B2+1,"").
But when new row is added the formula is missed I want to Add unique number for each row.
Because when you insert the row all values are bonded but when you type anything in newly added row in Col B all the number goes disturb. It should work like timestamp once a number is allotted to specific row data it should not change even new row is inserted within data.
For example If script has assigned number 1 to 7 and new row inserted then it should assign new row a number 8 even new row is inserted with data in col B
Your help will be appreciated
ALTERNATIVE SUGGESTION
If I have understood your goal correctly, you may try this custom script below that you can copy and paste as a bound script to your Spreadsheet file:
UPDATED Script
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getRange("B2:B").getValues().filter(String); //Add the range where data you want to have IDs, on my end, it's B2 and below (B2:B)
var ids = [];
var lastRow;
for(row=0; row<=data.length-1; row++){
ids.push([row]);
}
lastRow = ids.length + 1;
ss.getRange("A2:A"+lastRow).setValues(ids);
}
SAMPLE DEMONSTRATION
The custom function will add IDs starting on the cell A2 and below once you run it for the first time from the Apps Script editor:
When new added data on cell B10 has been entered:
In the top row,
=ARRAYFORMULA({"ID";SEQUENCE(COUNTA(B:B)+0)})
Change 0 to 1 or 2, if you want some blank row offset.

How do I detect the newest row in a specific column?

I am currently having some problems with my Google App Script.
What we have for now is: when the users input two values(time) into the column, the duration between the column will be automatically calculated in another column, which we did this by the functions in the Spreadsheet itself.
When we transfer the input values, we are using the function appendRow, and since things will get messed up in the column we set the value that is supposed to go in the duration column as null, so that it will be filled blank and that the function in the Spreadsheet itself will calculate it. However, the problem we encountered was that by using this function in the Spreadsheet, we get a 00:00 as a default value in every single row that doesn't have a time input, meaning that the appendRow function will detect these rows as filled, and will append the values to the very bottom of the Spreadsheet, creating a big gap between the rows with the values and without the values.
We need a solution to solve this issue, and we are posting to get any possible solutions about this.
So the method we thought of to solve this issue was using getRange(), then setValue. With getRange we thought of using something like make a 1x1 grid that has the row Index as the same row Index as the new cell in the specific column that we want to import the values, and for columns we can just choose the column Index. But we soon realized that this will still fail as appendRow in the row Index will still return the row index of the very last row in the spreadsheet.
The next method we tried was to detect any "0:00:00" in the duration column, then get the corresponding row Index, and use that row Index to setValue at that specific row using getRange(), which again, failed.
for (var i = 0; i < sheet.getMaxRows()+1; i++ ){
if (sheet.getRange(i,4).getValue() === "0:00:00") {
sheet.getRange(i,1).setValue(datas.enterDate);
sheet.getRange(i,2).setValue(datas.enterStartTime);
sheet.getRange(i,3).setValue(datas.enterEndTime);
sheet.getRange(i,6).setValue(datas.enterFullName);
sheet.getRange(i,7).setValue(datas.enterCategory);
sheet.getRange(i,8).setValue(datas.enterActivity);
sheet.getRange(i,9).setValue(datas.enterLink);
break;
}
}
Expected results: As the function goes down the column it will search for any cells containing "0:00:00", and when it does it will set the values with the users data.
Output: Nothing shown.
Try this:
col is the column number. The default is the column of the active cell
sh is a Sheet Obj and the the default is current active sheet
ss is a Spreadsheet Object and the default is the current active spreadsheet.
function newestRow(col,sh,ss){
var ss=ss || SpreadsheetApp.getActive();
var sh=sh || ss.getActiveSheet();
var col=col || sh.getActiveCell().getColumn();
var rg=sh.getRange(1,col,sh.getLastRow(),1);
var vA=rg.getValues();
while(vA[vA.length-1][0].length==0){
vA.splice(vA.length-1,1);
}
return vA.length+1;
}

Google Sheet: Script - Copy/Paste, Time delay, Copy/Paste, repeat for all values in column

I am trying to build a spreadsheet that automatically calculates intrinsic value of a list of stocks.
Cell 3 and cell 4 are linked to another sheet which has a range of formulas including data drawn from external websites.
I would like to write a script for a custom function that will use this setup:
Entered into cell 2, with cell 1 as an argument
Copies the ticker value (cell 1) into cell 3, activating the other sheet functions to do their calculations
After some time (e.g. 30 seconds), reads the value in cell 4 from those other functions
Returns the final value into cell 2.
I would then use this function for all values in column A.
So far Ive been able to figure out how to copy and paste one value. Any guidance would be appreciated, thanks in advance.
function val(ticker) {
var sheet = SpreadsheetApp.getActiveSpreadsheet(); //access to the spreadsheet
SpreadsheetApp.setActiveSheet(sheet.getSheetByName('List of Companies')); //access to the sheet by name
var range = sheet.getRange('A2'); //assign the range you want to copy
var copy = range.getValues();
sheet.getRange('D2').setValue(copy) //new range you want to paste a value
}

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.

How to copy row ranges in Google docs?

I have a Spreadsheet, like excel on Google Docs. I am using both Mozilla Firefox and Google Chrome, whichever works. Almost all my columns have dropdown list validation(you know, each cell has a dropdown list to select, I hope I made it clear). I arranged them when I first created the spreadsheet, gave all the columns validation from ranges I created.
My problem is, whenever I add a new row, that row doesn't have any validations, all of them are gone. The old rows still have the validations.
So then, I set the validations every time I add a new row, one by one. This is frustrating. Some people also had the same problem, asked online, but no one answered.
When I copy an empty row with validations and paste it on the new row, it works fine. So, what I am saying is, can you help me write a script for it? Like copying 5 rows when I execute the script?
I am trying to study the scripts but I did nothing nothing so far. I think
var actSc = SpreadsheetApp.getActiveSpreadsheet();
var range = actSc.getRange("A1:B1");
This all I got from the examples I saw. I mean it. I got nothing.
If this copies the ranges of one cell, then I guess I should do it for all my columns.
But how do I put them in the new row? Is there something like setRange?
I could really use some help. This is driving me crazy and I really don't get this script thing.
What I mean by range is that I have ranges like "STATES" and it includes "NY,LA,CA" etc. This NY,LA,CA fills the dropdown list in the cells of that STATES column. I hope this getRange means this range.
Sorry about my English.
If I understand correctly, you want to script a function that will add new rows to a sheet and maintain the existing validations for your columns. This is certainly possible and not too difficult. One approach could be a "refresh validations" function that updates your entire sheet all at once, in the event that you want to reuse it in other sheets. First, though, it sounds like you could use a brief overview of the object classes you need to know about to do basic Google Apps Scripts:
SpreadsheetApp - Think of this class as the foundation of the Spreadsheet Service. It provides file I/O and functionality that is not tied to specific spreadsheets, per se, such as UI and the creation of Data Validation sets. It's the interface to all of your individual spreadsheet documents.
Spreadsheet - A spreadsheet document file, which can contain multiple Sheets. This is what gets created when you create a new Google Sheets document in Drive. Provides document-level functions, such as the ability to manage ownership, set permissions, access metadata, etc. There's some overlap with the Sheet class, so this one can seem like a bit of a mishmash.
Sheet - An individual sheet is what you normally think of as a spreadsheet: a set of rows and columns. Each Spreadsheet document can contain many, distinct Sheets. The Sheet class lets you modify the overall appearance of the sheet. You can freeze or hide rows, protect ranges of cells from being edited, add/delete rows and columns, etc. You can also get data about the sheet, such as the last row that has content or the maximum range of the whole sheet.
Range - Dropping down another level, we reach the Range object, which represents a certain rectangular area of cells. This can be as small as a single cell or as large as the whole sheet. It does not seem possible, however, for Ranges to represent discontiguous cells. This is where you had some trouble, because you treated the Range object as content that you could copy and paste in your sheet, which is understandable. But a Range isn't the data in the cells it represents. It's just an interface to those cells. If you want to access the data itself, you have to drop down to the bottom level of the hierarchy:
Value - The actual contents of your sheets are normal JavaScript values: strings, integers, Booleans, etc. that you can manipulate with the subset of JavaScript that Google Apps Script supports.
In order to do something with the values in your sheet, you first get the Range object from the Sheet (which you get from the SpreadsheetApp) and then get the values from the Range:
var values = SpreadsheetApp.getActiveSheet().getRange("A1:B1").getValues(); // returns [[]]
Note that getValues() returns a multi-dimensional array. As a representation of the values in your sheet, it looks like this:
// row 1 [[column A, column B, column C, column D, ...],
// row 2 [column A, column B, column C, column D, ...],
// row 3 [column A, column B, column C, column D, ...],
// row 4 [column A, column B, column C, column D, ...],
// row 5 [column A, column B, column C, column D, ...], ...]
So if the range A1:B1 is a range of one row and two columns, you can retrieve the values with A1 notation or by specifying the upper left row and column of the range, and the number of rows and number of columns you want to retrieve:
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1:B1");
var range = sheet.getRange(1, 1, 1, 2); // row 1, column 1, 1 row, 2 columns
var values = range.getValues(); // returns [[50, 100]]
If the value in A1 is 50, and the value in B1 is 100, the last function above will return [[50, 100]]. You can access individual cell values directly, too:
var range = sheet.getRange("A1");
var value = range.getValue(); // returns 50
var cell = range.getCell().getValues(); // returns [[50]]
Obviously, you can set the values of ranges, too:
var range = sheet.getRange("A1:B2");
range.setValues([[50, 100]]);
range = sheet.getRange(1, 1); // same as sheet.getCell(1, 1)
range.setValue(50); // the value of A1, or row 1 column 1, is now 50
The next step is to figure out how the Data Validation class works. You create a Data Validation object using the Data Validation Builder, which lets you chain together a series of rules to apply to a range. You then set the range to that Data Validation rule set:
var stateList = ["AK", "AL", "AR", ...];
var rules = SpreadsheetApp.newDataValidation() // create a new Data Validation Builder object and use method chaining to add rules to it
.requireValueInList(stateList, true) // first param is the list of values to require, second is true if you want to display a drop down menu, false otherwise
.setAllowInvalid(false) // true if other values are allowed, false otherwise
.setHelpText("Enter a state") // help text when user hovers over the cell
.build();
range.setDataValidation(rules); // apply the rules to a range
Now you can insert rows and the rules should copy over into them automatically:
var lastRow = sheet.getLastRow(); // get the last row that contains any content
sheet.insertRowAfter(lastRow);
Or copy the rules and use them elsewhere:
var cell = sheet.getRange(1, 1, 1, 1);
var rule = sheet.getDataValidation(); // returns rule
var range = sheet.getRange("A1:B1");
var rules = range.getDataValidations(); // returns [[rules, rules]]
var lastRow = sheet.getLastRow(); // or sheet.getMaxRows()
range.setDataValidations(rules);
So you can very easily put these concepts together to write whatever sort of function you need to add rows, build validation rule sets, and add validations to new ranges of cells. You can do most of these things more concisely than I have here, but it sounds like you're looking for a more in-depth explanation. I hope it helps.
var sheetToUpdate = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheetToUpdate.insertRowAfter(sheetToUpdate.getLastRow());
var rangeToUpdate = sheetToUpdate.getRange(sheetToUpdate.getLastRow()+1,1,1,sheetToUpdate.getMaxColumns());
sheetToUpdate.getRange(sheetToUpdate.getLastRow(),1,1,sheetToUpdate.getMaxColumns()).copyTo(rangeToUpdate, {formatOnly:true});