google app script compare value to array values - javascript

I've got this working code:
function hUpdate() {
var ss = SpreadsheetApp.getActiveSheet();
var hRowNum = ss.getLastRow();
var hNew = ss.getRange(hRowNum,3).getValue(); // value being compared
var hCompare = ss.getRange(hRowNum-1,3).getValue();
if (hNew == hCompare)
{hNew = 'same';}
else
{hNew = 'different';}
return hNew;
}
What I really want to do is compare hNew with all values in previous rows of the same column. I know I have to use an array but I'm stuck with the actual coding.

Related

Google Apps Script is taking too much time, is there a way to reduce the runtime?

function dataManp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("My-Sheet-1");
var pasteSheet = ss.getSheetByName("My-Sheet-2");
var clearContentRange = pasteSheet.getRange("A1:Z100");
clearContentRange.clearContent();
var source = copySheet.getRange("a1:f100");
var destination = pasteSheet.getRange("a1:f100");
source.copyTo(destination, {formatOnly:true , contentsOnly:true});
source.copyTo(destination,SpreadsheetApp.CopyPasteType.PASTE_COLUMN_WIDTHS,false);
var rows = pasteSheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[3] == '') {
var deleteRowNum = (parseInt(i)+1) - rowsDeleted
pasteSheet.deleteRow(deleteRowNum);
// temp_array[i] = i
rowsDeleted++;
}
}
pasteSheet.deleteColumn(2)
}
Hi,
I have written the following script to perform the following operations,
Copy data from My-Sheet-1 and paste to My-Sheet-2
Deletion of rows that corresponds to empty cells in column 3.
After that deletion of column 2
Rigthnow, the My-Sheet-1 contains only 60 rows and 20, the script is taking approximately 7 secs to complete. However in future the number of rows may extend to some 1000. Is there a way to optimize the above code, so that it takes less time to complete.
My observation is that, copy and pasting the data takes just milli secs. The major time consuming part are the operations, I am performing in the pasteSheet after pasting it. It may be helpful, if we can copy My-Sheet-1 to a temporary variable (copy everything including the formulas, format specifications, values, text etc..) and perform all operations in the temporary variable and then paste everything in the temporary variable to the desired target sheet. But, I don't know, how to copy everything in a sheet to a temporary variable, also, I am not sure, whether this will reduce the time or not. I would be glad, if I can get some help on this as well (i.e. copying everything in a sheet to a temporary variable, perfrom operations on the variables and then paste data in the variable to a new sheet)
Thank you
Edit - 1
Would like to add that, My-Sheet-1 contains mixed data (i.e. numerics, color formatted text, formulas in some cells etc)
Explanation:
deleteRow() takes some time per execution, so it's not recommended to use on hundreds of rows in a loop.
Simple answer would be:
Make a 2D array for Sheet1 using getValues().
Delete / filter out array elements depending if row2 is blank.
Use setValues() to write the filtered array into Sheet2.
Sample Code:
function dataManp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("My-Sheet-1");
var pasteSheet = ss.getSheetByName("My-Sheet-2");
var lr = copySheet.getLastRow();
var clearContentRange = pasteSheet.getRange(1,1,lr,26);
clearContentRange.clearContent();
var source = copySheet.getRange(1,1,lr,6);
var destination = pasteSheet.getRange(1,1,lr,6);
source.copyTo(destination, {formatOnly:true , contentsOnly:true});
source.copyTo(destination,SpreadsheetApp.CopyPasteType.PASTE_COLUMN_WIDTHS,false);
destination.clearContent();
var values = source.getValues();
var temp_array = [];
for (var i = 0; i < lr; i++) {
var rowValue = values[i];
if (rowValue[2] != '') {
temp_array.push(rowValue);
}
}
var newDest = pasteSheet.getRange(1,1,temp_array.length,6)
newDest.setValues(temp_array);
pasteSheet.deleteColumn(2);
}
One caveat is that you need to have the same format for all rows in a column.
Sample Input:
Sample Output:

