I have around 5000 rows. Now I want to move data for one sheet to another if their status is Delivered.
This is what I am doing:
function move(){
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastSourceRow = sourceSheet.getLastRow();
var lastSourceCol = sourceSheet.getLastColumn();
var sourceRange = sourceSheet.getRange(1, 1, lastSourceRow, lastSourceCol);
var sourceData = sourceRange.getValues();
var activeRow = 0;
//Loop through every retrieved row from the Source
for (row = lastSourceRow; row > 1; row--) {
//IF Column B in this row has 'deal', then work on it.
if (sourceData[row-1][1] === 'Delivered') {
//then push that into the variables which holds all the new values to be returned
activeSheet.appendRow(sourceData[row-1]);
//delete current
sourceSheet.deleteRow(row);
}
Logger.log(row);
}
}
My problems:
Script is so slow it takes so much time to execute.
As per google policy script timeout in 5 mins.
So the loop has to be started again. So also index is set to null. So I can't check the record at the top if they are delivered or not.
Please can anyone provide me a better solution for this? I search for a faster loop but still no luck.
function move(){
const ash=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
const ssh=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var data=ssh.getDataRange().getValues();
var d=0;
var oA=[];
data.forEach(function(r,i){
if(r[1]=="Delivered") {
oA.push(r);
ssh.deleteRow(i+1-d++);
}
});
ash.getRange(ash.getLastRow()+1,1,oA.length,oA[0].length).setValues(oA);
}
The deletion of the rows always takes time. You could splice them out of the data array and clear the sheet and place them back in with a setvalues(). But if you have formulas this may mess some things up. I'll leave that up to you.
Related
I am working through data in Google sheets, attempting to delete EMPTY COLUMNS based I have 600 so it take too much time
Any suggestions on how to speed this up?
Here is my code :
function delCols() {
var s = SpreadsheetApp.getActive().getActiveSheet()
var c = 600
var data = s.getRange(1, 1, 1, c).getValues()
Logger.log(data[0].length)
var delCol=0
for (var i = data[0].length; i >0 ; i--) {
if(s.getRange(1, c ).getValue() =="") { s.deleteColumn(c);delCol=delCol+1 }
c=c-1
}
s.insertColumns(600-delCol, delCol);
Logger.log(delCol)
}
Thank you in advance if you may help
Create a new sheet, for example "Sheet0", for intermediate data. It can be hidden.
Run this script function to remove empty columns from the active sheet:
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var activeSheet = ss.getActiveSheet(),
sheet0 = ss.getSheetByName('Sheet0');
// AHG is 600-th column (if I am not mistaken)
var formula = "=TRANSPOSE(QUERY(TRANSPOSE('" + activeSheet.getName() +
"'!A:AHG); \"select * where Col1 is not null\"; 0))";
sheet0.getRange('A1').setFormula(formula);
SpreadsheetApp.flush();
var values = sheet0.getDataRange().getValues();
sheet0.getRange('A1').clear();
activeSheet.clearContents();
activeSheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}
I believe it is faster, than column by column deletion. Some remarks:
There is a restriction for use of formulas on the active sheet, because we copy only values.
If you do not remove the formula in the script, then calculations will take place on every active cheet change, resulting in delays.
Please note, empty column detection is based on top cell value, as in your code.
I have a script which combines three scripts to do the following:
1) Insert rows from one tab to the top of another tab
2) Remove duplicates from the tab in which the data was just added
3) Clear out the old tab from which the data was just ported over from
For the De-dupe script, it deletes rows starting at the bottom and then goes up. So I'm having established and existing data deleted. What I need it to do is start at the top and go down. So if new row records ported over from the first script are found to be a duplicate, it should delete those instead.
How can I get the de-dupe script to essentially process the opposite way?
I did find reverse logic with the below link, but I can't find a way to make it work with my script and keep getting errors. I'm also not sure if this would be the best methodology to fit in with my overall script.
Link: Removing Duplicate Rows in a google Spreadsheet from the end row
function Run(){
insert();
removeDuplicates();
clear1();
}
function insert() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheetByName('Candidate Refresh'); // change here
var des = ss.getSheetByName('Candidate Listing'); // change here
var sv = source
.getDataRange()
.getValues();
sv.shift();
des.insertRowsAfter(1, sv.length);
des.getRange(2, 1, sv.length, source.getLastColumn()).setValues(sv);
}
//Code in Question Start
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getLastRow();
var firstColumn = sheet.getRange(1, 2, rows, 1).getValues();
firstColumn = firstColumn.map(function (e) {return e[0]})
for (var i = rows; i >0; i--) {
if (firstColumn.indexOf(firstColumn[i-1]) != i-1) {
sheet.deleteRow(i);
}
}
}
//Code in Question End
function clear1() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Candidate Refresh');
sheet.getRange('A2:K100').clearContent()
}
If new rows at the top of the sheet are found to be a duplicate, delete the new rows at the top.
try this:
function removeDuplicates() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows=sheet.getLastRow();
var firstColumn=sheet.getRange(1, 2, rows, 1).getValues();
firstColumn = firstColumn.map(function(e){return e[0]})
var uA=[];
for (var i=rows;i>0;i--) {
if (uA.indexOf(firstColumn[i-1])!=-1) {
sheet.deleteRow(i);
}else{
uA.push(firstColumn[i-1]);
}
}
}
Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.
I'm fairly new to Google script and have been basically cobbling together code by finding it in Stack Overflow answers or the Google documentation. I have a working script, but it's very slow, so when I try to run it on a large file, it times out.
I know there must be inefficiencies in my code given how I went about writing it, but since I'm not super familiar with the language and learning as I go, I need help understanding how to find them (or help finding them, or both).
The code basically goes through a file that is a series of datasheets, each with a block of data that I need to copy into a "master sheet". Basically it's copy and paste, but from sometimes over a hundred sheets (which is why I'm not just doing this by hand).
Code in question (excessively commented, I know, but I'm not the only one using it):
function CleanAllTheThings(){
//Define all the variables first thing
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
//Get rid of blank rows and columns so Google doesn't get upset
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns()
var lastColumn = sheet.getLastColumn()
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn)
}
var maxRows = sheet.getMaxRows()
var lastRow = sheet.getLastRow()
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow)
}
}
//Continue defining variables
var create = ss.insertSheet("Merged",0);
var destsheet = ss.getSheetByName("Merged");//Name of sheet you’re putting everything into. If you don’t like the name, change it, but make sure you change it in the for loop too, or it’ll give you an error
//var StartRow = 4;//Row to start with, must be the same for every sheet; change it if the data start in a different row
//var StartCol = 3;//Column where the actual data start, ditto above
//var EndCol = 11;//End Column of all data, ditto above, but shouldn't ever change unless there are more columns--then just change this number accordingly
var prOne = ss.getSheetByName("Profile 1");
var rows_deleted = 0;
//Create a Merged sheet
for (var s in allsheets){
var sheet=allsheets[s]
if (sheet.getName() == "Merged"){//If the sheet is called “Merged”, don’t do anything to it!
}
else{//If the sheet is not called “Merged”, do stuff
var EndRow = sheet.getLastRow()
var copyRng = sheet.getRange(4,3,EndRow,11)//If the data start in a different place than row 4 column 3 and end in a different column than column 11, you need to change these numbers. Leave EndRow alone because it changes
var nextRow = 1 + destsheet.getLastRow()
destsheet.insertRowsAfter(destsheet.getMaxRows(), EndRow)//insert rows so Google doesn’t have fits. You end up with extras but you can either take them out with the add-on or just leave them be. I don’t think they’ll fill with ###### when you bring them back to Excel, if that’s what you decide to do
copyRng.copyTo(destsheet.getRange(nextRow,2))//Row and Column of where to paste the information; you’re pasting into column 2 because you’re about to fill column one with the sheet name the data came from
var LastRow = destsheet.getLastRow()//New last Row after pasting the data
var Sample = sheet.getRange(1,1)
sheet.getRange(4,2).copyTo(destsheet.getRange(nextRow,1,LastRow-nextRow+1,1))//Paste the name of the sheet in column 1 for every row copied
}
}
//Add a Header
prOne.getRange(3,3,3,11).copyTo(destsheet.getRange(1,2,1,10))
prOne.getRange(4,1).copyTo(destsheet.getRange(1,1,1,1))
//Get rid of the Solvent Peak rows
var range = destsheet.getDataRange();
var values = range.getValues();
for (var i = 0; i < values.length; i++){
for (var j = 0; j < values[i].length; j++){
var value = values[i][j];//row numbers are 1-based, not zero-based like this for-loop, so we add one AND every time we delete a row, all of the rows move down one, so we will subtract this count
var row = i + 1 - rows_deleted;//if the type is a number, we don't need to look
if (typeof value === 'string'){
var result = value.search("SOLVENT PEAK");//the .search() method returns the index of the substring, or -1 if it is not found we only care if it is found, so test for not -1
if (result !== -1){
destsheet.deleteRow(row)
rows_deleted++
}
}
}
}
//Delete blank rows and columns in Merged
var maxColumns = destsheet.getMaxColumns()
var lastColumn = destsheet.getLastColumn()
if (maxColumns-lastColumn != 0){
destsheet.deleteColumns(lastColumn+1, maxColumns-lastColumn)
}
var maxRows = destsheet.getMaxRows()
var lastRow = destsheet.getLastRow()
if (maxRows-lastRow != 0){
destsheet.deleteRows(lastRow+1, maxRows-lastRow)
}
}
For one thing, I know adding blank rows that I later delete is just a waste of time, but I'm not sure if it's such a big waste of time that fixing that alone would help this.
Thanks in advance for any help!
I have put this little script together to insert an if(vlookup) formula into a spreadsheet but even though it is running it isn't inserting anything into the spreadsheet. Can anyone suggest what I have missed off or overlooked please?
Or is there an easier way to do this with an array? I'm still fairly new so not sure how I would execute that successfully.
function idSiteCoordinator(){
var SS = SpreadsheetApp.getActiveSpreadsheet();
var shLog = SS.getSheetByName("LOG");
//declare how many rows to interrogate (to the last row)
var lastRow = shLog.getLastRow();
var startRow = 2;
for (var i=startRow; i<lastRow+1; i++)
{
//Grab the values for LocationCodes
var siteCo = shLog.getRange([i],[12]).getValue();
//Enter formula
if(siteCo = ""){
siteCo.setFormula('=if(E'+[i]+'="08 - Maintenance Request System",LocationCodes!$E$33,vlookup(D'+[i]+',LocationCodes!$D$3:$E$32,2,false))');
}
}
}
You cannot apply the method setFormula to the value, but to the range. Change this on your method:
var siteCo = shLog.getRange([i],[12]);
if(siteCo.getValue() == ""){
siteCo.setFormula('=if(E'+[i]+'="08 - Maintenance Request System",LocationCodes!$E$33,vlookup(D'+[i]+',LocationCodes!$D$3:$E$32,2,false))');
}