Google Sheets - Create Data Validation - google-apps-script

I need to create several hundred data validation dropdowns that will refer to different ranges.
Dropdowns needs to be created in each cell of sheet1, in column 'O'.
However each dropdown has to refer to a different row in another sheet ('sheet2').
For example:
data validation dropdown in 'sheet1', cell 'O2' will refer to an entire row 1 in 'sheet2'
dropdown in 'sheet1', cell 'O3' will refer to a row 2 in 'sheet2', and so on.
Data validation setup:
List from range, show dropdown list in a cell, reject input.
I'd appreciate if you guys could help out with a script that will create predefined number of such dropdowns in a column.
regards,

You can use this sample code:
function createDataValidation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var s2_lastRow = s2.getLastRow();
//create data validation per row
for (var row = 1; row <= s2_lastRow; row++){
//create an a1Notation to select a complete row sample: "A1:1", "A2:2", and so on.
var a1Notation = "A"+row+":"+row;
//create data validation rule
var listRange = s2.getRange(a1Notation);
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(listRange, true)
.setAllowInvalid(false)
.build();
//assign rule to the current cell in sheet1 column "O"
//add 1 row offset since data validation will start at cell "O2"
a1Notation = "O"+(row+1);
Logger.log(a1Notation);
var cell = s1.getRange(a1Notation);
cell.setDataValidation(rule);
}
}
What it does?
Get individual sheets using Spreadsheet.getSheetByName(name)
Get Sheet2's last row number using Sheet.getLastRow()
Loop each individual row in Sheet2
Create an a1Notation string for the current row using this format (row1: "A1:1")
Select a range to be used for the data validation using Sheet.getRange(a1Notation)
Create a new data validation rule using SpreadsheetApp.newDataValidation(). This will return a DataValidationBuilder where you can specify what type of data validation to use. For Values in Range, use DataValidationBuilder.requireValueInRange(range, showDropdown)
Regarding rejecting inputs, by default new data validation rules set it to true. See DataValidationBuilder.setAllowInvalid(allowInvalidData)
Set newly created data validation rule for a specific cell using Range.setDataValidation(rule)
OUTPUT:
Sample Sheet2:
Sample Output Sheet1:

Related

How to lookup value from sheet1 on sheet2, and then fill in the date to the right of match on sheet2

