Faster Alternative to a For Loop Google App Script Javascript - javascript

I've created a for loop that pulls sales data in columns from an array (salesLog) and places them into a sheet (targetSheet). The sales data is kept in multiple columns. The columns between columnStart and columnEnd are added (unit), matched with the correct row in the source sheet, and placed in the correct row in the targetSheet. This loop works fine except it's too slow, and a fear unscalable with more data. I'm looking for a way to run this loop faster. Any help?
var length = POlistTarget.length;
for (var i=0; i <= length; i++){
//find Row
var row = POlistSource.indexOf(POlistTarget[i]);
//findColumns
var columnStart = periodArr[0]+1
var columnEnd = periodArr.length
var unitArr =
salesLog.getRange(row+3,columnStart,1,columnEnd).getValues().flat().filter(row=>row!="");
//add units in an array
var unit = unitArr.reduce(function(a,b){return a+b;},0);
//execute
targetSheet.getRange(i+4,7,1,1).setValue(unit);
}

Explanation:
As also Tanaike mentioned in his comment, the main improvement is to use setValues once outside of the for loop instead of using setValue inside the for loop iteratively. In this way you will make one call to set the data instead of POlistTarget.length calls. See more details in best practices.
Another improvement both in terms of performance but also code formatting is to use forEach instead of for loop. The idea is to get rid of unnecessary code.
Improved Solution:
const units = [];
POlistTarget.forEach((pt,i)=>{
let row = POlistSource.indexOf(pt);
let columnStart = periodArr[0]+1;
let columnEnd = periodArr.length;
let unit = salesLog.getRange(row+3,columnStart,1,columnEnd).
getValues().
flat().
filter(r=>r!='').
reduce((a,b)=>a+b);
units.push([unit]);
});
targetSheet.getRange(4,7,units.length,1).setValues(units);
More improvements:
If your code was complete, we could see whether it could be feasible to use getRange for the full array on salesLog so you can get rid of getRange inside the for loop. In this way, you could skip the for loop altogether.

Related

Searching a string

