Insert row below based on the column value using google scripts - google-apps-script

I stuck with this. Seems easy at first then I got lost!
What I am aiming for:
to insert a row(s) based on a cell value (Col C) -- insert row(s) below
after a new row(s) is inserted, copy the data from previous row + string value from previous row, removing that value as well from the previous row.
I am trying to use this formula but I got lost...
function addRows(){
var ss = SpreadsheetApp.getActive();
var sheet1 = ss.getSheets()[1];
var dataRange = sheet1.getDataRange();
var dataValues = dataRange.getValues();
for (var i = 0; i<dataValues.length; i++) {
for (var j= 0; j<dataValues.length; i++) {
/* If blank or 0 - zero, skip */
if (dataValues[i][3] == "" || dataValues[i][3]== 0) {continue;}
/* If value is >=1, insert new row(s) below the active row */
if (dataValues[i][3] >=1) {
sheet1.insertRowAfter(i);
sheet1.getRange(...) // copy the data from previous + string
}
}
}
}

In your shared Spreadsheet, you want to convert from the values of "A2:B3" to the values of "A6:B11".
In your shared image, you want to achieve as follows.
From
Data1 A0HD, B0DP
Data2 C12X, D0B1, E2C1, F6H1
To
Data1 A0HD
Data1 B0DP
Data2 C12X
Data2 D0B1
Data2 E2C1
Data2 F6H1
You want to achieve this using Google Apps Script.
I could understand like above. If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Sample script:
In order to test the script, please use the following script to your shared Spreadsheet. And run the script.
function myFunction() {
var sourceSheetName = "Sheet6";
var destinationSheetName = "Sheet6";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheetName);
// Retrieve source values.
var values = sheet.getRange("A2:B3").getValues();
// Convert values.
var result = values.reduce(function(ar, [a, b]) {
var temp = b.split(",");
for (var i = 0; i < temp.length; i++) {
ar.push([a, temp[i].trim()]);
}
return ar;
}, []);
// Put values.
var dstSheet = ss.getSheetByName(destinationSheetName);
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, result.length, result[0].length).setValues(result);
}
In above script, when the script is run, the values of "A2:B3" from the "Sheet6" are retrieved and the converted values are put to the last row of "Sheet6".
If only input values are put to the source sheet, you can also use var values = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues(); instead of var values = sheet.getRange("A2:B3").getValues();.
Note:
This is a simple sample script. So please modify this for your actual Spreadsheet.
Reference:
reduce()

Related

Compare two columns for matches and pull the adjacent value for each match

