Apps script: Paste filtered range to the next empty row in specific column - api

I would like to create a code which copies a filtered column and pastes the whole column to the next available row in another sheet. The code below works however pastes the range in a loop until the end of the spreadsheet. How can I paste it just once?
I could not find an answer myself.
I would much appreciate your help!
function CopyFilter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("MASTER");
var values = sheet.getDataRange().getValues();
Logger.log("VALUES "+values.length);
var criteria = SpreadsheetApp.newFilterCriteria()
.whenNumberGreaterThanOrEqualTo(1)
.build();
var range = sheet.getFilter()
.setColumnFilterCriteria(52, criteria)
.getRange();
range.getValues();
var sheet2 = ss.getSheetByName("DL CSV OUTPUT");
sheet.getRange("AZ4:AZ").copyTo(sheet2.getRange(2,41,sheet2.getLastRow()+1), {contentsOnly:true});
sheet.getFilter().remove();
}

Do not use the copy method. Simply select the range you are interested in, then get a range (of same size - very important) in the target sheet and set all values at once.
EXAMPLE:
const valuesToCopy = sheet.getRange("AZ4:AZ").getValues();
sheet2.getRange(2,3,valuesToCopy.length).setValues(valuesToCopy);
And no need to worry about: {contentsOnly:true}
Since the above method does not bring over any formatting.
Reference:
setValues(values)

Related

In Google sheets, using a script, can I replace all the functions in cells with the plain text or number values they've calculated?

I have a spreadsheet with multiple sheets. The first sheet is an order form where you can enter customer details and their order summary.
On another sheet, these data are copied to relevant cells in a picking slip and a tax invoice, just using preformatted cells, and simple functions, ie:
='Order Submission Form'!E9
And then that cell is populated with the Customers name.
However, I then have a script which duplicates that spreadsheet, deletes all except the sheet with the picking slip and tax invoice, then turns it into a PDF. All of that code works fine, except that deleting all the redundant sheets also breaks the cell references, and so I end up with the picking slip and tax invoice just populated with #REF! in all the cells.
This is the complete code that I'm using to generate the PDF. I know I copied at least some of it from somewhere else, but I don't remember where I originally got it.
There is a section which seems to suggest it replaces the cell values with text to avoid broken references, but it does not seem to work, and it gives an error: "Incorrect Range height: Was 59 but should be 999 (Line 22, file createPDF)"
function checkSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = "Picking Slips";
var folderID = "1gp3dqdwMSXzwQ6gzhctbQk5ODnpBOHB_"; // Folder id to save in a folder.
var orderNumber = ss.getRange("'Picking Slips'!G4").getValue()
var pdfName = "Picking Slip # "+ orderNumber + " - " + Utilities.formatDate(new Date(), "GMT+8", "yyyy-MM-dd");
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var folder = DriveApp.getFolderById(folderID);
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
//Dump the contents of the picking slip into order history
var destSheet = destSpreadsheet.getSheets()[0];
//repace cell values with text (to avoid broken references)
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
//save to pdf
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var url = 'https://docs.google.com/spreadsheets/d/'+sourceSpreadsheet.getId()+'/export?exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=A4' // paper size legal / letter / A4
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='+sourceSheet.getSheetId(); // the sheet's Id
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Any help would be greatly appreciated.
If you select the whole sheet and select copy and then paste special > paste values, only you will overwrite every formula in the sheet with its value.
You can script this as follows:
var range = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
range.copyTo(range, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);

Get data from cells in a row and populate a new row in google sheets

I'm a total noob with scripts.
The idea is to get the data from the row of one sheet and then populate the row of the second sheet once a day.
I have 2 sheets with a spreadsheet.
1. A parent list of text in column 1 and numbers in column 2.
2. A target sheet to be populated.
From what I understand I have to do this
identify the parent sheet
identify the target sheet
set a variable (R) for the row of the parent sheet to get data from
find the data in column 1 of the parent sheet on row R
find the date in column 2 of the parent sheet on row R
Go to first empty row in the target sheet
Fill column 1 of the target sheet with data from column 1 of the parent sheet
repeat for column 2
update R to R+1 so when the function is triggered the next day then it gets the data from the next row.
I think that makes sense, but haven't got a clue of how to code that.
Would looooove the help of someone out there!
I manged to do steps 1 - 5 but stuck on one aspect.
Each day I want the script to go to the next row in the parent sheet.
I tried to set up a var currentRow for this, but when I put it within or out of the function it always goes back to 1.
var currentRow = 1;
function myFunction() {
// create a variable to know what row to get the data from in the parent list
Logger.log(currentRow);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var parent = ss.getSheetByName("parent");
var range = parent.getDataRange(),
values = range.getValues();
var row = values[currentRow],
text = row[0],
video = row[1];
Logger.log(text);
Logger.log(video);
currentRow = currentRow + 1;
}
Nice I managed to work out how to do it all.
I changed the currentRow to instead figure out what is the last row number in the target sheet.
Then the script adds 1 to that and get the data from the next row of the parent sheet.
This stuff is fun!
function myFunction() {
// create a variable to know what row to get the data from in the parent list
var ss = SpreadsheetApp.getActiveSpreadsheet();
var parent = ss.getSheetByName("parent");
var range = parent.getDataRange(),
values = range.getValues(),
lastRow = parent.getLastRow();
//get the position of the last row in the populate sheet
var populate = ss.getSheetByName("populate"),
range = parent.getDataRange(),
lastPopulateRow = populate.getLastRow();
var row = values[lastPopulateRow + 1],
text = row[0],
video = row[1];
Logger.log(text);
Logger.log(video);
Logger.log(lastRow);
Logger.log(lastPopulateRow);
//append to the populate sheet
populate.appendRow([text, video]);
}