Google Script GetRange with Filter based on values in a column AND select only certain columns

I have a dataset of 35 columns and 300 rows. I want to get the range that contains rows only for certain values in column 30 (names). The name for which to filter the data is based on the report file cell B6 in the report sheet that is active. So far I tried this:
var report = SpreadsheetApp.getActiveSpreadsheet();
var tsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getValues();
var criteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo(agent).build();
var trange = tsheet.getRange().createFilter().setColumnFilterCriteria(30, criteria); // ERROR
var tdata = trange.getValues();
I receive an error Exception: The parameters () don't match the method signature for SpreadsheetApp.Sheet.getRange.
The second part, I only want to get several columns, 5,6,7, 13, 15. I can't create another filter with the Spreadsheet app, so is the only way to make an array and filter out the needed data from there? I'm just trying to think ahead and reduce the amount of calculations.
Try with filter():
var report = SpreadsheetApp.getActiveSpreadsheet();
var tsheet = report.getSheetByName("Transactions");
var areport = SpreadsheetApp.getActiveSheet();
var agent = areport.getRange('B6').getValue();
var data = tsheet.getRange('A1:AI300').getValues();
var tdata = data.filter(function (row) {
return row[29] == agent && row[5] == 'Closed' ; // starts from 0, column A is 0.
});
To select particular columns from tdata do:
var cr_data = getCols(tdata,[5,6,7, 13, 15]);
where getCols() is defined as follows:
function getCols(arr,cols) {
return arr.map(row =>
row.filter((_,i) => cols.includes(++i)))
}
and finally you can copy cr_data to a particular place/sheet like that:
sheet.getRange(1,1,cr_data.length,cr_data[0].length).setValues(cr_data);
Regarding the second part of your question I would like to redirect you to this post:
Best method to extract selected columns from 2d array in apps script

How to set values in new column with validation from a Named Range?

Following a previous question
I want to classify text entries by adding a tag in the next column.
I could do it using regex but it will take too much time writing all conditions like :
if(String(data[i][0]).match(/acme|brooshire|dillons|target|heb|costco/gi))
{
labValues[i][0]='Supermarket';
}
Instead I created a named list with all stores names (in another sheet).
If an entry matches a term in the list, the next column is set to "Supermarket".
I am using this script below... No bugs but nothing happens when executed !
function tagStore() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A2:A655')
var store = range.getValues();
var tag = sheet.getRange('B2:B655');
var tagvalues= tag.getValues();
var storeList= SpreadsheetApp.getActive().getRangeByName("store_list");
for (var i = 0; i<store.length; i++)
{
if(String(store[i][0]).match(storeList))
{
tagvalues[i][0]='Supermarket';
}
}
tag.setValues(tagvalues);
}
Edit:
It is important to use a Regex as the "store" Values are not exactly the same as the "store_list".
Store Values : ["Acme Store", "HEB PLaza", "Dillons Group"...]
Store_List : [acme, heb, dillons...]
Instead of trying to go with the regEx approach there is a more straightforward approach by retrieving the range as a list.
// For a Column
var storeList = SpreadsheetApp.getActive().getRangeByName("store_list").getValues().map(function(r){return r[0];});
// For a Row
var storeList = SpreadsheetApp.getActive().getRangeByName("store_list").getValues()[0];
And then look if the values you are looking for are in this list with indexOf().
Try this:
function tagStore() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A2:A655')
var store = range.getValues();
var tag = sheet.getRange('B2:B655');
var tagvalues= tag.getValues();
var storeList= SpreadsheetApp.getActive().getRangeByName("store_list").getValues().map(function(r){return r[0];});//if it's a column
//var storeList=SpreadsheetApp.getActive().getRangeByName("store_list").getValues()[0];//if it's a row
for (var i=0;i<store.length; i++) {
if(storeList.indexOf(store[i][0])!=-1) {
tagvalues[i][0]='Supermarket';
}
}
tag.setValues(tagvalues);
}

