How can I make this Google Apps Script loop efficiently? - javascript

I'm making an script that increases the value of a cell by 0.01 until it matches the value of another cell (gets the value, pass through a formula than see if the other cell value matches). The problem is that it takes too long to execute. It was very simple to do on excel, but I don't know how to program in G-Apps Script (neither js).
I guess it's taking too long because it runs on the cloud. There is anyway I can solve it?
Here the code so far:
function Calculate() {
var ss = SpreadsheetApp.getActive();
var vF = ss.getSheetByName('magic').getRange('C31').getValue();
ss.getSheetByName('magic').getRange('C32').setValue(0);
var vE = ss.getSheetByName('magic').getRange('C32').getValue();
var vP
for(vE=0;vE != vP;vE+=0.01){
ss.getSheetByName('magic').getRange('C32').setValue(vE);
var qParc = vF - vE;
ss.getSheetByName('magic').getRange('C3').setValue(qParc);
vP = ss.getSheetByName('magic').getRange('F3').getValue();
}
Thanks in advance!

More likely it's running "too long" because vE != vP is never false, because the kind of floating point used by JavaScript (IEEE-754 double-precision binary floating point, used by most programming languages) is inherently imprecise. Famously, 0.1 + 0.2 is not 0.3 (it's 0.30000000000000004). As a result, it's unreliable to use == or != (or === or !==) with possibly-fractional numbers. (You're okay if they're integers, provided they're not really big ones.)
There probably isn't any need for a loop if you want to update a value to make it match another. Just take the difference and add that to the one you're updating.
But if you do need the loop, replace != with <.
for (vE = 0; vE < vP; vE += 0.01) {
You're also repeating a lot of operations, perhaps expensive ones, there. Once you have the object for (say) a sheet or cell, remember that object reference and reuse it:
function Calculate() {
var ss = SpreadsheetApp.getActive();
var magic = ss.getSheetByName('magic');
var c32 = magic.getRange('C32');
var c3 = magic.getRange('C3');
var f3 = magic.getRange('F3');
var vF = magic.getRange('C31').getValue();
var vE, vP, qParc;
for (vE = 0; vE < vP; vE += 0.01) {
c32.setValue(vE);
qParc = vF - vE;
c3.setValue(qParc);
vP = f3.getValue();
}
}
(I also removed an unnecessary var vE = c32.getValue(); from that, since you immediately overwrite vE with 0 at the beginning of the loop. I just added the declaration to the var declaring vP and, now, qParc.)

Related

Values are correct and within range but still sends an email

I have a Upper Limit and Lower Limit on my spreadsheet responses that I input towards the right of my responses. The limits are both on F2 and G2. F2 is Lower Limit and G2 is Upper Limit. The code is using the function onFormSubmit and it has an MailApp.sendEmail function that will send an email in case someone enters a figure that is out of the limit.
What I do not understand that, the value that I read when the form is submitted is actually within the range but it still triggers an email.
I have tried this code specifically on a brand new form and spreadsheet and it did not have any problem.
function onFormSubmit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Responses 1");
var speedUL = sheet.getRange("K2").getValue(); //250V
var speedLL = sheet.getRange("L2").getValue(); //207V
//var speedUL= parseInt(activess.getRange("K2").getValue()); //250V
//var speedLL = parseInt(activess.getRange("L2").getValue()); //207V
var speed1 = parseInt(e.values[2]);
if((speed1 >= speedUL) || (speed1 <= speedLL)){
console.log('SpeedUL: '+speedUL);
console.log('SpeedLL: '+speedLL);
console.log('Speed1: '+speed1);
var template1 = HtmlService.createTemplateFromFile("speed1");
template1.speed1 = speed1;
MailApp.sendEmail("someone#gmail.com ",
"Out of Range Notification Speed of VSD",
"",
{htmlBody : template1.evaluate().getContent()});
}else
{
return;
}
}
This is the test script that I am trying to do. With this code, I'm getting this
Error
Aug 26, 2019, 5:24:29 PM
TypeError: Cannot call method "getRange" of null.
at onFormSubmit(Code:7)
Line 7 refers to var speedUL = sheet.getRange("K2").getValue();
Probable Cause:
getRange() is called upon spreadsheet class (ss) and not on the sheet class (sheet). This usually means value of G2 and F2 of the first sheet is taken and not the specied sheet.
e.values is array of strings. Implicit conversion takes place when numbers are compared with strings. This is probably not the cause of your issue, but still needs to be addressed.
Solution:
Call getRange() on the sheet or specify the sheet name, when calling getRange() on spreadsheet.
Use explicit type conversion of e.value to number.
Snippet:
var temperature = Number(e.values[1]);
var temperatureUCL = ss.getRange("H3 Reponses!G2").getValue();
//or
var temperatureUCL = sheet.getRange("G2").getValue();
References:
Spreadsheet#getRange
Sheet#getRange

I want to obtain a certain part of text from a large webpage using Javascript, how do I?

