1

I have the following functions and it takes way too much time to execute, is there any way to simplify it?

In short,

  1. I want to copy 10 docs

  2. Paste them to 10 different folders

  3. Rename the 10 new docs

  4. Copy 5 ranges from each of the 10 original docs, each of which in different sheet names

  5. Paste the above mentioned 5 ranges to 10 new docs respectively

These are my functions

function newcopy(Sheet,Folder) {  

var sss = SpreadsheetApp.openById(Sheet)

//getting the name of the new doc

var d = new Date();

var curr_month = d.getMonth();

var curr_year = d.getFullYear();

var theDate = curr_month + curr_year;

var newFileName = "Billing " + theDate;

//I want it to be Billing 92021, instead it becomes Billing 2030

var destFolder = DriveApp.getFolderById(Folder);

var tsss = DriveApp.getFileById(sss.getId()).makeCopy(newFileName, destFolder);

var tssid = tsss.getId()

var tss = SpreadsheetApp.openById(tssid)

//copy 1st range

var ss = sss.getSheetByName('Budget');

var sr = ss.getRange("I7:J29");

var format = sr.getNumberFormats();

var values = sr.getValues();

var bGcolors = sr.getBackgrounds();

var colors = sr.getFontColors();

var fontSizes = sr.getFontSizes();

//copy 2nd range

//not neccessary AA1:AH10000,but I don't know how to get the last row

var ss2 = sss.getSheetByName('Billing(last month)');

var sr2 = ss2.getRange("AA1:AH10000");

var format = sr2.getNumberFormats();

var values = sr2.getValues();

var bGcolors = sr2.getBackgrounds();

var colors = sr2.getFontColors();

var fontSizes = sr2.getFontSizes();

//copy 3rd range

var ss3 = sss.getSheetByName('Billing(YE to last month)');

var sr3 = ss3.getRange("AA1:AH10000");

var format = sr3.getNumberFormats();

var values = sr3.getValues();

var bGcolors = sr3.getBackgrounds();

var colors = sr3.getFontColors();

var fontSizes = sr3.getFontSizes();

//copy 4th range

var ss4 = sss.getSheetByName('Outstanding(last month)');

var sr4 = ss4.getRange("AA1:AN10000");

var format = sr4.getNumberFormats();

var values = sr4.getValues();

var bGcolors = sr4.getBackgrounds();

var colors = sr4.getFontColors();

var fontSizes = sr4.getFontSizes();

//copy 5th range

var ss5 = sss.getSheetByName('Outstanding(last month)');

var sr5 = ss5.getRange("AA1:AN10000");

var format = sr5.getNumberFormats();

var values = sr5.getValues();

var bGcolors = sr5.getBackgrounds();

var colors = sr5.getFontColors();

var fontSizes = sr5.getFontSizes();

//paste 1st range

var ts = tss.getSheetByName('Budget');

var tr = ts.getRange("I7:J29");

tr.setNumberFormats(format);

tr.setValues(values);

tr.setBackgrounds(bGcolors);

tr.setFontColors(colors);

tr.setFontSizes(fontSizes);

//paste 2nd range

var ts2 = tss.getSheetByName('Billing(last month)');

var tr2 = ts2.getRange("AA1:AH10000");

tr2.setNumberFormats(format);

tr2.setValues(values);

tr2.setBackgrounds(bGcolors);

tr2.setFontColors(colors);

tr2.setFontSizes(fontSizes);

//paste 3rd range

var ts3 = tss.getSheetByName('Billing(YE to last month)');

var tr3 = ts3.getRange("AA1:AH10000");

tr3.setNumberFormats(format);

tr3.setValues(values);

tr3.setBackgrounds(bGcolors);

tr3.setFontColors(colors);

tr3.setFontSizes(fontSizes);

//paste 4th range

var ts4 = tss.getSheetByName('Outstanding(last month)');

var tr4 = ts4.getRange("AA1:AN10000");

tr4.setNumberFormats(format);

tr4.setValues(values);

tr4.setBackgrounds(bGcolors);

tr4.setFontColors(colors);

tr4.setFontSizes(fontSizes);

//paste 5th range

var ts5 = tss.getSheetByName('Outstanding(Today)');

var tr5 = ts5.getRange("AA1:AN10000");

tr5.setNumberFormats(format);

tr5.setValues(values);

tr5.setBackgrounds(bGcolors);

tr5.setFontColors(colors);

tr5.setFontSizes(fontSizes)}