Complete Google Apps Script novice question. I am trying to build some additional functionality into my spreadsheet where I can attach a script to a button that "checks off" the current selection as completed with the current date on a different sheet.
The pages are on one page of the sheet "sheet1" and the index of all pages are on "sheet2".
I am trying to run a textfind/replace function to find the cell of the match from sheet1, match the cell value from a column in "sheet2". From there I was thinking to try and pull out the row and column of that cell and then try write the date one cell to the right.
Im not sure how to pull the row and column out of the .getA1notation() below... and then really not sure if this is the right way to approach the problem.
From below I would want to look up the cell values from sheet 2 (F1143 on this one) and then write the current date in G1143.
Here is where I am so far:
function markComplete() {
var spreadsheet = SpreadsheetApp.getActive();
var encounterSheet = spreadsheet.getSheetByName("sheet2");
var tosearch = "chapter1"; //hoping to make cell reference from "sheet1"
var tf = encounterSheet.createTextFinder(tosearch);
var all = tf.findAll();
for (var i = 0; i < all.length; i++) {
Logger.log('The sheet %s, cell %s, has the value %s.', all[i].getSheet().getName(), all[i].getA1Notation(), all[i].getValue());
Logger.log(all[i].getA1Notation());
}
}
Logger results:
8:41:33 AM Notice Execution started
8:41:34 AM Info The sheet Sheet2, cell F1143, has the value Chapter1.
8:41:34 AM Info F1143
8:41:34 AM Notice Execution completed
Sheet.findAll() returns an array of the ranges containing that text.
let all = tf.findAll();
all.forEach( range => range.offset(0,1).setValue(new Date());
Reference
TextFinder.findAll()
Range.offset()

How can I conditionally copy between Google sheets?

I would like to conditional copy a value from one sheet to another.
About the below script:
It checks if in SOURCE tab the value in column H is equal to 45 and if Column A is empty. If so, it should copy the value in Column G into the last row of column A in TARGET tab. It also ads data to some of the TARGET tab columns
If I change this: sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(destination.getLastRow()+1, 1)),{contentsOnly:true};
to this: sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(destination.getLastRow()-1, 1)),{contentsOnly:true}; the cell is pasted on Column A on the penultimate row, which makes me think that the present code tries to copy to a row after the last visible one instead of pasting into the next blank cell after a non blank one in Column A, hence returning the error explained on the next bullet
As is, it returns the error: The coordinates of the range are outside the dimensions of the sheet. (I understood that the problem is because of the use of getlasrow and being used as is, it's trying to retrieve a row that does not exist)
This is the code so far:
function CopyTo(){
var sheet = SpreadsheetApp.getActive();
var sourceSheet = sheet.getSheetByName("SOURCE");
var destination = sheet.getSheetByName("TARGET");
var lastRow = sourceSheet.getDataRange().getLastRow();
for(var row=3; row<=lastRow; row++){
if(sourceSheet.getRange(row,1).getValue() == "" & sourceSheet.getRange(row,8).getValue() >= "45"){
Logger.log("CELL H"+row+" has \">=45\""+"\n=================\n"+"CELL A"+row+" is \"empty\""+"\n=================\n"+"RANGE TO COPY:\n"+ "\"G"+row+":G"+row+"\"");
var rangeToCopy = "G"+row+":G"+row;
sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(destination.getLastRow()+1, 1)),{contentsOnly:true};
destination.getRange(destination.getLastRow(), 9).setValue("Added Text Column 9");
destination.getRange(destination.getLastRow(), 13).setValue("Added Text Column 13");
destination.getRange(destination.getLastRow(), 14).setValue(new Date());
}
}
}
Also, there are other columns in Target sheet that fetch values so maybe that's the reason why the script isn't pasting on the right place but I can't get it to work.
Could you be so kind to help out?
Thank you in advance.
This is a test sheet which returns the previously mentioned error when running the script.
UPDATE
I've added to the script:
var column = destination.getRange('A' + destination.getMaxRows())
var lastFilledRow = column.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1)
Logger.log(lastFilledRow);
so I would get the last filled row number in Column A. It retrieves it but I'm having trouble in using it as range and I get the error: Exception: Cannot convert 'Range' to int.
sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(destination.getRange(lastFilledRow,1), 1)),{contentsOnly:true};
The problem lies with your TARGET sheet having a set maximum row of 84, thus, destination.getLastRow()+1 will return 85 which is outside the said maximum row.
To solve this, create a new TARGET sheet or find a way to remove the limit in the current TARGET sheet. Your current code should be fine.
EDIT:
I've edited your v2 code to make it work on the current test sheet provided. Please see code below.
function CopyTo_V2(){
var sheet = SpreadsheetApp.getActive();
var sourceSheet = sheet.getSheetByName("SOURCE");
var destination = sheet.getSheetByName("TARGET");
var lastRow = sourceSheet.getDataRange().getLastRow();
var column = destination.getRange('A' + destination.getMaxRows())
for(var row=3; row<=lastRow; row++){
if(sourceSheet.getRange(row,1).getValue() == "" & sourceSheet.getRange(row,8).getValue() >= "45"){
Logger.log("CELL H"+row+" has \">=45\""+"\n=================\n"+"CELL A"+row+" is \"empty\""+"\n=================\n"+"RANGE TO COPY:\n"+ "\"G"+row+":G"+row+"\"");
var rangeToCopy = "G"+row+":G"+row;
var lastFilledRow = parseInt(column.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1))
Logger.log(lastFilledRow);
sourceSheet.getRange(rangeToCopy).copyTo(destination.getRange(lastFilledRow+1,1)),{contentsOnly:true};
destination.getRange(lastFilledRow+1, 9).setValue("Added Text Column 9");
destination.getRange(lastFilledRow+1, 13).setValue("Added Text Column 13");
destination.getRange(lastFilledRow+1, 14).setValue(new Date());
}
}
}

Need to find a way for data entry to start after a specific row

