Copy rows after loop - javascript

after my last question I'm facing a problem with copying rows.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName('ws1');
var startRow = 4;
var lastRow = sheet.getLastRow();
var numRows = lastRow - startRow + 1;
var lastCol = sheet.getLastColumn();
var dataSetValues = sheet.getRange(startRow, 1, numRows, lastCol).getValues();
for (var i = 0; i < numRows; i++){
let fVal = dataSetValues[i][5];
let gVal = dataSetValues[i][6];
let sum = +fVal + +gVal;
if (sum > 115) {
let row = dataSetValues[i];
}
}
What do I expect?
I wish set which columns to copy
I edited the code like this
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName('ws1');
var startRow = 4;
var lastRow = sheet.getLastRow();
var numRows = lastRow - startRow + 1;
var lastCol = sheet.getLastColumn();
var dataSetValues = sheet.getRange(startRow, 1, numRows, lastCol).getValues();
for (var i = 0; i < numRows; i++){
let aVal = dataSetValues[i][0];
let bVal = dataSetValues[i][1]; // + other columns
let fVal = dataSetValues[i][5];
let gVal = dataSetValues[i][6];
let sum = +fVal + +gVal;
if (sum > 115) {
let row = dataSetValues[i];
var ssDest = spreadsheet.getSheetByName('ws2');
var rngDest = ssDest.getRange(ssDest.getLastRow()+1,1);
//start copy
rngDest.setValues(row)
}
}
I get this error
The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues
Thanks

Your script just needs a few changes made to it:
1. It is important to note that the setValues() method accepts as parameter a two dimensional array in the form of Object[][].
You are simply passing it a one-dimensional array, hence the The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues error you are receiving.
In order to fix this, you will have to transform row into a 2 dimensional array and making the following changes
From
rngDest.setValues(row)
To
rngDest.setValues([row])
2. You will have to specify exactly the number of rows and the number of columns expected in the destination range.
After making the change above, you will end up running into a The number of columns in the data does not match the number of columns in the range error which is again expected. This is due to the fact that the getRange method will also need the number of rows and the number of columns such that when using setValues it will know exactly the structure of the data to set.
If you take a look at the getRange method:
getRange(row, column, numRows, numColumns)
Returns the range with the top left cell at the given coordinates with the given number of rows and columns.
In order to fix this, a simple change has to be made in order to indicate exactly the number of rows and the number of columns:
From
var rngDest = ssDest.getRange(ssDest.getLastRow()+1,1)
To
var rngDest = ssDest.getRange(ssDest.getLastRow() + 1, 1, 1, row.length);
As you can see, the number of rows here is 1 (as you are copying the data one row at a time) and the number of columns is equal to row.length (as the row variable has all the values corresponding to one row at a time).
Reference
Apps Script Range Class - setValues();
Apps Script Sheet Class - getRange();
Apps Script Troubleshooting.

Related

How to clear/delete data after a given row index in Google Spreadsheets

I would like to delete all data after row [x]. For example if the x = 355, I want all data to be deleted after 355th row.
I am able to delete all content in a simple sheet in google spreadsheet with:
var sheet = ss.getSheetByName('foo');
sheet.clearContents();
I've tried to do it with getRange method:
var range = SpreadsheetApp
.getActive()
.getSheetByName("foo")
.getRange("A20:E71");
range.clearContent();
but this is simply deleting inside data from A20:E71. What I want is to delete all data after A20th row.
Try:
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(`foo`)
sheet.getRange(21, 1, sheet.getLastRow()-20, sheet.getLastColumn())
.clearContent()
The range is defined as all rows beyond the equivalent of A20.
Function:
function myFunction(allRowsAfter) {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(`foo`)
sheet.getRange(allRowsAfter+1, 1, sheet.getLastRow()-allRowsAfter, sheet.getLastColumn())
.clearContent()
}
myFunction(20); // Delete all rows after 20
See:
getRange(row, column, numRows, numColumns)
Clear everything after row
function clearAfterRow(r) {//clear everything after row r
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("foo");
const sr = r + 1;
sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn()).clearContent();
}
You need to use getLastColumn() and getLastRow() methods.
The number of rows to be deleted depending on a row x can be calculated by lastRowIndex - x + 1. In terms of columns you can just delete all columns using 1 as a start value for the range and lastColumnIndex for the number of columns to remove.
Logger.log("Starting script...")
const ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = ss.getLastRow();
const lastCol = ss.getLastColumn();
const x = 20;
if(lastRow - x + 1 > 0) {
ss.getRange(20, 1, lastRow - x + 1, lastCol).clearContent();
Logger.log(`Deleting all content in Range (${[x, 1, lastRow - x + 1, lastCol]})`);
}
Logger.log("Done.")
However there is one thing you need to account for in case you run this multiple times or x is bigger than the last row with content in you Sheet. You need to check whether the range affects at least one row otherwise you will get an error The number of rows in the range must be at least 1., so check for lastRow - x + 1 > 0 before deletion.