function newpaste () {

//run the above function 10times

copy("Sheet1","Folder1")

copy("Sheet2","Folder2")

copy("Sheet3","Folder3")

copy("Sheet4","Folder4")

copy("Sheet5","Folder5")

copy("Sheet6","Folder6")

copy("Sheet7","Folder7")

copy("Sheet8","Folder8")

copy("Sheet9","Folder9")

copy("Sheet10","Folder10")

}
Rubén
  • 34,714
  • 9
  • 70
  • 166
  • See [tag info page](https://stackoverflow.com/tags/google-apps-script/info) for best practices, free resources and more details. – TheMaster Oct 10 '21 at 16:09
  • 1
    you can reduce the ranges, but I am afraid it will not help. Btw, what do you mean by "too much time", how much is not "too much"? – GoranK Oct 10 '21 at 16:14
  • Instead of `var theDate = curr_month + curr_year;` you can use the standard JS trick `var theDate = '' + curr_month + curr_year;` it converts the numbers into strings as you combine them. – Yuri Khristich Oct 10 '21 at 16:35
  • Here is the solution that could work better: https://stackoverflow.com/a/10485959/14265469 You copy not ranges but sheets a whole from source spreadsheet to destination spreadsheet. Then you copy ranges from sheet to sheet via `range.copyTo()` method inside the destination spreadsheet. It works faster. And you don't need to copy color, fonts, etc -- they will be copied automatically. Then you remove copied sheets from destination spreadsheet. – Yuri Khristich Oct 10 '21 at 17:29
  • There are a lot of things that could be done to improve your script, some are easy as using the IDE's format document feature and avoid using so big range references, other requires to learn programming stuff like data structures (when working with JavaScript and Google Sheets the basics are arrays and objects) and looping. Anyway, as Google Apps Script basics services are slow if your end goal is to reduce the script execution time to the minimum you should use the Advanced Drive Service and Advances Sheets Services instead of Drive Service and the Spreadsheet Service . – Rubén Oct 10 '21 at 18:18
  • 1
    Actually 3 minutes doesn't seem too long. Opening and closing documents on google sheets is pretty slow. I'd go fix myself a snack when I run it and not worry about it. – Cooper Oct 10 '21 at 19:07
  • Not too much is when I run newpaste() it wouldn't show "it exceeded the time limit (which is 6 minutes I guess)". Every single newcopy() takes 3minutes min, and 10 would take 30 minutes. – Bobbie Kan HKMS Oct 11 '21 at 01:17

1 Answers1

1

I don't know how could it be speed up significantly, but I see how you can short it:

function newcopy(Sheet, Folder) {

    var sss = SpreadsheetApp.openById(Sheet);

    var d           = new Date();
    var curr_month  = d.getMonth();
    var curr_year   = d.getFullYear();
    var theDate     = '' + curr_month + curr_year;
    var newFileName = 'Billing ' + theDate;

    var destFolder  = DriveApp.getFolderById(Folder);
    var tsss        = DriveApp.getFileById(sss.getId()).makeCopy(newFileName, destFolder);
    var tssid       = tsss.getId();
    var tss         = SpreadsheetApp.openById(tssid);

    // all names of the sheets and its ranges can be gathered 
    // in one array of objects [{name,range},{name,range}...]

    var sheets = [
        {name: 'Budget',                    range: 'I7:J29'},
        {name: 'Billing(last month)',       range: 'AA1:AH10000'},
        {name: 'Billing(YE to last month)', range: 'AA1:AH10000'},
        {name: 'Outstanding(last month)',   range: 'AA1:AH10000'},
        {name: 'Outstanding(last month)',   range: 'AA1:AH10000'}
    ]

    // then they can be iterated in loop
    // you can call name of a sheet as 'sheet.name'
    // and get a 'a1' notation of its range as 'sheet.range'

    for (var sheet of sheets) {
        var ss = sss.getSheetByName(sheet.name);
        var sr = ss.getRange(sheet.range);

        var ts = tss.getSheetByName(sheet.name);
        var tr = ts.getRange(sheet.range);

        // these getValues and setValues commands are
        // most time consuming, but there is no way to avoid them
        // in this particular algorithm

        // sr.copyTo(tr); <-- it would work much faster
        // but it copies ranges within the same spreadsheet only

        var format    = sr.getNumberFormats();
        var values    = sr.getValues();
        var bGcolors  = sr.getBackgrounds();
        var colors    = sr.getFontColors();
        var fontSizes = sr.getFontSizes();

        tr.setNumberFormats(format);
        tr.setValues(values);
        tr.setBackgrounds(bGcolors);
        tr.setFontColors(colors);
        tr.setFontSizes(fontSizes);
    }
}

function newpaste() {
    newcopy('Sheet1', 'Folder1');
    newcopy('Sheet2', 'Folder2');
    newcopy('Sheet3', 'Folder3');
    newcopy('Sheet4', 'Folder4');
    newcopy('Sheet5', 'Folder5');
    newcopy('Sheet6', 'Folder6');
    newcopy('Sheet7', 'Folder7');
    newcopy('Sheet8', 'Folder8');
    newcopy('Sheet9', 'Folder9');
    newcopy('Sheet10','Folder10');
}

But I didn't test it. So it's up to you to debug it.

If you want to get ranges dynamically based on last row on the sheet you I'd change the object this way:

var sheets = [                   // define the ranges without last row numbers
    {name: 'Budget',                    range: 'I7:J'},   
    {name: 'Billing(last month)',       range: 'AA1:AH'},
    {name: 'Billing(YE to last month)', range: 'AA1:AH'},
    {name: 'Outstanding(last month)',   range: 'AA1:AH'},
    {name: 'Outstanding(last month)',   range: 'AA1:AH'}
]

And change define the ranges this way:

var ss = sss.getSheetByName(sheet.name);
var last_row = ss.getLastRow();                // get last row number
var sr = ss.getRange(sheet.range + last_row);  // add the number to the range
var ts = tss.getSheetByName(sheet.name);
var tr = ts.getRange(sheet.range + last_row);  // add the number to the range

But it unlikely makes the script faster. You reduced amount of the copied data, but you added one more call to server. I suspect the result will be about the same.

Yuri Khristich
  • 13,448
  • 2
  • 8
  • 23
  • 1
    Yep. You very right. I'm not good in the long well written descriptions. I've added a couple comments right now. Hope it become a little better. – Yuri Khristich Oct 10 '21 at 19:10
  • 1
    It might be possible to reduce the number of getRange calls by using Spreadsheet.getRangeList (I'm not !00% sure that doing this will reduce significantly the total execution time in every case) – Rubén Oct 10 '21 at 19:20
  • 2
    @Rubén [related old stats](https://stackoverflow.com/questions/51392301/getting-a-range-from-a-range-in-google-apps-scripting/51393840#comment89787128_51393840) – TheMaster Oct 10 '21 at 20:15
  • This is so much more easier to read, I will try that later – Bobbie Kan HKMS Oct 11 '21 at 01:19
  • I tried this and reduce the range from AA1:AH1000 Could you please help to change the range from 'AA1:AH10000' to getting the last row of the data? – Bobbie Kan HKMS Oct 11 '21 at 02:12
  • You can get a last row (number) on the sheet pretty easy: `var num = ss.getLastRow()`. And then to use this number as you define the range: `var sr = ss.getRange('AA1:AH' + num)`. But another command `get` is one more call to server. So it unlikely helps to speed your script up. – Yuri Khristich Oct 11 '21 at 13:46