So I was referencing this question "https://stackoverflow.com/questions/56826834/how-to-get-a1notations-when-findall-function-returning-range-range-range-ran" and I like it and what it does but I need to go a little further and im still a beginner but this is what I have.
var searchTerm = "Changeme"; // Please set this.
var sheetName = "Sheet1"; // Please set this.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheetName = ss.getSheetByName(sheetName);
var completeSearch = dataSheetName.createTextFinder(searchTerm).findAll();
for (var i = 0; i < completeSearch.length; i++) {
var range = completeSearch[i];
var value = range.getValue();
var rowValue = dataSheetName.getRange(range.getRow(), 1, 1, dataSheetName.getLastColumn()).getValues();
Logger.log(value) // Value of the searched range
Logger.log(rowValue)
Logger.log(range.getA1Notation()
//Logger.log(rowValue) // Values of the row of searched range
}
}
This is enough to get me the row that I am looking for and the Cell of what I am searching for but what I want to do is check a column for what I am searching for and return the row then grab the value of another column in the same row and based off that answer send an email out to someone with the answer.
So Search Column H for the "SearchTerm" If I found it in the row check the cell value in column A, B, and C and if then put the values in another sheet to send them out in an email.
Any help will be appreciated.
I believe your goal is as follows.
You want to retrieve the values of columns "A" to "C" by searching the value of searchTerm at the column "H".
In this case, how about the following modified script?
Modified script 1:
When you want to use TextFinder, how about the following script?
var searchTerm = "Changeme"; // Please set this.
var sheetName = "Sheet1"; // Please set this.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheetName = ss.getSheetByName(sheetName);
var completeSearch = dataSheetName.getRange("H:H").createTextFinder(searchTerm).findAll();
for (var i = 0; i < completeSearch.length; i++) {
var range = completeSearch[i];
var rowValue = dataSheetName.getRange(range.getRow(), 1, 1, 3).getValues();
Logger.log(rowValue)
}
Modified script 2:
I thought that in your situation, at first, when the values are retrieved from the sheet and retrieve the expected values in a loop, the process cost might be a bit low. The sample script is as follows.
var searchTerm = "Changeme"; // Please set this.
var sheetName = "Sheet1"; // Please set this.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheetName = ss.getSheetByName(sheetName);
var values = dataSheetName.getRange("A1:H").getValues();
var res = values.reduce((ar, [a,b,c,,,,,h]) => {
if (h == searchTerm) ar.push([a, b, c]);
return ar;
}, []);
console.log(res);
References:
Class TextFinder
reduce()
Edit:
From your sample Spreadsheet and your replying comments,
You want to search the column "H".
You want to use the values of columns "C,D,E" of the searched row.
About the values of recipient, subject, body, options of MailApp.sendEmail(recipient, subject, body, options), you will put subject and options as the constant value. recipient is from the column "G". body is like Here is the list of your employees that are in the sheet Last Name First Name Location 123 456 Test1 1r3 1t1 Test1.
In this case, how about the following sample script?
Sample script:
function myFunction() {
var searchTerm = "Supervisor1"; // Please set this.
var sheetName = "Sheet1"; // Please set this.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheetName = ss.getSheetByName(sheetName);
var values = dataSheetName.getRange("C1:H").getValues();
var email = "";
var res = values.reduce((ar, [c,d,e,,g,h]) => {
if (h == searchTerm) {
ar.push([c, d, e]);
email = g;
}
return ar;
}, []);
if (res.length == 0 || !email) return;
var message = {
to: email,
subject: "sample subject",
body: "Here is the list of your employees that are in the sheet Last Name First Name Location\n\n" + res.map(r => r.join(",")).join("\n"), // Modified
name: "Test",
bcc: "testing",
attachments: [SpreadsheetApp.getActiveSpreadsheet().getAs(MimeType.PDF).setName("Employee Report")]
}
MailApp.sendEmail(message);
}
Related
I'm looking to edit this script so that before the rows are deleted, they are appended to the tab "Done". I tried creating a target sheet, but can't seem to get it working:
function remove(){
var SS = SpreadsheetApp.getActive();
var SHEET = SS.getSheetByName("Todo");
var TARGETSHEET = SS.getSheetByName("Done");
var RANGE = SHEET.getDataRange();
var DELETE_VAL = "Remove";
var COL_TO_SEARCH = 0;
var rangeVals = RANGE.getValues();
for(var i = rangeVals.length-1; i >= 0; i--){
if(rangeVals[i][COL_TO_SEARCH] === DELETE_VAL){
// TARGETSHEET.appendRow(i+1);
// TARGETSHEET.moveRows(rangeVals);
SHEET.deleteRow(i+1);
};
};
};
I essentially am trying to get it to operate like the script below, but for ANY value that has the word "Remove" in a specific column; not one by one. I want to run it from a button:
function onEdit(e){
var sourceSheet = e.range.getSheet();
if(sourceSheet.getSheetName() === 'Todo'){
var row = e.range.getRow();
var rowRange = sourceSheet.getRange(row, 1, 1, sourceSheet.getLastColumn());
var rowValues = rowRange.getValues()[0];
if(rowValues[0] === "Remove"){
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Done");
targetSheet.appendRow(rowValues);
sourceSheet.deleteRow(row);
}
}
}
Any thoughts are greatly appreciated
I believe your goal is as follows.
You want to check column "A" of "Todo" sheet, and when the value of column "A" is "Remove", you want to move the row to the destination sheet "Done".
You want to run the script by a button on the Spreadsheet.
In this case, how about the following sample script?
Sample script:
function myFunction() {
const srcSheetName = "Todo";
const dstSheetName = "Done";
const ss = SpreadsheetApp.getActiveSpreadsheet();
const srcSheet = ss.getSheetByName(srcSheetName);
const dstSheet = ss.getSheetByName(dstSheetName);
const range = srcSheet.getDataRange();
let srcValues = range.getValues();
const dstValues = srcValues.filter(([a]) => a == "Remove");
srcValues = srcValues.filter(([a]) => a != "Remove");
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, dstValues.length, dstValues[0].length).setValues(dstValues);
range.clearContent().offset(0, 0, srcValues.length, srcValues[0].length).setValues(srcValues);
}
When this script is run, the above goal is obtained. In this sample, the sheet of "Todo" is updated by new values filtered by "Remove".
When you want to run this script by a button, please assign myFunction to the button.
Reference:
filter()
I have a forms responses sheet and an index sheet. The index sheet has Name, Location, Email. I am needing to get the value of the last row in form responses and find a name match in my index sheet. Then log the location and email. Right now my code finds all matches and displays them. As I stated before I only want the match for last row.
function findLocation() {
var ss = SpreadsheetApp.getActive();
var sh1 = ss.getSheetByName('Form Responses');
var vs1 = sh1.getRange('I3:I' + sh1.getLastRow()).getValues().flat();
var sh2 = ss.getSheetByName('Match');
var vs2 = sh2.getRange('A2:C' + sh2.getLastRow()).getValues();
var matchRows = vs2.filter(row => row[0].length && vs1.includes(row[0]));
matchRows.forEach(row => {
var siteMatch = row[1];
var emailMatch = row[2];
Logger.log(JSON.stringify(siteMatch));
Logger.log(JSON.stringify(emailMatch));
});
}
Screenshot for clarification
Description
I made a simple test data set that I think is similar to yours. The sample script finds the name in index sheet Match that matches the last form response row.
Form Responses
Match
Sample script
function findLocation() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('Form Responses');
var vs1 = sh1.getRange(sh1.getLastRow(),1).getValue();
var sh2 = ss.getSheetByName('Match');
var vs2 = sh2.getRange('A2:C' + sh2.getLastRow()).getValues();
var matchRow = vs2.find( row => row[0] === vs1 );
Logger.log(matchRow);
}
catch(err) {
Logger.log(err);
}
}
Execution log
9:51:59 AM Notice Execution started
9:52:00 AM Info [John, A, john#somemail.com]
9:52:01 AM Notice Execution completed
References
Array.find()
I got the following table to populate (range D6:J15) as I search the data in another sheet, based on a date criteria found in row 4:
This is where I'm to look for the data, considering Col A as the basis for the criteria:
My difficulty is to concatenate the data, as they meet the criteria.
This is the code I'm working on:
/* #OnlyCurrentDoc */
function editarPrevProd() {
const lock = LockService.getScriptLock();
lock.tryLock(3000);
if (lock.hasLock()) {
var sourceSheet = 'PrevProdDB2';
var destinationSheet = 'Previsão Entreposto';
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheet);
var ActiveSheetName = ss.getActiveSheet().getName();
var LastRowSource = sheet.getLastRow();
var LastColumnSource = sheet.getLastColumn();
var values = sheet.getRange(2,1,LastRowSource,9).getValues();
var csh = ss.getSheetByName(destinationSheet);
var itens = csh.getRange("I40:J57");
var data = [];
var weekNo = csh.getRange("B4").getValue();
var weekDates = csh.getRange("D4:J4").getValues();
if (weekNo == "") {
Browser.msgBox("Escolher uma data e tente novamente!");
return;
}
//var clearRng = ["K34:K35", "N34:N35", "I40:K"];
//csh.getRangeList(clearRng).clearContent();
for (var i = 0; i < values.length; i++) {
if (values[i][7] == weekNo) {
data.push(values[i]);
//break;
}
}
var dias = 0;
var prevData = [];
for (var j = 0; j < weekDates.length; j++) {
dias = dias + 1;
Logger.log("Dias da Semana: " + dias);
for (var a = 0; a < data.length; a++) {
if (weekDates[j].valueOf() == data[a][0].valueOf()){
prevData.push(data[a][4]);
}
}
}
//map columns whose data will be set in the header.
var user = data.map(function(e){return e[5];});
var lastUpdate = data.map(function(e){return e[6];});
//Copy data array to destination sheet
csh.getRange("I1").setValue(user);
csh.getRange("I2").setValue(lastUpdate);
//csh.getRange("E6").setValue(timeStamp);
//If you wanted to set arrays in the form of
//a table, you'd use this below instead
var seg = data.map(function(e) {return [e[3]];});
var ter = data.map(function(e) {return [e[4]];});
var qua = data.map(function(e) {return [e[5]];});
var qui = data.map(function(e) {return [e[6]];});
var sex = data.map(function(e) {return [e[7]];});
var sab = data.map(function(e) {return [e[8]];});
var dom = data.map(function(e) {return [e[9]];});
//csh.getRange(6,4,data.length,1).setValues(seg);
lock.releaseLock();
}
}
Here's a sample of the file. Note that the gs file I'm working on is named SalvaPrevProducao.
https://docs.google.com/spreadsheets/d/1NOWkzQIAPPdZdxeeTR7Id2v8LR00_u06uPhHs3tzLuU/edit?usp=sharing
I believe your goal as follows.
You want to convert the above image to the bottom image using Google Apps Script.
The date header is the cells "D4:J4".
The source values are the cells "A6:M".
The column "M" of ID is Semana in the destination sheet.
In this case, I would like to propose the following flow.
Retrieve values from the source sheet.
Create an array for putting to the destination sheet.
Put the array to the destination sheet.
When this flow is reflected to the Google Apps Script, it becomes as follows.
Sample script:
Before you use this script, please set the variables of srcSheetName and dstSheetName.
function editarPrevProd() {
const srcSheetName = "Data Source"; // This is the source sheet name.
const dstSheetName = "destSheet"; // Please set the destination sheet name.
// This is from https://stackoverflow.com/a/44563639
Object.prototype.get1stNonEmptyRowFromBottom = function (columnNumber, offsetRow = 1) {
const search = this.getRange(offsetRow, columnNumber, this.getMaxRows()).createTextFinder(".").useRegularExpression(true).findPrevious();
return search ? search.getRow() : offsetRow;
};
// 1. Retrieve values from the source sheet.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const srcSheet = ss.getSheetByName(srcSheetName);
const lastRow = srcSheet.get1stNonEmptyRowFromBottom(1);
const [[, , , ...header1], header2, ...srcValues] = srcSheet.getRange("A4:M" + lastRow).getValues();
// 2. Create an array for putting to the destination sheet.
const values = header1.reduce((ar, h, i) => {
srcValues.forEach(([a, b, c, ...dm]) => ar.push([h, a, b, c, dm[i] || 0, "", "", dm.pop(), h]));
return ar;
}, [["Data", "Tipo", "Cod", "Descrição", "Qtd", "Usuário", "TimeStamp", "Semana", "Data"]]);
// 3. Put the array to the destination sheet.
const dstSheet = ss.getSheetByName(dstSheetName);
dstSheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}
When above script is run, the values are retrieved from srcSheetName and the converted values are put to dstSheetName .
Result:
When above script is run, the following result is obtained.
Note:
Unfortunately, from your question and sample Spreadsheet, I couldn't understand about Usuário and TimeStamp of the columns "F" and "G". At the sample output situation of Turn the data from the left into the format on the right side, Usuário and TimeStamp have no values.
References:
reduce()
forEach()
It is unclear why you would need to resort to scripting to look up those values, when a filter() formula would seem capable to do the same. Try this formula in cell D6:
=sum( iferror( filter(PrevProdDB2!$E$2:$E, PrevProdDB2!$B$2:$B = $A6, PrevProdDB2!$H$2:$H = $B$4, PrevProdDB2!$I$2:$I = D$4) ) )
I need to pull/import data from "sheet 1" to "sheet 2" based on column 4 being a specific text string. The script should not pull lines that already exist.
I have no idea if this is possible. I can pull the data but it just recopies everything so I have duplicates.
Any help would be super appreciated.
function onEdit() {
var ss = SpreadsheetApp.openById('1Ognzsi6C0DU_ZyDLuct58f5U16sshhBpBoQ8Snk8bhc');
var sheet = ss.getSheetByName('Sheet 1');
var testrange = sheet.getRange('D:D');
var testvalue = (testrange.getValues());
var sss = SpreadsheetApp.getActive();
var csh = sss.getSheetByName('Sheet 1');
var data = [];
var j =[];
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Dan') {
data.push.apply(data,sheet.getRange(i+1,1,1,11).getValues());
j.push(i);
}
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
Sheet 1
Sheet 2
Solution
You should be able to replace your code with this and it will work. You would put this script in the target sheet (Sheet 2), and replace the ID in the first line of the function with the origin (Sheet 1).
I'll leave it up to you to change to an onEdit or to make it a menu item. Right now it can be run from the script editor. onEdit doesn't make sense to me as an appropriate trigger. Maybe you prefer a Time-Driven Trigger. Though a custom menu would be the best way IMO.
function pullData() {
var sourceSs = SpreadsheetApp.openById('[YOUR_SPREADSHEET_ID]');
var sourceRange = sourceSs.getSheetByName('Sheet1').getDataRange();
var sourceHeight = sourceRange.getHeight();
var sourceWidth = sourceRange.getWidth();
var sourceData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, sourceHeight - 1, sourceWidth).getValues();
var targetSs = SpreadsheetApp.getActive();
var targetRange = targetSs.getSheetByName('Sheet1').getDataRange();
var targetHeight = targetRange.getHeight();
var targetWidth = targetRange.getWidth();
var sourceDataChecker = [];
var targetDataChecker = [];
sourceData.forEach((row) => {
sourceDataChecker.push(row[0] + row[1] + row[2] + row[3]);
})
if (targetHeight != 1) {
var targetData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, targetHeight - 1, targetWidth).getValues();
targetData.forEach((row) => {
targetDataChecker.push(row[0] + row[1] + row[2] + row[3]);
});
};
sourceData.forEach((row, i) => {
if (!(targetDataChecker.includes(sourceDataChecker[i]))) {
targetSs.appendRow(row);
};
});
}
Explanation
This script builds an "index" of each row in both sheets by concatenating all the values in the row. I did this because I noticed that sometimes you have "joe" in two rows, and so, you can't simply use column 4 as your index. You are basically checking for any row that is different from one in the target sheet (Sheet 2).
If the target sheet is blank, then all rows are copied.
References
Append Row to end of sheet
Get Data Range (range of sheet that contains data)
Get Range Height (to deal with headers)
Get Range Width
for Each
I am wondering if there is a way to auto select the cell range based on selected filter.
Example:
Set Filter in (Column H)
Auto select the result cell data starting Column A (A2000): Column C (C5000) etc. --- This is where I am getting stuck. I do not know how to write to auto select the result cell data based on the selected filter.
Currently, my workout is to manually enter the cell so I could move on with writing the codes. I hope I am making sense above.
---- Code----
function ColHtoActive() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Current');
var crange = sheet.getRange('A7:I7350').activate();
var currentCell = sheet.setCurrentCell(sheet.getRange('H7');
var hSfilter = sheet.getRange('A7:I7350').createFilter();
var ccC1 = sheet.getRange('H7').activate();
var cCriteria = SpreadsheetApp.newFilterCriteria().setHiddenValues('Inactive']).build();
sheet.getFilter().setColumnFilterCriteria(8, cCriteria);
}
function copycolA() {
var ss = SpreadsheetApp.getActive().getSheetByName('A');
ss.getRange('A2307').activate();
ss.getRange('A2307:A7155').copyTo(
ss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
false);
}
You can get the filtered range dimensions from getFilter().getRange(). This will copy all the filtered range:
function copycolA() {
var sourceSheet = SpreadsheetApp.getActive().getSheetByName('Current');
var targetSheet = SpreadsheetApp.getActive().getSheetByName('A');
var sourceRange = sourceSheet.getFilter().getRange();
sourceRange.copyTo(
targetSheet.getRange('A1'),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL,
false);
}
To read:
Filter#Range
Related answer
Note that .getValues() or other script operations will NOT get the filtered only, but all of the values.
Possibly
function myFilter() {
const ss = SpreadsheetApp.getActiveSpreadsheet(),
sht = ss.getSheetByName("Current"),
rng = sht.getDataRange(),
rawData = rng.getDisplayValues();
let filterValues = ["Inactive"],
col = 8, // column "H".
out = rawData.filter(dataFilter);
out = [rawData[0], ...out]; //Add headers to filtered data
function dataFilter(arr) {
return filterValues.includes(arr[col-1]);
}
const osh=SpreadsheetApp.getActive().getSheetByName("A");
osh.clear();
osh.getRange(1,1,out.length,out[0].length).setValues(out);
}