Remove duplicates across multiple sheets

I want to remove duplicates across 2 different sheets.
I have my active sheet, and I want to remove duplicates that already exist in my sheet "Blacklist". I want to run this process for both Column A and Column B (or simply for any values across the entire sheets). When a duplicate is found, I want to leave the row in tact but replace the value with '' (e.g. an empty cell).
I have a working version I mangled together, but only for the active sheet.
N.B. it's the findDuplicate function that I use, the removeDuplicate function I left there not to mess anything up :)
// this is a Google Apps Script project
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{ name: 'Find duplicates...', functionName: 'findDuplicate' },
{ name: 'Remove duplicates...', functionName: 'removeDuplicate' }
];
spreadsheet.addMenu('Duplicates', menuItems);
}
function removeDuplicate() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
var duplicateData = [];
// iterate through each 'row' of the selected range
// x is
// y is
var x = 0;
var y = data.length;
// when row is
while (x < y) {
var row = data[x];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, delete the 'row' from the sheet and add it to the duplicateData array
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + x,
columnNum,
1,
columnLength
);
duplicateRange.deleteCells(SpreadsheetApp.Dimension.ROWS);
duplicateData.push(row);
// rows shift up by one when duplicate is deleted
// in effect, it skips a line
// so we need to decrement x to stay in the same line
x--;
y--;
range = sheet.getActiveRange();
data = range.getValues();
// return;
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
x++;
}
}
function findDuplicate() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
// iterate through each 'row' of the selected range
for (var i = 0; i < data.length; i++) {
var row = data[i];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, highlight the 'row' from the sheet
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + i,
columnNum,
1,
columnLength
);
duplicateRange.setValue('');
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
}
}
Thanks so much for your help! I've been at this for a few hours and figured I should just ask the experts for advice :)
The first lines of both your removeDuplicate and findDuplicate function seems indeed to indicate that you refer to the active spreadsheet / sheet / range
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
If you want to be able to use the same function for a given spreadsheet / sheet / range which is not the active one, you will need to use other functions than the getActiveXXX().
For example, to get the sheet named "Blacklist", you should use
sheet = spreadsheet.getSheetByName("Blacklist")
(see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename)
If you want to access a specific range which differs from the active range, you should use the getRange method (see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getrangea1notation)
Note that getRange method can be used in different ways, e.g.
getRange("A1:D4"), getRange(1, 1, 3, 3) (the parameters being respectively startRow, startColumn, numRows,numColumns)
Additionally, if you don't want to hardcode the last line of your 2 columns, you will most probably need this function to find the last line in the code :
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getlastrow
(there is also an example there showing how to use getRange() in combination with getLastRow()).
I hope this will help you going further.
Please note that I didn't check the rest of your code and just assumed that your deduplication logic works fine as you mentioned it in your commment.
Good luck !

Simple for loop, if statement and output message

I think this one is pretty simple, but I am new to this, so I am not sure where to go from here :)
I have a Google Sheet, with some data (pretty large sheet). I want a script that check whether the number in a cell (column I) is larger than the number in the same row, but another column (column D).
Imagine two columns with 5 rows: Column D = (3, 3, 3, 3, 3) and Column I = (2, 2, 7, 2, 2)
SO in this example, I want that the script to tell me that I have problem in "Row 3", because the number in Column I, Row 3 is larger than the number in Column D, Row 3 :)
This is what I have:
function Check() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var lr = sheet.getLastRow();
var Summary;
for (var i=6; i<lr; i++) {
if (sheet.getRange(i,9) > sheet.getRange(i,4)){
summary.setvalue("Problem")
}
}
}
I want it to start in row 6, because my data starts here. I am only checking column I (therefore 9) and column D (therefore 4)
I am not sure what to do with my for loop and If statement from here? SO now the code is checking every row in column 4 (D) and column I (9), but how do I store store the value, whenever the number in column 9 is larger than the number in column 4? And I also want an output, with all rows where we have a problem, when the code is done? If we don't have any problem, I want a message saying: "no problem"
Can anybody guide me from here?
If your output can be set in the sheet (let say in column "J"), you can use this:
function Check() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var maxRows = sheet.getMaxRows(); // get the number of rows
var startRow = 6;
var diffCol1 = 'D';
var diffCol2 = 'I';
var outputCol = 'J';
var outputStrOK = 'no problem';
var outputStrKO = 'problem';
var outputRange = []; // stored values
for (var i = startRow; i <= maxRows; i++) {
var valueA = sheet.getRange(diffCol2+i).getValue();
var valueB = sheet.getRange(diffCol1+i).getValue();
// add controls on values then
if (valueA > valueB) {
outputRange.push([outputStrKO]);
} else {
outputRange.push([outputStrOK]);
}
}
// setting a range of values is much faster than fulfilling cell by cell in loop
sheet.getRange(outputCol+startRow+':'+outputCol+maxRows).setValues(outputRange);
}
you should have this as a result:
#|D|I|J
6|9|4|problem
7|4|9|no problem

