For loop with multiple variables and math calculations

Sonic42 21-05-2020

Essentially I have a calculated field that I want to end up with a rating %.

 

That rating % is going to be based on the values selected from a series of dropdowns in a subform. The drop downs have values from 0 to 5. Drop downs with a value of 0 are to be ignored and not included in the calculation.

 

For example, if there are 5 drop downs and 1 has a selcted value of 0, the rating percentage would be based on the values of 4 of the drop downs out of a maximum score of 20.

 

Here is what I have so far in the calculated field:

var Fields = xfa.resolveNodes("Page2.Comm_Sub.CommRtng[*]");
var Answered = 0;
var Values = 0;
var Rating = (Values / (Answered*5)) * 100

for (i=0; i<= Fields.length; i++){
     if (Fields.item(i).rawValue > 0) {
     Answered++;
     Values += Fields.item(i).rawValue;
     }
}
this.rawValue = Rating

 

As I said, it's a pretty basic loop (I think), but I cannot get it to output a value in Answered or Values or Rating or at least I don't think it is. For instance when I input xfa.host.messageBox(Answered) in the code to see what's happening, I only ever get a blank messageBox, same when I use "Values". So I know the loop is running, but it doesn't seem to be grabbing anything and updating the variables.

 

I'm also thinking that I have a glaring omission in the code that does not account for users going back and changing an entry. I think that will throw the Answered variable off. However, I have no idea how to account for that.

 

I'm trying to keep the code as basic as possible as 1. I'm exactly a master of JavaScript and 2. I have upwards of 20 different ratings to find across 12 different foms, where the number of drop downs relating to each rating can vary. So the more compact and easy to understand I can make it the better.

 

I have a sample PDF that I can send to anyone if that will help.

Mark Solution

Are these answers useful?
Help other community members by marking useful answers as accepted.

Accepted Solutions (0)

Answers (2)

Answers (2)

radzmar
MVP
30-05-2020

I think you should change the formula the following way, to get the right results.

 

var Fields = xfa.resolveNodes("CommRtng[*]"),
	Answered = 0, 
	Values = 0,
	Rating = 0,
	i;
	
for (i = 0; i < Fields.length; i += 1){
     if (parseInt(Fields.item(i).rawValue, 10) > 0) {
     	Answered += 1;
     	Values += parseInt(Fields.item(i).rawValue, 10);
     }
}
Rating = 100 / (Answered * 5) * Values
this.rawValue = Rating;

 

 

To fire this calculation just once, put the script into a click event of a button so it's executed only when the user clicks it. Otherwise it will always execute as soon a value of the related objects has been changed. 

 

There's also a way to calcuate this with FormCalc. This way needs just a single line of code with a combination of built-in functions.

// Short version which works but doesn't handle arithmetic over-/underuns when calculating with 0 values. 
$ = 100 / ($.resolveNodes("CommRtng.[$ gt 0]").length * 5) * Sum(CommRtng[*])

//  Full version which handles 0 values and formats the result.
$ = Choose(Within($.resolveNodes("CommRtng.[$.isNull eq 0]").length, 1, 5), Concat(Round(100 / Max($.resolveNodes("CommRtng.[$ gt 0]").length * 5, 1) * Max(Sum(CommRtng[*]), 1), 1), " %"))

 

 

radzmar
MVP
24-05-2020

Hi, 

 

your current script has some issues, so it could not work. Let me explain: 

 

var Fields = xfa.resolveNodes("Page2.Comm_Sub.CommRtng[*]");
var Answered = 0;
var Values = 0;
var Rating = (Values / (Answered*5)) * 100 // When executed here, it's just 0 and doesn't get updated later.
// var i is not declared

// i cannot be equal to Fields length because the nodelist created with resolveNodes() uses 0-based indexes, so i <= Fields.length creates an out-of-bounds-error.
for (i=0; i<= Fields.length; i++){ 
     if (Fields.item(i).rawValue > 0) {
     Answered++;
     Values += Fields.item(i).rawValue;
     }
}
this.rawValue = Rating

 

 

 

This way it will do what you're after. But I don't think the formula for the ratings is correct at all.

 

var Fields = xfa.resolveNodes("CommRtng[*]"),
	Answered = 0, 
	Values = 0,
	Rating = 0,
	i;
	
for (i = 0; i < Fields.length; i += 1){
     if (Fields.item(i).rawValue > 0) {
     	Answered += 1;
     	Values += Fields.item(i).rawValue;
     }
}
Rating = (Values / (Answered * 5)) * 100 // This formula seems to be wrong
this.rawValue = Rating;

 

 

HI radzmar, thank very much for helping. however, I still cannot seem to get the solution to work properly.

I'm still fudging around with it. I thought that having "i" declared twice might be a problem (in the for equation and as the item(i)). But changing the variable i to a in the for loop had no effect.

I also tried inserting messageBox pop up in between Answered and Values (xfa.host.messageBox(Answered.value)) in the for loop to see if both were firing and the messageBox never fired (pre and post changing i to a).

So somethng is hanging up somewhere it seems.

While the above likely highlights my ignorance of javaScript, I hope it indicates that I'm not just sitting back waiting for you to answer all my problems. I am giving a go, no matter how ineptly 😅.

I do have a sample PDF I can e-mail you if that helps.

 

On a side note, for the math equation, using BEDMAS it should work. For example if five drop downs are answered with rawValues of 4,4,4,4,0 the result should be:

Answered=4

Values=16

(Values/(Answered*5))*100

(16 / (4*5)) * 100,

(16/20) * 100,

0.8*100,

80%

Unless javaScript calculates it's math differently that is how I think it should work. Am I wrong in this assumption?

So progress has been made it seems, I can now get the code to fire (Sweet!), buuuut it fires everytime a selection is made.

 

I'm now working on tyring to figure out two things.

1. how to only get it to fire after all selections in the subform have been made

2. if the user goes back at a later time and changes one of their entries, deleting the stored value in Ratings and running the loop again to get the new adjusted values and creating a new value in Rating.

 

Onward I go...