Copy relative formula in excel with VBA

I'm trying to copy formulas with relative row and column names.
I have base column with 10 formulas and I'm trying to copy formula 5 times on columns on the right.
So for example in base column i have this formula
=A1+B1 and i want to make next cell this =B1+C1 next =C1+D1 and so on.
I have this code
For i = 1 To Range("B1").Value
For j = 1 To 10
Sheet2.Cells(5 + j, 2 + i).FormulaR1C1 = "=c[-1]"
Next j
Next i
But this just copies value from left cell
It would be easy if the formulas were same but they are different. So im trying to find a way to dynamically reference always left cell.
This is one of the real formulas
='Balance sheet'!B13*360/'P&L'!B2
I want next cell to be
='Balance sheet'!C13*360/'P&L'!C2
And with code i have now next cell is =B3
If you want to copy a formula to right, that can be done with a fill operation. So with your formula in a cell defined by your base row and base column, and with it using relative addressing as in your question, merely:
Option Explicit
Sub foo()
Const lBaseColumn As Long = 3
Const lBaseRow As Long = 6
Const lRpts As Long = 10
Cells(lBaseRow, lBaseColumn).Resize(columnsize:=lRpts).FillRight
End Sub
You should note that you can FillRight from multiple rows at once, if that is what you need to do. The below would fill C6:C10 through to L6:L10
Option Explicit
Sub foo()
Const lBaseColumn As Long = 3
Const lBaseRow As Long = 6
Const lColRpts As Long = 10
Const lRows As Long = 5
Cells(lBaseRow, lBaseColumn).Resize(rowsize:=lRows, columnsize:=lColRpts).FillRight
End Sub
these two lines will copy formula / contents from left cell and copy in the reference cell. this will also copy the formula with relative reference
Range(refCellID).Offset(0,-1).Copy
Range(refCellID).PasteSpecial

Google Sheets Addon or Formula that accomplishes the same thing as the Prevsheet Macro's for Excel

I want to have a cumulative total running on each sheet (so the current total will be a value on the current sheet summed with the cumulative total from the previous sheet, each sheet representing a day of the month. In Excel this was definable in a Macro similar to this.
Function PrevSheet(rg As Range)
n = Application.Caller.Parent.Index
If n = 1 Then
PrevSheet = CVErr(xlErrRef)
ElseIf TypeName(Sheets(n - 1)) = "Chart" Then
PrevSheet = CVErr(xlErrNA)
Else
PrevSheet = Sheets(n - 1).Range(rg.Address).Value
End If
End Function
Is there anyway to do this in Google Sheets. It keeps referring back to sheet 1. Thanks
I left an editable link here which is a copy of what I am working on.
https://docs.google.com/spreadsheets/d/1cx9w9vOJXKj2WqYBoJOuZ7dWqP1VunFYpZbMLjdpkKQ/edit?usp=sharing
You can do that only using a script. Try this:
function PrevSheet(range) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var active = ss.getActiveSheet().getName();
for (i in sheets) {
if (sheets[i].getName() === active) {
return (sheets[i-1].getRange(range).getValues());
}
}
}
The only problem is, you will have to push the range as a String by surrounding it with quotation marks.
This will give you a range of Data
=PrevSheet("D2:D6")
This will give you the sum
=Sum(PrevSheet("D2:D6"))
UPDATE:
If you want this to be updated, use this script
function PrevSheetName() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var active = ss.getActiveSheet().getName();
for (i in sheets) {
if (sheets[i].getName() === active) {
return (sheets[i-1].getName());
}
}
}
And get the values like this:
=INDIRECT(PrevSheetName()&"!D2")
And
=SUM(INDIRECT(PrevSheetName()&"!D2:D6"))
Hope this helps.

VBA in Excel: writing cell value to a column if it doesn't already exist there

Ok, so I have a table of names in one column and corresponding numbers in another. Most names appear more than once and each time with a different number. The table is likely to be added to in the future. I'm trying to write a VBA macro that will output each name once and the total sum of the numbers attached to them on a separate sheet. I haven't used VBA in like 8 months and I'm really rusty. Suggestions?
remember to add the reference microsoft scripting runtime, i believe. u would need to use dictionary here.
Sub test()
Dim var As Variant
Dim rng As Range
Set rng = Range("A1:A100")
Dim dico As Dictionary
Set dico = New Dictionary
For Each var In rng.Cells
if dico.Exists(var.value) Then
dico(var.value) = dico(var.value) + var.offset(0,1).value
else
dico.Add var.value, var.offset(0,1).value
end if
Next var
Set rng = Range("C1")
Dim i as double
i = 0
For Each var In dico.keys
rng.offset(i).value = var
rng.offset(i,1).value = dico(var)
Next var
End sub