I'm trying to compare the values from two columns on two different sheets (Column G on Sheet A and Column J on Sheet B). For each match, I want the script to copy the value from Column N of Sheet A to Column K of Sheet B.
I've spent quite a few days trying to figure it out and can't seem to get it right. Here is some of the code that I've tried -
function CopyTCEmailImportV6() {
// gets spreadsheet A and the range of data
var ssA = SpreadsheetApp.getActiveSpreadsheet();
var sheetA = ssA.getSheetByName('(Test) E-mail List');
var dataA = sheetA.getRange('G:N').getValues();
// gets spreadsheet B and the range of data
var sheetB = ssA.getSheetByName('Ticket Counts (Master)');
var dataB = sheetB.getRange('J:K').getValues();
// loops through column A of spreadsheet A & B and compares
for(var i = 0; i < sheetA.getLastRow(); i++){
if (dataA[i][0] === dataB[i][0]){
values.push([dataA[i][7]]);
} else {
values.push([""]);
}
}
sheetB.getRange(1, 11, values.length,
values[0].length).setValues(values);
};
Any help is greatly appreciated!
Sheet A - I'd like match the value in Column G with sheet B, and if there's a match copy the corresponding value in Column N
The Column G values from Sheet A should be matched to the Column J values of Sheet B and if there's a match paste the copied values onto the same row in column K. In this case, "25" should be copied into cell K11.
Here's the finished code in case it can help someone out down the line -
function CopyTCEmailImportV6() {
// gets spreadsheet A and the range of data
var ssA = SpreadsheetApp.getActiveSpreadsheet();
var sheetA = ssA.getSheetByName('(Test) E-mail List');
var dataAa = sheetA.getRange('G2:N' + sheetA.getLastRow()).getValues(); // Modified
var dataA = dataAa.reduce(function(ar, n) {
if (n[7]) ar.push(n)
return ar;
}, []);
// gets spreadsheet B and the range of data
var sheetB = ssA.getSheetByName('Ticket Counts (Master)');
var dataB = sheetB.getRange('J2:K' + sheetB.getLastRow()).getValues(); // Modified
// Below script was also added.
var obj = dataA.reduce(function(o, e) {
o[e[0]] = e[7];
return o;
}, {})
var values = dataB.map(function(e, i) {return e[0] in obj ? [obj[e[0]]] : [e[1]]});
sheetB.getRange(2, 11, values.length, values[0].length).setValues(values);
};
You want to compare the values of the column "G" on the sheet A (Test) E-mail List and column "J" on the sheet B Ticket Counts (Master).
The row numbers that you want to compare are the same between the column "G" on the sheet A and the column "J" on the sheet B.
When the values of the column "G" on the sheet A and column "J" on the sheet B are the same, you want to copy from the values of column "N" on the sheet A to the column "K" on the sheet B.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this modification?
Modification points:
Please modify from for (var i = 0; i > sheetA.getLastRow(); i++) { to for (var i = 0; i < sheetA.getLastRow(); i++) {.
In this case, the for loop doesn't work.
Please modify from if (dataA[1][i] === dataB[1][i]){ to if (dataA[i][0] == dataB[i][0]) {.
For example, in your case, the value of the cell "G1" and "G2" can be retrieved by dataA[0][0] and dataA[1][0], respectively.
When the array for putting to Spreadsheet is created and put the array using setValues(), the process cost can be reduced than that of the method that setValue() is used in the for loop.
Modified script:
When your script is modified, please modify as follows.
From:
for(var i = 0; i > sheetA.getLastRow(); i++){
if (dataA[1][i] === dataB[1][i]){
var value = sheetA.getRange(i+1, 8).getValue();
array is 0
sheetB.getRange(i+1, 2).setValue(value);
} // end if;
}; // end i
};
To:
var values = [];
for (var i = 0; i < sheetA.getLastRow(); i++) {
if (dataA[i][0] == dataB[i][0]) {
values.push([dataA[i][7]]);
} else {
values.push([""]);
}
}
sheetB.getRange(1, 11, values.length, values[0].length).setValues(values);
or
var values = dataA.map(function(e, i) {return e[0] === dataB[i][0] ? [dataA[i][7]] : [""]});
sheetB.getRange(1, 11, values.length, values[0].length).setValues(values);
References:
setValues(values)
Loops and iteration
Benchmark: Loop for Array Processing using Google Apps Script
If I misunderstood your question and this was not the result you want, I apologize.
Edit:
From your shared images, I could know that the values of the column "G" on the sheet A are randomly arranged. And also, it seems that each value of the column "G" is only one in the column "G". By this, I modified above script.
Modified script:
function CopyTCEmailImportV6() {
// gets spreadsheet A and the range of data
var ssA = SpreadsheetApp.getActiveSpreadsheet();
var sheetA = ssA.getSheetByName('(Test) E-mail List');
var dataA = sheetA.getRange('G2:N' + sheetA.getLastRow()).getValues(); // Modified
// gets spreadsheet B and the range of data
var sheetB = ssA.getSheetByName('Ticket Counts (Master)');
var dataB = sheetB.getRange('J2:K' + sheetB.getLastRow()).getValues(); // Modified
// Below script was also added.
var obj = dataA.reduce(function(o, e) {
o[e[0]] = e[7];
return o;
}, {})
var values = dataB.map(function(e, i) {return e[0] in obj ? [obj[e[0]]] : [""]});
sheetB.getRange(2, 11, values.length, values[0].length).setValues(values);
};
Your for loop seems to be bad,
for(var i = 0; i > sheetA.getLastRow(); i++){
The > should be a <

Google Script insert formula in 1 column where cell are black

Hi could someone help me with this script im trying to insert a formula in all the blank cell from (J15) to (J38) here is what ive got but I just cant seem to get it the proper way any help would be greatly appreciated.
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
for(var i=0;i<data.length;i++) //this runs over entire selected range
{
if(!data[i][0]) //If true then it's blank
{
data[i](('=Iferror(If(G15="",, if($B$5 = Iferror(query(\'Client
List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D15,Config_Rate_List
,2,False))),"")');)
}
}
range.setFormula(data); //Sets all data.
}
You want to put the formula to "J15:J38". If my understanding is correct, how about this modification?
I thought that it is possible that the cells of "J15:J38" have the values and formulas. So in this modified script, the formula is put to the empty cells which don't have both. The flow of script is as follows.
Retrieve values and formulas from "J15:J38".
Create range list.
Put the formula using setFormula().
I think that there are several solutions for your situation. So please think of this as one of them.
Modified script :
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
// The following script was modified.
var formulas = range.getFormulas();
var rangeList = [];
var offset = 15;
for(var i=0;i<data.length;i++) {
if(!data[i][0] && !formulas[i][0]) {
rangeList.push("J" + (offset + i));
}
}
var formula = '=Iferror(If(G15="",, if($B$5 = Iferror(query(\'Client List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D15,Config_Rate_List,2,False))),"")';
ss.getRangeList(rangeList).setFormula(formula);
}
Note :
I was not sure whether the sheet name is Client List or ClientList, because of the line break in your script.
I'm not sure about the formula which was put to cells.
About your title, "black" of "Google Script insert formula in 1 column where cell are black" is "blank"?
Reference :
getRangeList(a1Notations)
If this was not what you want, I'm sorry.
Edit :
Unfortunately, the formulas in an array cannot be put the cells using the range list yet. I think that Sheets API can put various formulas once. But as a simple way, here, I would like to propose the following script.
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
// The following script was modified.
var formulas = range.getFormulas();
var val = [];
var offset = 15;
for(var i=0;i<data.length;i++) {
if(!data[i][0] && !formulas[i][0]) {
ss.getRange("J" + (offset + i)).setFormula('=Iferror(If(G' + (offset + i) + '="",, if($B$5 = Iferror(query(\'Client List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D' + (offset + i) + ',Config_Rate_List,2,False))),"")');
}
}
}