How find last line in a Google Sheet with getRange

I'm using the following script to find and replace text. The only problem is in the line:
var range = sheet.getRange("D2:D2000");
It creates new rows past my last row as it is set to 2000. I need to find the last row and then change this line of code. I'm lost :(
My total script is as follows:
function runReplaceInSheet() {
var spreadsheet = SpreadsheetApp.openById("1NK-pmvrJoSqKSgx2zRCAcRKGjk8SiiSsfwwerhfdM1SE"); // UPDATE ID
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]); // UPDATE number in square brackets
var range = sheet.getRange("D2:D2000");
// get the current data range values as an array
// Lesser calls to access the sheet, lower overhead
var startRow = 2; // First row of data to process
var numRows = 2; // UPDATE number of rows to process
// Fetch the range of cells
var dataRange = sheet.getRange(startRow, 4, numRows, 1) // Numbers of rows to process
// Fetch values for each row in the Range
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var v = row[3]; // edit: don't need this
var values = range.getValues();
// Replace Names
replaceInSheet(values, '1', 'Active');
replaceInSheet(values, '0', 'Inctive');
//write the updated values to the sheet, again less call;less overhead
range.setValues(values);
}
}
function replaceInSheet(values, to_replace, replace_with) {
//loop over the rows in the array
for (var row in values) {
//use Array.map to execute a replace call on each of the cells in the row.
var replaced_values = values[row].map(function(original_value) {
return original_value.toString().replace(to_replace, replace_with);
});
//replace the original row values with the replaced values
values[row] = replaced_values;
}
}
You can use sheet.getDataRange() to return a range that describes the area of the sheet with some data in it.
If you just want column D2:d you could do this
var range = sheet.getDataRange();
range = range.offset (1,3,range.getNumRows()-1,1);
Or you could get the data into a 1 dimensional array like this
var values = sheet.getDataRange().getValues().slice(1)
.map (function (d) {
return d[3];
});
There are many ways.

Sort ranges in an array

I have a timesheet spreadsheet for our company and I need to sort the employees by each timesheet block (15 rows by 20 columns). I have the following code which I had help with, but the array quits sorting once it comes to a block without an employee name (I would like these to be shuffled to the bottom). Another complication I am having is there are numerous formulas in these cells and when I run it as is, it removes them. I would like to keep these intact if at all possible. Here's the code:
function sortSections()
{
var activeSheet = SpreadsheetApp.getActiveSheet();
//SETTINGS
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var headerRows = 53; //number of header rows
var pageHeaderRows = 5; //page totals to top of next emp section
var sortColumn = 11; //index of column to be sorted by; 1 = column A
var pageSize = 65;
var sectionSize = 15; //number of rows in each section
var col = sortColumn-1;
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var data = sheet.getRange(headerRows+1, 1, sheet.getMaxRows()-headerRows, sheet.getLastColumn()).getValues();
var data3d = [];
var dataLength = data.length/sectionSize;
for (var i = 0; i < dataLength; i++) {
data3d[i] = data.splice(0, sectionSize);
}
data3d.sort(function(a,b){return(((a[0][col]<b[0][col])&&a[0][col])?-1:((a[0][col]>b[0][col])?1:0))});
var sortedData = [];
for (var k in data3d) {
for (var l in data3d[k]) {
sortedData.push(data3d[k][l]);
}
}
sheet.getRange(headerRows+1, 1, sortedData.length, sortedData[0].length).setValues(sortedData);
I think to solve your problems is possible to use the Range.sort function instead of the custom code. The sort function relocates also the formulas but in a tricky way - if a cell formula contains a cell reference, the sort function changes the row index in relocated cell to have the new cell row index, for instance, initially the cell C1 contains the =A1*B1 formula, after the sort operation the row 1 relocated to the row 3 and the cell 'C3' will contain not =A1*B1, but =A3*B3.
With this modification your code should looks something like this
function sortSections()
{
var activeSheet = SpreadsheetApp.getActiveSheet();
//SETTINGS
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var headerRows = 53; //number of header rows
var pageHeaderRows = 5; //page totals to top of next emp section
var sortColumn = 11; //index of column to be sorted by; 1 = column A
var pageSize = 65;
var sectionSize = 15; //number of rows in each section
var col = sortColumn-1;
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange(headerRows+1, 1, sheet.getMaxRows()-headerRows, sheet.getLastColumn());
range.sort({column: sortColumn, ascending: true});
...
}

Categories