OK Ive been able to get the following to partially work
var Global_Wound_array =[{"WoundNumber":1,"BodySide":"Front","BodyPart":"Nose"},{"WoundNumber":2,"BodySide":"Left","BodyPart":"Head"},{"WoundNumber":3,"BodySide":"Back","BodyPart":"Ear"}]
var Global_Wound_Counter = 1
I can get the page to loop through and display the individual wounds but I need a way to say at a particular page one of the values eg on WoundNumber 2 BodyPart has changed and updated the string without affecting the rest of it.
page9200.setEventHandler("pageFinishing", function () {
//getSelectedButtonLabel this is ok - specific on the system
let Q1 = Q3_WoundNumber.getValue();
let Q2 = Q1_BodySide.getSelectedButtonLabel();
let Q3 = Q2_BodyPart.getSelectedButtonLabel();
for (var i = 0; i < Global_Wound_array.length; i++) {
if (i+1 == Q1){
//create new temp variable array
var Temp_Wound_obj2 = {"WoundNumber": Q1,"BodySide": Q2,"BodyPart":Q3}
Global_Wound_array.push(Temp_Wound_obj2)
}
}
});
As well as being able to reach the end of the string to present a blank set of values to have the option to add a new wound.
Every time I think Ive got something that looks like it would work I go around in circles, when I try to update the system at the end I get and error that the - invaid parameters for RPC call: variable is bad
It seems you are pasting JSON onto JSON, with no separator. This creates a messy and non-standard data structure. If you wrote your JSON with a newline at the end, you would end up with a JSONL file, which is very simple to process.
const jsonl = `
[{"WCount":1,"Side":"Centre","Part":"Ocipit","Type":"Other","SurroundingSkin":"Dermatitis","Height":"","Width":"","Depth":""}]
[{"WCount":2,"Side":"Front","Part":"Neck","Type":"Diabetic foot wound","SurroundingSkin":"Healthy/intact","Height":"3","Width":"4","Depth":"5"}]
`;
const jsonItems = jsonl.trim().split("\n");
const lastJsonItem = jsonItems[jsonItems.length - 1];
const lastItem = JSON.parse(lastJsonItem);
const lastWCount = lastItem[0].WCount;
console.log(lastWCount);
If you already have a file without newlines... it would be best to insert them, and correct your data to JSONL. This is simple in your case just by replacing ][ with ]\n[ (and making sure the file ends with a newline too, so the next write would not be messed up), since you have no nesting and (hopefully) no ][ in your text, but in general it is not easy - I don't know of a JSON parser that will return unconsumed text, so it would probably involve writing a JSON parser. Much easier to write data correctly in the first place.

I used js to create my command syntax, now how can I use it?

I have a Google Sheet with .gs script that is successfully generating dynamicnewRichTextValue() parameters which are meant to be injected into a Sheet cell that will contain multiple lines of text each with their own URL. I do not know all of the parameters in advance (might be one text and one link, or two each, or more) which is why I am dynamically generating the parameters.
Let's say the end-state should be this (in this case there are only two line items, but there could be more or less:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
In my script I don't know how many "setText" parameters or "setLinkUrl" statements I will need to generate, so I am doing it dynamically.
This is simple to handle for "setText" because I can just pass a single variable constructed during an earlier loop that builds the "setText" parameters. Let's call that variable setTextContent, and it works like this:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText(setTextContent)
So up to this point, everything is great. The problem is that I have another variable that generates the URL portion of the newrichtextvalue() parameters up to the ".build();" statement. So let's call that variable setUrlContent and it is built in an earlier loop and contains the string for the rest of the statement:
.setLinkURL(0,22,"mailto:fred#abcdef.com").setLinkURL(23,44,"mailto:jim#abcdef.com")
I am stumped trying to figure out how to attach it to the earlier bit. I feel like this is something simple I am forgetting. But I can't find it after much research. How do I hook up setUrlContent to the code above so that the command executes? I want to attach the bits above and get back to assigning it all to a variable I can put into a cell:
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com") // I can dynamically create up to here
.setLinkUrl(0,6,"mailto:fred#abcdef.com") // ...but these last couple lines are
.setLinkUrl(7,19,"mailto:jim#abcdef.com") // stuck in a string variable.
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
Thanks!
I believe your goal and situation as follows.
You want to use your script by dynamically changing the number of emails.
Modification points:
When your following script is run, I think that the links are reflected to mailto and fred#abcdef..
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
I thought that you might have wanted the linked email addresses like below.
fred#abcdef.com has the link of mailto:fred#abcdef.com.
jim#abcdef.com has the link of mailto:jim#abcdef.com.
In this answer, I would like to propose the modified script for above direction.
Modified script:
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => {
var v = e.trim();
return [v.split(":")[1], v];
});
var text = ar.map(([e]) => e).join(",");
var emailCell = SpreadsheetApp.newRichTextValue().setText(text);
var start = 0;
ar.forEach(([t, u], i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, u);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
In this modification, inputText is splitted to the hyperlink and the text (for example, when your sample value is used, it's fred#abcdef.com and mailto:fred#abcdef.com.), and the text including the hyperlink are put to the cell.
In this case, for example, even when var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com" is modified to var inputText = "mailto:fred#abcdef.com" and var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com,mailto:sample#abcdef.com", each hyperlink are reflected to each text.
Note:
When you want to the hyperlink of mailto:fred#abcdef.com to the text of mailto:fred#abcdef.com, you can also use the following modified script.
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => e.trim());
var emailCell = SpreadsheetApp.newRichTextValue().setText(inputText);
var start = 0;
ar.forEach((t, i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, t);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
References:
newRichTextValue()
Class RichTextValueBuilder
Class RichTextValue

Javascript dynamically generated variables with 'this'

This question relates directly to Highcharts, but is really more pertinent to general JavaScript and loops.
My code is as follows:
load:function(){
var series0 = this.series[0];
var series1 = this.series[1];
setTimeout(function(){
series0.addPoint(myjson.list[0].value);
series1.addPoint(myjson.list[1].value);
}, 1000);
}
I wanted to first show an example of code that works. With Highcharts, this code gathers the designated indexes from my JSON lists and appends them to my existing chart.
When attempting a for loop to perform the same action however I end up bungling it.
My for loop attempt:
var update = [];
for (i = 0; i < myjson.list.length; i++){
update[i] = this.series[i];
update.addPoint(myjson.list[i].Printvalue);
}
There is clearly something wrong with my loop logic, and yet I am unable to figure out exactly what. When running this loop code I get an error of:
update.addPoint is not a function
My biggest guess is it has to do with the way I am handling the this instance.
In your example, update is a normal array because you declare it with var update = []. The normal JavaScript array does not have a function called addPoint.
I'm not sure what it should be, but it definitely doesn't have anything to do with your use of this.
If the items in this.series include addPoint, you might want to use this:
update[i].addPoint(myjson.list[i].Printvalue);
Note the [i] after update.

Make Jquery\Javascript more Efficient

I have the following piece of code to generate an select list on the fly. The code works okay when the records retrieved are between 0-300 records. However when I try to work with records over this number, 671 records to be exact, the browsers(IE,FF,Chrome,Opera) hang and it seems the javascript is taking up a lot of resources and the browsers become slow after the select option is finally generated after a 5 minute wait....
I am looking for a way to make this more efficient and prevent such hangups as I will work with records upto 5000
$("#idea_selectrel").on("change",function(){
var attributeid = $("option:selected",this).val();
$.post("/OCSRM/PopulateHTMLTablesServlet.do",{"attributeid":attributeid,"table":'idearelations'},function(data){
if(!$.isEmptyObject(data))
{
$("#idea_selectrelvalue option:not(:first)").remove();
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
$("#idea_selectrelvalue").append(html).show();
}
}
else
{
alert('No data found.');
}
},'json');
//alert(attributeid);
});
Your code is building up a long string of HTML containing the options. It's also appending the string over and over again to the <select> element.
Move the .append() to after the loop:
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
Not being able to test the code but from what I can tell you're not clearing the html variable inside the loop, currently, the html will be added recursively for every iteration, and every iteration is updating the DOM, which will get pretty large, fast.
I'm not sure if this is by design, but if it's not, try moving the append outside of the loop.
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
That is a lot of DOM elements that will be slow to load even if created without JS. Assuming you don't have a way to pre-generate the select on the server and send it down, you could try not appending the value in the loop. You could build out a string of option values and then, after the string is created, set the html of the select to the generated string. The DOM manipulation in the loop could be a culprit at that level. However, it is important to note that large or deeply nested DOM elements can be a performance concern regardless.

Google spreadsheet query: How do I stop Range appearing as a result?

Good evening,
I'm trying to use some coding skills to help manipulate a spreadsheet in Google Docs and think the logic I've worked out is sound - but the script just returns results as 'Range'.
Essentially the script is a nested loop and (should) take values from six points in a row of data (starting in 11) and plonk them into a long vertical (column 38). Then it should move onto the next row.
I think it works, but the results just come back as 'Range' and can't see how to put the values into range, if that's what this means.
I also realise that it might be more effective to use a single array to gather the data on an individual array, but I'm still trying to get to grips with the syntax.
Here's my code:
function Transform() {
//load spreadsheet data and initialise variables
var sheet = SpreadsheetApp.getActiveSheet();
var firstrow = 11;//this is a manual figure
var d = firstrow;
var valuerplc = sheet.getRange(firstrow, 38);
var value =1;
for(var c=firstrow; c<sheet.getLastRow(); c++){
for (var e=3; e<33; e=e+6){
var mon = sheet.getRange(c, e);
valuerplc = sheet.getRange(d, 38);
valuerplc.setValue(mon);
}
d++;
}
}
Can anyone help, or at least point me in the right direction, please?
You've been caught with a common newbie problem, having trouble differentiating between Ranges and Values.
A Range expresses a region of a spreadsheet, with methods that give you access to various characteristics of the object. The contents, or data that you see in a spreadsheet is accessible via the .getValue() method (for one cell) or .getValues() (for more than one cell).
Change:
var mon = sheet.getRange(c, e);
To:
var mon = sheet.getRange(c, e).getValue();
... and you'll have the data that's in that range, as a String, Number or Date, depending on what was there.
The variable names you've selected may lead to confusion, it's worthwhile being careful with that. For example, look at valuerplc. The name implies that it's a value, which can be confused with .getValue() and .setValue(). But the way that it's used is as a Range, as in valuerplc.setValue(mon), so it's clearly NOT a value.
function Transform() {
//load spreadsheet data and initialise variables
var sheet = SpreadsheetApp.getActiveSheet();
var firstrow = 11;//this is a manual figure
var d = firstrow;
var valuerplc = sheet.getRange(firstrow, 38);
var value =1;
for(var c=firstrow; c<sheet.getLastRow(); c++){
for (var e=3; e<33; e=e+6){
var mon = sheet.getRange(c, e).getValue();
valuerplc = sheet.getRange(d, 38);
valuerplc.setValue(mon);
}
d++;
}
}

Categories

Resources