Google Apps Script Copy/Paste Filtered DataSet - arrays

I've been trying to write a script that simply takes a filtered data, copies it, and then pastes it into another sheet. Nothing I seem to do works. With the code below, which I found online, it should work, but I keep getting an error that states The number of rows in the range must be at least 1. However, I have data in the range A7:R500 and I'm only filtering out blanks and 'W'. Am I correct in this thinking?
function copyPaste(){
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getRange('A7:R500').getValues();
var hiddenValues = ['', 'W'];
values = values.filter(function(v){
return hiddenValues.indexOf(v[4]) == 'W';
});
sheet.getRange(1,21, values.length, 18).setValues(values);
}

Solution:
Since you are already using a filter Array, you can compare hiddenValues.indexOf(v[4]) to -1 to filter out blanks and "W".
Also, since your goal is to paste the results in a different sheet, you need to define both the source and the destination sheet. Create a sheet and plug its name into the new sheet name tag in the code below.
Sample Code:
function copyPaste() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getActiveSheet();
var sheet2 = ss.getSheetByName('<new sheet name>');
var values = sheet1.getRange('A7:R500').getValues();
var hiddenValues = ['', 'W'];
values = values.filter(function(v){
return hiddenValues.indexOf(v[4]) == -1;
});
sheet2.getRange(1,21, values.length, 18).setValues(values);
}
Reference:
indexOf()

Related

Google Sheet Query - Building Reference Array Dynamically

I have multiple tabs in a file and want to merge the same range to a master tab.
I did a Query
=QUERY({source1!AR5:AU;source1!AR5:AU}, "select Col1,Col2,Col3,Col4 where Col1 is not null order by Col1", 0)
But at the end I will have more and more tabs (around 30), and I don't want to change my query manually each time. how can i do?
I saw somewhere that I can use macros to create a function, do you have an idea of the code?
I just want to have something easy where I have added in another tab all my tab names
Try:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var allSheets = [];
// Append more names if you want to exclude more
// Since 'query' is master sheet, excluded it as well
// Remove 'query' below if you want to include it in the formula
var excludedTabs = ['other', 'random', 'query'];
sheets.forEach(function (sheet){
var sheetName = sheet.getSheetName();
if (!excludedTabs.includes(sheetName)){
allSheets.push(sheetName);
}
});
// Set formula in A1 cell in "query" sheet
// Modify A1 to change the cell
var cell = ss.getSheetByName('query').getRange("A1");
cell.setFormula("=QUERY({" + allSheets.join('!AR5:AU;') + "!AR5:AU}, \"select Col1,Col2,Col3,Col4 where Col1 is not null order by Col1\", 0)");
}
This will set the formula to A1 of the sheet query, feel free to change where to set the formula by changing A1 and query.
You can also add sheets to be excluded. Append it on the excludedTabs array.
Sample Output:
Sample sheets were added to check the new sheet cases.
Expected formula was added. (excluding query, random and other sheets)

two active sheets in google sheets update error based on cell comparison

