using query() with apps script keeps adding 500 rows to sheet - google-apps-script

I'm working on a large sheet and cells are precious given gsheets quota.
I have a range, and when data updates automatically a script updates the named range automatically to be the full length of the data.
The named range is called "gadatapull". This range is on the tab "datapull".
Two tabs. "datapull" is where fresh data is dumped and "data_prep" is where I do stuff to the data. After a fresh pull just now datapull has 2,733 rows of data, including the headers.
I would like data_prep to have the same length as datapull. Plus 7 rows for text at top of data_prep.
When my script to update data runs I do this:
// clear dataprep sheet for new data
var lastRow = 7;
var maxRows = dataprep.getLastRow();
if(maxRows - lastRow > 0) {
dataprep.deleteRows(lastRow+1, maxRows-lastRow);
}
data_prep has 7 rows (because the script just deleted all rows above 7).
Now, in data_prep cell A7 I have:
=query(indirect("gadatapull"),"select *")
Expected result was that all the fresh data in "gadatapull" would appear in data_prep tab and that data_prep tab would expand accordingly.
But what actually happens is all the data arrive as expected, but then there are an additional blank 500 rows at the bottom. This 500 number is too rounded off. Makes me think Gsheets is automatically adding this number as a default under some condition.
How can I prevent Gsheets from adding these additional 500 rows?

Instead of letting the sheet API expanding the number of rows (which is your hypothesis and might well be true ;-) you can add all the necessary cells before importing data.
I didn't try in real conditions but this should work.
Btw, the script imports data as well.
code :
function copyDataToSheet(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataprep = ss.getSheetByName('data_prep');
var datapull = ss.getSheetByName('datapull');
var lastRow = 7;
var maxRows = dataprep.getLastRow();
if(maxRows - lastRow > 0) {
dataprep.deleteRows(lastRow+1, maxRows-lastRow);
}
var datapullSize = datapull.getLastRow();
dataprep.insertRows(7,datapullSize);// insert exactly the number of rows you need.
var dataToCopy = datapull.getDataRange().getValues()
dataprep.getRange(7,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy);
}

Related

Paste formula as value on a certain date

I've been digging around on line for a couple of hours now to no avail. I'm trying to write a formula(which I assume is only possible through AppScript), that based on a certain date, will re-paste contents in a cell as values only.
The issue I've had with other scripts is that in my situation I have several different values that need to be checked. I've set up a continuously updating calendar that pulls from an array with listed dates for each item. However, in that array, the row gets moved into an archive once it hits todays date. Thus, they get deleted from the calendar too.
If there is another solution to this, like pasting based off conditional formatting(this would then keep the formulas that haven't found a value yet, and paste ones that have been found as values only), that would be great too.
Any help or advice would be greatly appreciated. If you need any other info, please ask.
EDIT:
The spreadsheet link is in comments. Attached are a before and after picture to help visualize whats going on here. Since the calendar is pulling from the active sheet, and rows on the active sheet go to "archive" when J2-Today() is -1(yesterday), those then disappear from the calendar. I've tried to make a mixed sheet with both archive and active, but I get duplicates as a frequent issue.
See code below:
function archiveRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheetByName("Sheet1");
var archiveSheet = ss.getSheetByName("Sheet2");
// last row should be on the data itself, same with column
var lastRow = mainSheet.getLastRow();
var lastCol = mainSheet.getLastColumn();
// set time to 0 so we can only compare dates
var dateToday = new Date().setHours(0, 0, 0, 0);
var range = mainSheet.getRange(1, 1, lastRow, lastCol);
var values = range.getValues();
// remove header to only process data
var header = values.shift();
var indices = [], difference = [];
// remove dates that are earlier than today
// collect rows and indices data when condition is not met
values.forEach(function (row, index) {
if (row[9] >= dateToday)
return true;
difference.push(row);
indices.push(index);
});
// remove the row with earlier date
// offset with 2 due to 0-indexing and header
indices.forEach(index => mainSheet.deleteRow(index + 2));
// all rows that were deleted are copied to archive sheet
difference.forEach(row => archiveSheet.appendRow(row));
}
Sample Data:
Output:
Note:
If you need to have this run on a daily basis, Time Driven trigger is your friend. Just have it set on a daily basis triggering archiveRows
Make sure that the main sheet only contains the data. (I see your sheet has rows containing 9AM values below. Make sure to clear those as the script will mistakenly include it on the processing)
Adjust date condition if needed.