Deleting rows in google sheets using Google Apps Script

I encountered the weirdest error while trying to delete rows that match a specific value using Google Apps Script.
Here is my Code:
function myFunction() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getSheetByName("file.csv");
var values = sheet.getRange("N2:N").getValues();
var row_del = new Array();
for(var i=0;i<values.length;i++)
{
if(values[i] == 'del'){
row_del.push(i+2); // This line was added for debugging purposes.
// sheet.deleteRow(i+2) was the line that was in this condition
// (i+2) is used because row 1 has headers and the range starts from 0.
}
}
// Logger.log(row_del);
// GmailApp.sendEmail("my_email_address", "subject", row_del)
for (var i = 0; i < row_del.length; i++)
{
sheet.deleteRow(row_del[i]);
}
}
The code that I have written picks up the row numbers that should be deleted but not all these rows are deleted in my first try. I should execute my script a number of times for these rows to be deleted.
If my code has an error, it should show up and if the logic is wrong, incorrect rows must be deleted. I encounter neither of these scenarios and I should just execute this function multiple times.
Is there something that I'm missing here?
When a row is deleted from a sheet, the rows below it get renumbered even as the script continues to run. If the script subsequently tries to also delete those rows, the result is unpredictable. For this reason, when deleting rows one should proceed from bottom to top. In your case, like so:
for (var i = row_del.length - 1; i>=0; i--) {
sheet.deleteRow(row_del[i]);
}
refer to lock function suggested by google team:
var lock = LockService.getScriptLock();
lock.waitLock(30000); // lock 30 seconds
//do whatever you want here
lock.releaseLock();
That way, you got your deleting job work once at a time! The system thread won't go on to other jobs until 30 seconds is up or releasing the lock.
google dev-document: https://developers.google.com/apps-script/reference/lock/lock
To delete blank rows from a single named sheet, assuming column 1 has data in valid rows.
Search and delete from highest row number to lowest row number.
// Deletes any row whose first column is blank
// WARNING: Assumes any valid row has data in column 1
function deleteBlankRows() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getSheetByName("Sheet3");
var lastRow = SpreadsheetApp.getActiveSheet().getLastRow();
for (var i = lastRow; i > 0; i--) {
var range = sheet.getRange(i,1);
var data = range.getValue();
if (data == '') {
sheet.deleteRow(i);
}
}
}
increment i only if you didn't delete a row
function del_F_rows(){
var i=1;
while(!sht_balanceHistory.getRange(i,1).isBlank()){
if(sht_balanceHistory.getRange(i,3).getValue()=="F")
sht_balanceHistory.deleteRow(i);
else
i=i+1;
}
}
You can just delete the rows alter the last row using the deleteRows function like this:
var maxRows = newsheet.getMaxRows();
var lastRow = newsheet.getLastRow();
if (maxRows-lastRow != 0)
{
newsheet.deleteRows(lastRow+1, maxRows-lastRow);
}
Update 2020
A faster and a more modern JavaScript approach would be to use forEach and reverse() to iterate backwards.
It makes more sense to flatten the values array since it concerns data of a single column.
Solution:
function myFunction() {
const doc = SpreadsheetApp.getActiveSpreadsheet();
const sheet = doc.getSheetByName("file.csv");
const values = sheet.getRange("N2:N").getValues().flat();
values.reverse().forEach((r,i)=>{
if (r=='del'){
sheet.deleteRow(values.length-i+1);
}
});
}
So, is this what it should look like?
function myFunction() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getSheetByName("Sheet1");
var values = sheet.getRange("A:A").getValues();
var row_del = new Array();
for(var i=0;i<values.length;i++)
{
if(values[i] == 'N'){
row_del.push(i+2); // This line was added for debugging purposes.
// sheet.deleteRow(i+2) was the line that was in this condition
// (i+2) is used because row 1 has headers and the range starts from 0.
}
}
// Logger.log(row_del);
// GmailApp.sendEmail("my_email_address", "subject", row_del)
for (var i = row_del.length - 1; i>=0; i--) { sheet.deleteRow(row_del[i]); }
}
Copy pasting from: https://gist.github.com/dDondero/285f8fd557c07e07af0e
Instead of looping through the rows twice, you can count how mwny rows have been deleted, to calculate the correct index for the row that you will delete next.
function deleteRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.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[0] == 'delete' || row[0] == '') { // This searches all cells in columns A (change to row[1] for columns B and so on) and deletes row if cell is empty or has value 'delete'.
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
};
This is a script I used to delete everything but certain values, but can be easiliy modified to keep certain strings and runs a lot faster than looping through all the data filters out and deletes the values you want to remove and bulk deletes. my data had about 10000 rows so loop would have taken forever.
function DeleteCertainRows(){
columnCopy("D","Z","YourSheetName");//Copy from your target column in this case D to another column in this case Z
replaceInSheet("Z","YourSheetName","keep String 1","keep");//find and replace the value with what you want to keep
replaceInSheet("Z","YourSheetName","keep String 2","keep");//Can repeat for additional values
DeleteValueInColumn("Z","YourSheet","keep");//filters and deletes all other values Column is case sensitive and cant go past Z
};
function replaceInSheet(repColumn,sheetname, to_replace, replace_with) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var values = sheet.getRange(repColumn+'1:'+repColumn+sheet.getMaxRows());
var textFinder = values.createTextFinder(to_replace);
var replaceall = textFinder.replaceAllWith(replace_with);
};
function columnCopy(copyfrm,copyto,sheetname){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var copyrange = sheet.getRange(copyfrm+'1:'+copyfrm+sheet.getMaxRows());
var pasterange = sheet.getRange(copyto+'1:'+copyto+sheet.getMaxRows());
copyrange.copyTo(pasterange);
};
function DeleteValueInColumn(colStr, sheetname, deleteval){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var filterRange=sheet.getDataRange();
var myFilter = filterRange.createFilter();
var CriteriaBuild = SpreadsheetApp.newFilterCriteria();
var Criteria = CriteriaBuild.whenTextDoesNotContain(deleteval);//change to whenTextContains to delete your string value instead of everything else
var myCriteria = Criteria.build();
var str = colStr;
var myCol = parseInt(str.charCodeAt(0) - 64);
Logger.log(myCol);
myFilter.setColumnFilterCriteria(myCol, myCriteria);
var deleterange=sheet.getRange('2:'+sheet.getLastRow());
sheet.deleteRows(deleterange.getRow(), deleterange.getNumRows());
myFilter.remove();
};
Another method is to sort records and delete all at once.
My following code will sort the data with N/A value and will delete all rows at once.
function DeleteErrorLine2() {
var SS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ORDER SHEET"); //Get Open Lines Sheet
var lastRow = SS.getLastRow();
var range = SS.getRange(2, 1, lastRow-1, 33); //get range
range.sort({column: 2, ascending: false}) // filter data descending
var cell = SS.getRange('B1'); // values stored in cell B
var ct = 1; // starting row
while(cell.offset(ct, 0).getValue() == "#N/A" ) {
ct++;
}
if(ct!=1){
ct = ct - 1 // minus 1 to get the last row
SS.deleteRows(2, ct)
}
range.sort({column: 2, ascending: true}) // filter data again ascending
}