There is a certain webpage which randomly generates a number, for example "Frequency : 21". I am trying to create a script which takes the number, 21, and compares it to another variable, then to an if else function. Basically, I've completed most of it, but I can't obtain the number 21. And since it is random, I can't put in a fixed value.
Can anyone help me out?
My code goes like:
setTimeout(MyFunction,5000)
function MyFunction(level,legmin) {
var level = x
var legmin = 49
if (level <= legmin) {
location.reload(true)
}
else {
alert("Met requirements.")
}
where the address of the text I want is:
html>body>div#container>div#contentContainer>div#content>
div#scroll>div#scrollContent>div>div>div#pkmnappear>form>p (x in the code above).
A quick-n-dirty solution without regex.
var lookFor = "Frequency : ";
var text = document.querySelector("#pkmnappear>form>p").textContent;
var level = text.substr(text.indexOf(lookFor) + lookFor.length).split(" ")[0];
This assumes the number will be followed by a space

Big-integer Fibonacci sequence diverges at 36'th term: Project Euler #25

So I am trying to solve project euler #25 via the big integer brute force method with this module. Everything seems to go properly until the 36'th term, which actually decreases. Then the terms increase as they should, and then decrease again; they never go over 10 million. I have also noticed the 36'th term has all digits correct except one as it is supposed to be 14930352 but I get 4930352 Could this be a problem with my code or a bug in the module?
var bigInt = require('big-integer');
var number = bigInt(1);
var last = bigInt(1);
for(i=0;i<50;i++){
number = number.add(last);
last = number.minus(last);
console.log(number.toString());
}
Looks like a bug in the library. If you use a tmp variable it works fine.
var bigInt = require('big-integer');
var number = bigInt(1);
var last = bigInt(1);
for(i=0;i<50;i++){
//number = number.add(last);
//last = number.minus(last);
var tmp = number.add(last);
last = number;
number = tmp;
console.log((i + 3) + ":" + number.toString());
}

Javascript Big Decimal - Nth Root

Recently I run into the well known floating point precision errors of Javascript. Usually I would avoid floating point calculations on the thin client & rather leave it to the back-end.
I started using the big.js library created by Michael Mclaughlin. Though it has a square-root method/function, it does not have a nth-root methods/function nor does the power function support fraction values as arguments.
So I was wondering if anyone using the library has extended it to have such a function or at least use it to calculate accurate nth-root results.
Michael Mclaughlin suggested that I implement such a function similar in structure to the square-root function. However my attempts at understanding the logic proofed my maths-disability, resulting in simple calculations yielding very wrong results.
Using the algorithm on Rosetta Code also yields incorrect results.
So I was wondering if anyone using the library has extended it to have such a function or at least use it to calculate accurate nth-root results.
Here is the code to my last attempt:
P['nthrt'] = P['nthroot'] = function (n, prec)
{
var negate, r,
x = this,
xc = x['c'],
i = x['s'],
e = x['e'];
// Argument defaults
n = n || 2;
prec = prec || 12;
// Zero?
if ( !xc[0] ) {
return new Big(x)
}
// Negative?
negate = ( n % 2 == 1 && i < 0 );
// Estimate.
r = new Big(1); // Initial guess.
for (var i = 0; i < prec; i++) {
r = (ONE.div(n)).times(r.times(n-1).plus(x.div(r.pow(n-1))));
}
if (negate) r['s'] = -1;
return r;
};
It does not even get obvious results correct like the 4th root of 81 = 3, instead it gets 3.00000000xxx
Newton's method only gives an approximation for the root, so 3.0000xxx should be expected. If you know that the answer should be an integer, you can round r down (Newton's method overestimates the root) and check that r^n=x.
You can use big-numbers library to solve your problem. They support sqrt, pow, exp and many other features.
The pow method accept positive, negative, integer and floating point numbers:
var bn = new BigNumber();
var value = bn.of('81');
var xRoot = value.pow(0.25);
console.log('Result: ' + bn.format(xRoot));
You can use Basenumber.js to perform nth root. Documentation here.
E.g.
// Set precision decimals required
Base.setDecimals(25);
let x = Base("1e+10");
console.log(x.root(10).toString());
<script src='https://cdn.jsdelivr.net/gh/AlexSp3/Basenumber.js#main/BaseNumber.min.js'></script>

Excel spreadsheet formula to javascript question #2

Hey again everyone. Yet again i am having some problems with trying to get the match correct on this Excel Spreadsheet to JavaScript conversion.
Here is the excel formula:
=IF(IF($B$7=TRUE,$B$28/$B$10,$B$28/$B$5)>1,1,IF($B$7=TRUE,$B$28/$B$10,$B$28/$B$5))
WHERE
B7 = TRUE
B28 = 76800
B10 = 892015
B5 = 999500
And this is my JavaScript i have so far:
function percent(x) { return Math.round((x-0)*100) + '%'; }
if($('#section179').is(':checked'))
{
var percentRepaid = $("#rev3DScanYear").val() / $("#section179Real").val();
if (percentRepaid > 1)
{
$("#paymentCashPer").val('100.00');
}else
{
percentRepaid = $("#rev3DScanYear").val() / $("#SalePrice").val();
$("#paymentCashPer").val(percent(percentRepaid));
}
}else
{
//to be done
}
WHERE
rev3DScanYear = 76800
SalePrice = 999500
section179Real = 892015
For the JavaScript code i keep getting a value of 8% and i should be getting a value of 8.61% as it has on the spreadsheet.
As always, any help would be great! :o)
David
Math.round((x-0)*100) makes x an integer.
You could try Math.round(((x-0)*100)*100)/100 which makes the x = 8.609720... into x=861 and then divides it to get the x=8.61 you're looking for, which is what they would suggest here.
...Also, not really sure why you're subtracting 0 from x...?
Ok, so I've been looking at this again, and I think I didn't look deeply enough the first time.
The logic, if I understand it, is this:
If Section179 is checked then Divisor is Section179Real, else it is SalePrice.
Give me the smaller of 1.00 or (rev3DScanYear / Divisor).
If that's correct, you can do it in excel with =MIN(1,$B28/IF($B$7=TRUE,$B$10,$B$5)) (same references), which means that the following should do what you want it to:
var Divisor = $("#SalePrice");
if($('#section179').is(':checked'))
{
Divisor = $("#section179Real");
}
$("#paymentCashPer").val(Math.round(100*(Math.min(1, $("#rev3DScanYear")/Divisor)*100)/100;

Categories

Resources