Trying to copy a variable-length list

I'm trying to copy a list from Sheet A to sheet B. (I have the number of rows used by the data in the list saved in a cell, if needed.)
The spreadsheet file contains 5 sheets, of which "Analysis" is one. It contains 4-5 tables, each separated by a blank row.
The list itself is variable in length - sometimes it's 9 rows, sometimes its 17. Thankfully, it's always 5 columns wide.
Once I get the list into Sheet B, I need to set all cell borders to TRUE.
Here's the code that I've been playing with:
var sss =
SpreadsheetApp.openById(Sheet A);
var ss = sss.getSheetByName('Analysis'); //
var tblRows = sss.getRange('Analysis!K190').getValue();
var range = SpreadsheetApp.getRange('190,5,tblRows,5'); //assign the range you want to copy
var data = range.getValues();
var tss =
SpreadsheetApp.openById(Sheet B);
var ts = tss.getSheetByName('2018');
ts.getRange(ts.getLastRow()+1,1,tblrows,5).setValues(data);
(I'm not sure that this formatted properly)
Anyhow, I have the number of rows used by the list saved as a variable in a spreadsheet cell, and I'd like to use that, because I have found "getLastRow" to get the last row of the sheet, not the last row that is non-blank. (It might be giving me trouble because I'm doing something wrong, tho...) I know that I've messed up, so feel free to throw everything out and start over, if needed.
Anybody care to throw some ideas my way?

Google-Apps-Script can not write back a database with getDataRange().setValues()

I have a database loaded from a google spreadsheet:
mydatabase = sheet.getDataRange().getValues()
which then I extend with a new record:
mydatabase.push(mydatabase[x])
and then at the end of the script, I would want to write back the entire database to the google spreadsheet but the
sheet.getDataRange().setValues(mydatabase)
gives me ERROR since the new database is one record higher than the original was when loaded.
Is there any way to force the getDataRange() to write back the database into the sheet? The spreadsheet otherwise would have enough rows to accommodate the bigger dataset.
In general, for .setValues(obj[][]) to work as expected, the Range that it is acting on must be the same size as the obj[][].
Commonly, this is ensured by acquiring a new Range from the desired Sheet:
var ss = SpreadsheetApp.openById("some id");
var sheet = ss.getSheetByName("some name");
var db = sheet.getDataRange().getValues();
/*
* Do some operations on the obj[][] that is db
* these operations can include adding / removing rows or columns.
*/
// If db is smaller than existing "written" data, the existing data should be cleared first.
if(db.length < sheet.getLastRow() || db[0].length < sheet.getLastColumn())
sheet.clearContent();
// Write the current db back to the sheet, after acquiring the
// exact number of rows and columns needed to hold the values.
sheet.getRange(1, 1, db.length, db[0].length).setValues(db);

How could a dummy Browser.msgBox call affect output in GAS?

I have a script that moves data from one sheet to another and as part of this must insert the appropriate number of rows into the second sheet to account for the data from the first sheet. I'm running into an issue where, for reasons I don't quite understand, the data is duplicated in the second sheet, e.g. if 94 rows are in the original sheet, the second contains these 94 rows then an additional 94 rows right below with the exact same data. My code is:
function moveToEntrySheet(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var rawRow = 2;
var entrySheet = ss.getSheetByName('Sheet2');
var rawSheet = ss.getSheetByName('Sheet1');
var entryRow = 4;
var numEntries = rawSheet.getLastRow() - 1;
if (numEntries <= 0){
return;
}
Browser.msgBox(numEntries);
entrySheet.insertRowsBefore(entryRow, numEntries);
Browser.msgBox(numEntries);
var entryRange = entrySheet.getRange(4, 2, numEntries, 6);
var vals = rawSheet.getRange(2, 1, numEntries, 6).getValues();
Browser.msgBox(numEntries);
entryRange.setValues(vals);
}
To try and debug this I put calls to Browser.msgBox() in my code to make sure that numEntries is the expected number (94), which it does appear to be at each of the 3 points that I put the call in. The weird thing is that when I run this function with the calls to msgBox() included, the entries are only pulled to the other sheet once. If I keep the first call in it works the same, and if I keep the second or third call in double the number of rows that should be inserted are inserted but the data isn't copied itself, essentially giving n empty rows in the sheet, where n is the number of rows of data in sheet 1. How could calls to Browser.msgBox() affect the copy in this way, and how can I avoid the copy from duplicating? Thanks!

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.