Implement vlookup between two ranges in Google Apps Script

I'm looking to find a way to write a script that behaves similarly to a vLookup in google sheets.
Here is a link to a simplified example document.
What I am trying to do is use "Sheet1" as a form of sorts. When I enter information in "Sheet1" column B I want to be able to hit a custom menu button and have the information in "Sheet1" column B automatically populate correspondingly based of the values in column A into the first empty column on "Sheet 2".
I can write the script to create the custom menu and execute the function but I'm unsure of how to write the function itself.
Here is how such a function can look like. It gets pointers to each sheet, then the appropriate range from each: columns A and B, ignoring empty rows at the bottom. Then gets the values and begins comparing them: when columns A match, column B is assigned to. The final line puts the modified array values back into Sheet2.
function vl() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var sheet2 = ss.getSheetByName('Sheet2');
var range1 = sheet1.getRange(1, 1, sheet1.getLastRow(), 2);
var range2 = sheet2.getRange(1, 1, sheet2.getLastRow(), 2);
var values1 = range1.getValues();
var values2 = range2.getValues();
for (var i = 0; i < values1.length; i++) {
for (var j = 0; j < values2.length; j++) {
if (values1[i][0] === values2[i][0]) {
values2[i][1] = values1[i][0];
}
}
}
range2.setValues(values2);
}