Here is my script:
function CopyRange() {
var sss = SpreadsheetApp.openById('1EA2F99W1lpgTzX3zoTNpzuEXWh-mDkvo9UyZC4kDNtQ'); //replace with source ID
var ss = sss.getSheetByName('UKG-A'); //replace with source Sheet tab name
var range = ss.getRange('G4:K4'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('1EA2F99W1lpgTzX3zoTNpzuEXWh-mDkvo9UyZC4kDNtQ'); //replace with destination ID
var ts = tss.getSheetByName('UKG-A'); //replace with destination Sheet tab name
ts.getRange(7 && ts.getLastRow()+1,15,1,5).setValues(data); //you will need to define the size of the copied data see getRange()
}
The code works as normal. When I run the code it get data from source sheet and enter on the destination sheet after the last row which has data. But what i want is to define a specific row to start data entry and continue from that row. For example I want data entry to start After the 7th row of UKG-A sheet.
Explanation:
Your goal is to add a new row after row seven and then set the new data on this newly created row.
you can use insertRowAfter(afterPosition) to insert a new row after a specified position.
start pasting from row 8.
(optional) dynamically get the length of data instead of hardcopying the size.
Solution:
Change:
ts.getRange(7 && ts.getLastRow()+1,15,1,5).setValues(data);
to:
ts.insertRowAfter(7)
ts.getRange(8,15,data.length,data[0].length).setValues(data);
Related:
Sheet.getRange(1,1,1,12) what does the numbers in bracket specify?
getRange(row, column, numRows, numColumns)

Automatically write data from one sheet to other with the help of dropdown box

I have a drop down box with 3 options in my sheet 1 and I want to copy data from sheet 1 to the dedicated sheets in the same spreadsheet according to the option selected, how do I do that?
Here is my solution using Apps Script. If you want to detect any changes in the dropdown and copy paste your original range to other sheet depening on the values of this dropdown, you want to use an onEdit() simple trigger that will run your function everytime the value of a cell is changed (in your case the dropdown).
Then by just simply using the methods getValue() and setValues() along with getRange() you can move your range from one Sheet to another as you pleased. The following piece of code includes self explanatory comments on the example range and dropdown shown below:
function onEdit() {
// Get sheet 1
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
// Get sheet 2
var sheet2 = SpreadsheetApp.getActive().getSheetByName('Sheet2');
// Get sheet 3
var sheet3 = SpreadsheetApp.getActive().getSheetByName('Sheet3');
// Get value of dropdown
var value = sheet.getRange('A1').getValue();
// Get values of the range in Sheet 1 that we want to copy in other sheets
var range = sheet.getRange('B1:C2').getValues();
// If dropdown value is A
if(value=='A'){
// Set the values of the range you want in sheet 2
// Bare in mind that for this to work the range in the new sheet must
// be equal size in rows and columns as from the range we are getting in
// the original sheet
sheet2.getRange('B1:C2').setValues(range);
// If it is B (and you could go on with as many as you want)
}else if(value=='B'){
sheet3.getRange('B1:C2').setValues(range);
}else{
}
}

Google Sheets - Identify duplicates and overwriting data

I've heavily edited the original question I posted, as i have solved some of the issue myself. I'm now stuck on just one thing.
function payINVOICE() {
var ss = SpreadsheetApp.getActive();
var ds = SpreadsheetApp.openById("14imcEob2qIZbH6AjGYtf16MJxbnfkhQn1ae4jR-Nzq4");
var srcSheet = ss.getSheetByName("INVOICE_ENTRY");
var dstSheet = ds.getSheetByName("INVOICE_ENTRY");
var data_range = srcSheet.getRange('B4:J100');
var data_data = data_range.getValues();
var data_clean = data_data.filter(function (r) {return r[1]});
var clear_range = srcSheet.getRange('B4:I100');
var lr = dstSheet.getLastRow();
dstSheet.getRange(lr+1, 2,data_clean.length,9).setValues(data_clean);
clear_range.clear();
}
This code checks the range B4:J100 for a value in Column B.
If there is a value and the script is run, it copies those rows onto dstSheet.
My role is marking invoices as paid or not.
The dstSheet will already contain the data, which is pulled back into the srcSheet with a query. Column K is not part of the original query.
If I mark a row as "PAID" in column K on the srcSheet, I want the code to take the data_data variable and overwrite what is already in the dstSheet, so that the query then pulls the data back into srcSheet with column J then showing "PAID".
It means I can then change column K to "NOT PAID", run the script again and it will over-write the "PAID".
This makes better sense than my last post and I am so close to achieving what I need, just stuck on this last bit.
If you simply want to monitor the changes between the two mentioned sheets, it would be much easier to use an onEdit(e) trigger which will tell you which cell has been edited.
Snippet
function payINVOICE(e) {
var srcSheet = SpreadsheetApp.getActiveSheet(); //gets the active sheet which is supposed to be source sheet
var dstSheet = SpreadsheetApp.openById('DEST_SHEET_ID').getSheetByName('INVOICE_ENTRY'); //gets the destination sheet
if (e.range.getSheet().getName() == 'INVOICE_ENTRY' && e.range.getColumn() == 11) { //e specifies where the edit has been made - therefore this if condition checks if the edit is in the INVOICE ENTRY sheet and if the column is the K column
var row =e.range.getRow(); //this gathers the row at which the edit has been made
var data = srcSheet.getRange(row, 2, 1, 10).getValues(); //this gathers the data corresponding to the row at which the edit has been made
dstSheet.getRange(row, 2, 1, 10).setValues(data); //this sets the data into the corresponding row in the destination sheet
}
Explanation
The above code uses the onEdit(e) installable trigger and the e event object. In this way, when an edit is being made on the srcSheet on the 11th column (aka K column) and the sheet name is "INVOICE_ENTRY", then the row at which the change has been made is kept in the row variable. Afterwards, the corresponding row of data is kept in the data variable; the getRange(row, 2, 1, 10) references the range for the row at which the change on the K column has been made. In order to update the dstSheet, the data value is set to the according range using setValues(data).
Installing the trigger
To make the payINVOICE(e) function trigger on an edit action, you need to install an onEdit trigger.
This is being done by accessing the project's triggers by clicking this icon:
After that, you just need to create a new trigger by clicking the Add trigger button and create a trigger with the following settings:
Trying the function
In order to try the behavior for this, you just need to make an edit on the srcSheet on the K column and this change will be reflected in the destSheet.
Note
The ranges that have been used in this script are chosen considering the fact that:
K column consists of the PAID/NOT PAID text;
The srcSheet and the dstSheet have the data wanted in the same ranges.
You might need to customize these according to your sheet and add the needed formulas/filters you have mentioned.
Reference
Apps Script Installable Triggers;
Apps Script Event Objects.