I'm trying to get this code working but I know I'm missing something. what the code should do is: to find a specific text from another cell using another sheet reference, so there are two sheets in the google sheet below:'BSR DATA' and 'ENTRY FORM'. from ENTRY FORM,cell g6 will compare its data to the first column of BSR DATA and if it meets the criteria, it will update the specific row of the array/cell reference with "new value".
link to google sheets:https://docs.google.com/spreadsheets/d/1kBuczydffPFBEfy2oegC1WsKXMjSxbGNRzWLCKurOMs/edit?usp=drivesdk
function UpdateBSRSpecial()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testForm = ss.getSheetByName("ENTRY FORM");
var testTable = ss.getSheetByName("BSR DATA");
var testFormValue = testForm.getRange("G6").getValue();
var rangeData = testTable.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
for(var i=0;i>lastRow;i++){
var dataID = testForm.getRange(i,1).getValue();
if(testFormValue == dataID)
{
testTable.getRange(i,6).setValue("new value");
};
};
};
The existing script is updating all rows because it loops with for(var i=0;i>lastRow;i++){. The following script demonstrates the process to search for a discrete term.
I am not going to deal with the updating of the "new value" since that is not clear from the question.
Aspects to note
the script gets all the data on BSR - var bsrdata = bsr.getRange(1,1,bsrLR,bstLC).getValues();. This can be used later when updating values.
using the Javascript map method, Column A is extracted as a separate array for the search - var bsrColA = bsrdata.map(function(e){return e[0];});//[[e],[e],[e]]=>[e,e,e]
the search is conducted using the Javascript indexOf method. Note: if a match is found, the method returns the index number of the search term within the array; if no match is found, the method returns "-1".
the script tests to see if the search was successful: if (result !=-1){
indexOf returns a zero-based index, therefore the actual row number is the index plus one: var row = result+1;
function so5866895401() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formsheetname = "ENTRY FORM";
var form = ss.getSheetByName(formsheetname);
var bsrname = "BSR DATA";
var bsr = ss.getSheetByName(bsrname);
// get input cell on form
var searchterm = form.getRange("G6").getValue();
// Logger.log(searchterm);// DEBUG
//get data from BSR
var bsrLR = bsr.getLastRow();
var bstLC = bsr.getLastColumn();
var bsrdata = bsr.getRange(1,1,bsrLR,bstLC).getValues();
// get column A of BSR
var bsrColA = bsrdata.map(function(e){return e[0];});//[[e],[e],[e]]=>[e,e,e]
// Logger.log(bsrColA);// DEBUG
// search for the searchterm in ColumnA of BSR
var result = bsrColA.indexOf(searchterm); // zero-based
if (result !=-1){
var row = result+1;
Logger.log("result = "+result+", row = "+row);
}
}

Google Script: getValues() and setValues() for rectangular grid

In Google Sheets, when I store a given a1notation into an appendRow, I want the values from this appended Row to be shown on another sheet while retaining the grid structure it had when it was saved intitially.
So from the below code, the data from the appendedRow(11,5,1,5) shall be set to the grid I5:J7.
Sadly I am not proficient enough to work with for loops and push() / array, so I would appreciate your support greatly.
Thank you.
function Test() {
var rs = SpreadsheetApp.getActiveSpreadsheet();
var ss = rs.getSheetByName("Sheet");
var tempArray = [ss.getRange(11,5,1,5)]
var values = tempArray.getValues();
ss.getRange('I5:J7').setValues(values);
}
Google Apps Script documentation has a Reference section which gives you a brief explanation and example of different methods. Under
https://developers.google.com/apps-script/reference/spreadsheet/range#setvaluesvalues
you can see that the method setValues(values) requires the dimension of the origin and destination range to match. Thus, you cannot paste a 1x5 range into a 2x3 range.
What you can do is to loop through the cells of the destination range and to assign them sequentially a value from the origin range - until either all the values have been passed or the destination range is full. You could implement it like this:
function Test() {
var rs = SpreadsheetApp.getActiveSpreadsheet();
var ss = rs.getSheetByName("Sheet");
var values = ss.getRange(11,5,1,5).getValues();
var destination=ss.getRange('I5:J7');
var value=0;
for(i=1;i<=3;i++)
{
for(j=1;j<=2;j++)
{
var cell=destination.getCell(i,j);
if(typeof values[0][value] !== "undefined")
{
cell.setValue(values[0][value]);
}
value=value+1;
}
}
}

setFormulas and rangeList

I'm trying to setFormulas over a range of non-contiguous cells. I need a formula (they're all different) set every 30 cells in a single column (c).
It works to setFormula for each cell, but creating 56 variables seems unnecessary. I can get the formulas but not set them as intended. I also tried using getRangeList but I'm not sure that does what I think it does. Any advice?
function test() {
var spreadsheetU09U10 = SpreadsheetApp.openById('some url');
var sheetU09 = spreadsheetU09U10.getSheetByName('TEST');
var sheetU10 = spreadsheetU09U10.getSheetByName('U10');
var sheetDATA = spreadsheetU09U10.getSheetByName('Sheet4');
//U09 SHEET
//var rangeListU09 = sheetU09.getRangeList(['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494',
//'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784']);
//Logger.log(rangeListU09);
var startRow = 4;
var startColumn = 3;
var numRows = sheetU09.getLastRow();
var numColumns = 1;
var range = sheetU09.getRange(startRow, startColumn, numRows, numColumns);
var getFormulasU09 = sheetDATA.getRange('C30:C57').getFormulas();
//Logger.log(getFormulasU09);
Logger.log(getFormulasU09.length);
for (var i = 0; i < getFormulasU09.length; i++) {
var setFormulasU09 = range.setFormulas(getFormulasU09);
Logger.log(setFormulasU09);
startRow = startRow + 29;
}
It isn't clear exactly where the formulas you are using are originating from, but the RangeList class can help reduce the read time, even if you use it just to call getRanges. If the formula is the same in R1C1 format, then you can very effectively use RangeList#setFormulaR1C1.
Assuming you have formulas in one region that must be written verbatim in a disjoint set of cells:
const wb = SpreadsheetApp.getActive();
// Assuming only text formulas, not actual "entered" formulas
const formulas = wb.getSheetByName("formulas").getDataRange()
.getValues()
.map(function (row) { return row[0]; });
const sheet = wb.getSheetByName("some name");
const destinations = [
// Depending on the relationship between destinations, one could programmatically generate these
];
// Efficiently acquire references to multiple disjoint Ranges
const rl = sheet.getRangeList(destinations);
// Assume the i-th formula goes in the i-th range
rl.getRanges().forEach(function (rg, i) {
rg.setFormula(formulas[i]);
});
// The RangeList makes uniformly formatting these disjoint ranges extremely simple
rl.setFontWeight('bold');
...
Reference
- RangeList
You want to put formulas to the individual cells.
You want to put 28 formulas to cells of ['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494', 'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'] in the sheet of TEST.
If my understanding is correct, how about using values.batchUpdate of Sheets API? The flow of this script is as follows.
Set range list as 1 dimensional array.
Retrieve formulas.
Create request body for sheets.spreadsheets.values.batchUpdate.
In order to use this script, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Sample script:
function test() {
var spreadsheetId = "### spreadsheetId ###"; // Please set this.
var sheetName = "TEST";
var spreadsheetU09U10 = SpreadsheetApp.openById(spreadsheetId);
var sheetU09 = spreadsheetU09U10.getSheetByName(sheetName);
// var sheetU10 = spreadsheetU09U10.getSheetByName('U10'); // This is not used in this script.
var sheetDATA = spreadsheetU09U10.getSheetByName('Sheet4');
var rangeListU09 = ['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494', 'C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
var getFormulasU09 = sheetDATA.getRange('C30:C57').getFormulas();
rangeListU09 = rangeListU09.map(function(e) {return sheetName + "!" + e});
var resource = {
data: rangeListU09.map(function(e, i) {return {range: e, values: [[getFormulasU09[i][0]]]}}),
valueInputOption: "USER_ENTERED",
};
Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
}
Note:
From your question, I'm not sure about the detail formulas. If the a1Notation of each formulas is required to be modified, can you provide a sample spreadsheet including the formulas?
Reference:
sheets.spreadsheets.values.batchUpdate
If I misunderstand your question, please tell me. I would like to modify it.
I'm assuming that you want to copy the whole column starting from the cell locations in the array. That wasn't really clear to me.
function test109() {
var ss=SpreadsheetApp.getActive();
var shU09=ss.getSheetByName('35');//formulas get copied into here starting at row 4
var shDATA=ss.getSheetByName('36');//formulas stored in here C30:C57
var fA=shDATA.getRange('C30:C57').getFormulas();
var dA=['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494','C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
for(var i=0;i<dA.length;i++){
var rgs=Utilities.formatString('%s:%s',dA[i],shU09.getRange(dA[i]).offset(fA.length-1,0).getA1Notation());//this uses range.offset to calculate the correct range in A1Notation.
shU09.getRange(rgs).setFormulas(fA);
}
}
As it turns out I just noticed that there are 28 locations and 28 formulas. Perhaps that was intentional and you want to copy a different formula in each location then this version would do that.
function test109() {
var ss=SpreadsheetApp.getActive();
var shU09=ss.getSheetByName('35');//formulas get copied into here starting at row 4
var shDATA=ss.getSheetByName('36');//formulas stored in here C30:C57
var fA=shDATA.getRange('C30:C57').getFormulas();
var dA=['C4','C34','C64','C94','C124','C154','C184','C204','C234','C264','C294','C324','C354','C384','C404','C434','C464','C494','C524','C554','C584','C604','C634','C664','C694','C724','C754','C784'];
for(var i=0;i<dA.length;i++){
shU09.getRange(dA[i]).setFormula(fA[i][0]);
}
}
Range Offset

Google Apps Script Replace and update cell within a range

I have a Google spreadsheet that I'm trying to remove the word "woo" within a range of cells
So far I've managed to loop through the results and log the results, however I haven't figured how to update that information in the spreadsheet itself.
Any guidance would be welcomed
Thank you
function myFunction () {
var ss = SpreadsheetApp.getActiveSheet().getRange('B:B')
var data = ss.getValues();
for (var i = 0; i < data.length; i++) {
var text = data[i].toString();
var finaltext = text.replace(/woo/g, "");
data[i] = finaltext;
Logger.log(data[i]);
}
}
Use setValues()
Notes:
Usually ss is used as a shorthand for spreadsheet, as it's used on the code for a range it's better to use range as a variable name.
setValues() returns a 2D array, so data[i] returns an array of row values rather than a cell value. To get/set cell values, use data[i][0] notation.
Considering the above replace
var ss = SpreadsheetApp.getActiveSheet().getRange('B:B')
by
var range = SpreadsheetApp.getActiveSheet().getRange('B:B')
then add the following line after the for block.
range.setValues(data);
Regarding text var declaration, replace
var text = data[i].toString();
to
var text = data[i][0].toString();
Using open ended references like B:B could lead to problems. To avoid them be sure to keep the sheet rows at minimum or better instead of using an open ended reference use something like B1:B10.

Resources