Simple Google Spreadsheet Script to copy and paste cells gives error

I have some relatively simple code below that throws an error: : "Cannot convert NaN to (class)".
All I want to do is copy some cells from one place to another!
Can anyone please let me know what is wrong with this code?
Many thanks in advance.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
for( var row = values.length -1; row >= 0; --row )
if (values[row][5] == 'Here')
var maxRows = sheet.getMaxRows()
var startRow = (row)+1
var numRows = (maxRows)-(row)
var Range = sheet.getRange(startRow, 3, numRows, 3).getValues()
sheet.getRange(row, 3, numRows, 3).setValues(Range) // (row, column, numRows, numColumns)
}
So as the code hopefully shows, I want to copy cells in the range C:E but only rows x to getMaxRows(), where x is the row number where 'Here' is found in column F, plus 1. Then I want to paste this into the same columns C:E but one row higher than originally (into the same row as 'Here' in column F).
Any help would be very much appreciated. Thanks for looking.
EDIT: The error is on this line :
var Range = sheet.getRange(startRow, 3, numRows, 3).getValues()
In my sheet, the cells to be copied could countain blank cells and even entire blank rows. Could this be causing the issue?
Starting from your description instead of your code (why don't you use {} in loops and conditions ?) I suggest you try this
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
var maxRows = sheet.getLastRow()
var datatoCopy = []
for( var row = values.length -1; row >= 0; --row ){
if (values[row][5] == 'here'){
var whereToCopy = row+1
Logger.log(whereToCopy);
break
}
}
for(row=whereToCopy-1;row<maxRows;++row){
datatoCopy.push([values[row][2]+'**']);// store column data in an array - remove the ** that I used to see what was copied ;-)
}
Logger.log(datatoCopy)
sheet.getRange(whereToCopy, 5, datatoCopy.length, 1).setValues(datatoCopy);// overwrite data to column E
}
Following your comment, try this version ?
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
var maxRows = sheet.getLastRow()
var datatoCopy = []
for( var row = values.length -1; row >= 0; --row ){
if (values[row][5] == 'here'){
var whereToCopy = row
Logger.log(whereToCopy);
break
}
}
for(row=whereToCopy-1;row<maxRows-1;++row){
var rowData=[]
rowData.push(values[row+1][2]+'*C*');// I added these 'indicators' to show what happens... delete them when the result is ok ;-)
rowData.push(values[row+1][3]+'*D*');//
rowData.push(values[row+1][4]+'*E*');//
datatoCopy.push(rowData);// store column data in an array - remove the ** that I used to see what was copied ;-)
}
Logger.log(datatoCopy)
sheet.getRange(whereToCopy, 3, datatoCopy.length, datatoCopy[0].length).setValues(datatoCopy);// overwrite data to column E
}