Writing Sorted Table Data to the Table in a Separate Format using Google Sheets

I'm relatively new to javascript and definitely new to google scripting.
I have a tab where raw data entry happens. I have another tab where that data is sorted, and an approval date is written next to that line later. I want that date to reflect back to the first tab in the proper line, and I also want to make sure that it stays on the proper line in the second tab (even after new lines are added and sorted in).
Attached is an example sheet with only a few lines. In reality I have more columns but they are irrelevant here.
https://docs.google.com/spreadsheets/d/14dh0IW7vO8c2OLc2O8WE-Ptuh3MJfLWTESVMA_DpOv0/edit?usp=sharing
My second tab uses the sheets SORT function. When A date is typed though, it creates an error. I was going to account for this by using onEdit() function and writing the date into the correct line in the first tab, then clearing the date column in the second tab (to avoid the error and keep it aligned).
''javascript
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = ss.getSheetByName("check released");
var sheet1 = ss.getSheetByName("Log");
var job2 = sheet2.getRange("A2:A1000").getValues();
var job1 = sheet1.getRange("A2:A1000").getValues();
var draw2 = sheet2.getRange("B2:B1000").getValues();
var draw1 = sheet1.getRange("B2:B1000").getValues();
var inv2 = sheet2.getRange("C2:C1000").getValues();
var inv1 = sheet1.getRange("C2:C1000").getValues();
var dates2 = sheet2.getRange("D2:D1000");
var dates_values = sheet2.getRange("D2:D1000").getValues();
var dates1 = sheet1.getRange("D2:D1000");
var indices = []
var to_paste = []
for(var i = 0; i < job2.length; i++) {
var job2_Value = job2[i][0];
var draw2_Value = draw2[i][0];
var inv2_Value = inv2[i][0];
for(var j=0; j<job2.length; j++) {
var job1_Value = job1[j][0];
var draw1_Value = draw1[j][0];
var inv1_Value = inv1[j][0];
if((job2_Value != "") && (job1_Value == job2_Value) && (draw1_Value === draw2_Value) && (inv1_Value === inv2_Value)) {
indices.push([i]);
}
}
}
for(k in indices) {
to_paste.push([dates2[k]])
}
Logger.log(to_paste)
dates1.setValues(to_paste)
dates2.clearContent()
Logger.log("Cleared on check released page")
};
'''
This is the code that I've written, but it doesn't work and I don't know why. I also don't know where to find the console log.
You have a sheet "check released" that takes values from "sheet "Log". Your aim is enter a "release date" on "check released" and have that updated to "Log", and then reflected in "check released". The approach you took was to edit the actual linked "release date" value on "check released". Not surprisingly this wasn't successful because it was merely taking date from "Log".
In the following code:
I have used a helper column on "check released" where the date can be entered.
The onEdit(e) script detects the new value, finds the equivalent row on "Log".
The script updates the "Date" column on "Log" - at this point, the values on "check released" are automatically updated.
Then the script deletes the value in the helper column.
Matching from "Log" to "check released"
The matching of rows from "check released" to "Log" relies on several elements:
No record has a truly unique identifier.
However, the values of Job, Draw and Invoice numbers, when each is converted to a string and concatenated, generate a unique value that can be used to compare values from "Log" to "check released".
The values on "Log" are processed in a loop to create a 1D array of concatenated values.
Event objects provide the edited Row on "check released"; and the Job, Draw and Invoice values in that row are concatenated.
Using the Javascript indexOf method, the script 'finds' the row on "Log" that matches the unique concatenated value from the edited row.
Other items to note:
the actual size of the data range on "Log" and "check released" is determined by using getlastRow()
getRange and getValues are run once each for "Log" and "check released"
Meaningful names for variables have been chosen to reflect their purpose; this assists in reading and understanding the code.
function onEdit(e) {
// 5824330602
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var logsheet = "Log";
var log = ss.getSheetByName(logsheet);
var checkrsheet = "check released";
var checkr = ss.getSheetByName(checkrsheet);
// get Log data
var logFR = 2;
var logLR = log.getLastRow();
var logLC = 4;
var logRange = log.getRange(logFR,1,logLR-logFR+1,logLC);
// Logger.log(logRange.getA1Notation());// DEBUG
var logValues = logRange.getValues();
// get check released data
var checkrRange = checkr.getRange(logFR,1,logLR-logFR+1,logLC);
//Logger.log(checkrRange.getA1Notation());
var checkrValues = checkrRange.getValues();
// build array of uniquelogitems
var logitems=[];
for (var i=0; i<logValues.length; i++) {
var logjob = logValues[i][0].toString();
var logdraw = logValues[i][1].toString();
var loginv = logValues[i][2].toString();
var logid = logjob+logdraw+loginv;
//Logger.log("DEBUG: LOG: Job= "+logjob);
//Logger.log("DEBUG: LOG: draw= "+logdraw);
//Logger.log("DEBUG: LOG: inv= "+loginv);
//Logger.log("DEBUG: LOG: concat= "+logid);
var logid = logjob+logdraw+loginv;
logitems.push(logid);
}
//Logger.log(logitems); //DEBUG
// get the event objects
var editedRow = e.range.getRow();
var editedCol = e.range.getColumn();
var editedSheet = e.range.getSheet().getSheetName();
var editedValue = e.value;
// Logger.log("DEBUG: row = "+editedRow+", column = "+editedCol+", Sheet = "+editedSheet)
// apply logic to test whether this edit should be processed
// Column 5 ("E") is the helper column
if(editedRow >= logFR && editedRow <=logLR && editedCol === 5 && editedSheet === checkrsheet){
// the edit is in Column E (Date), between the first and last rows of data, on the "check released" sheet
//Logger.log("DEBUG: edit is OK. edit row = "+editedRow+". Keep processing");
var checkrjob = checkrValues[editedRow-2][0].toString();
var checkrdraw = checkrValues[editedRow-2][1].toString();
var checkrinv = checkrValues[editedRow-2][2].toString();
var checkritem = checkrjob+checkrdraw+checkrinv;
//Logger.log("DEBUG: Checkr: job="+checkrjob+", draw= "+checkrdraw+", inv = "+checkrinv+", Item = "+checkritem);
var match = logitems.indexOf(checkritem);
//Logger.log("DEBUG: Matching row = "+match);
// get the existing date
var existingdate = logValues[+match+1][3];
var cell = log.getRange(+match+2,4);
//Logger.log("DEBUG: the update cell = "+cell.getA1Notation())
// date field is a date, so update new date
cell.setValue(editedValue);
cell.setNumberFormat('mm/dd/yy');
e.range.clearContent();
//Logger.log("DEBUG: updated date on Log")
}
else{
// the edit didn't meet the rule
//Logger.log("DEBUG: edit did NOT meet criteria. Do not proceed")
}
};
Column E - Helper Column

Get values by specific column name - App Script

I'm trying to get values ​​from a specific column using the method getRange and specifying the name of the column using this example that I saw of the following question ... StackOverflow
When I combine it with my code, I got something like this...
function hola (){
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var ssh1 = ss1.getSheetByName("Sheet 1");
var lsc = ssh1.getLastColumn();
var data = ssh1.getRange(1,1,1,lsc).getValues();//Get 2D array of all values in row one
data = data[0];
var name = data.indexOf('Names') + 1;//Arrays are zero indexed- add 1
Logger.log(name);
var lastRow = ssh1.getLastRow();
var getRange = ssh1.getRange(name +(lastRow)).getValue();
Logger.log(getRange);
}
I'm not sure I'm wrong, I hope someone can guide me better :D

Categories