Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

How to: Average/Max/Min from Feildsets

1538660055

Edited 1538660067
Axel
Pro
Sheet Author
Hey! I'd like to get some help to know if the following is possible: I want to have a repeating fieldset where each entry gives an input [number], and I want to get the average/maximum/minimum from that fieldset. I'm using the script " TheAaronSheet.js ", which is really helpful, and I can get the sum from the fieldset with " TAS.repeatingSimpleSum ". But is it possible to get the maximum/minimum or the number of instances of the fieldset to derive the average?
1538660988

Edited 1538661655
Jakob
Sheet Author
API Scripter
I don't know about TAS, but here's how you can get max, min, average, for the section repeating_foo and attribute name  bar : getSectionIDs("repeating_ foo ", (idArray) => { // Constructs an array containing attribute names for all rows const sourceAttrs = idArray.map(id => `repeating_foo_${id}_ bar `); getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); // Do more stuff with these values }); });
1538699918
GiGs
Pro
Sheet Author
API Scripter
Jakob, as a followup question: If i had two arrays, how would i multiply them together. Liek, starting from getSectionIDs("repeating_ foo ", (idArray) => { // Constructs an array containing attribute names for all rows const sourceAAttrs = idArray.map(id => `repeating_foo_${id}_ bar `); const sourceBAttrs = idArray.map(id => `repeating_foo_${id}_ lop `); getAttrs(sourceAAttrs.concat(sourceBAttrs), (values) => { // constructs an array containg attribute values for all rows const rowAValues = sourceAAttrs.map(attr => parseInt(values[attr], 10) || 0); const rowBValues = sourceBAttrs.map(attr => parseInt(values[attr], 10) || 0); const sumOfRows = rowAValues.reduce((m, x) => m + x, 0); }); }); What would I need to replace the sumOfRows line there to get the sum of the both arrays multipled together. Like say my first array was [1,2,3] my second array was [1,5,10] (the arrays are always  the same length), so the sum would be 1x1 + 2 x5 + 3 x10 = 41.
1538719999
Jakob
Sheet Author
API Scripter
The neatest (most functional, blah blah) way to do this would probably be Underscore's zip  method: _.zip(rowAValues, rowBValues)      // pairs up entries .map(([a, b]) => a*b)             // multiplies entries together .reduce((m, x) => m + x, 0);     // sums everything
1538754235
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Hmm, hadn't seen _.zip before Jakob. That could be very useful.
1538757238
Jakob
Sheet Author
API Scripter
I'm just waiting for The Aaron to laugh at me for going back on my principles and using a _ method instead of native ES6.
1538762787
GiGs
Pro
Sheet Author
API Scripter
Thanks, Jakob!
1539772151
Axel
Pro
Sheet Author
Hey! I wasn't able to look at this until now, and I can't seem to get this to work the way I want. Apparently, I'm such a novice at this that the "do more stuff" trips me up. Doesn't your code set maxOfRows  to a constant I can use? How exactly do I set it to an attribute? Wouldn't something like this work? on("change:repeating_foo remove:repeating_foo", function (eventInto) { getSectionIDs("repeating_foo", (idArray) => {   // Constructs an array containing attribute names for all rows   const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar`);   getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); // Do more stuff with these values setAttrs({barmax: maxOfRows});   }); }); });
1539773192
GiGs
Pro
Sheet Author
API Scripter
Yes, that should work, if you have an attribute called barmax. If you want to set all three, you should be able to use: on("change:repeating_foo remove:repeating_foo", function (eventInto) { getSectionIDs("repeating_foo", (idArray) => {   // Constructs an array containing attribute names for all rows   const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar`);   getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); let avgOfRows = Math.round(sumOfRows / numberOfRows); // Do more stuff with these values setAttrs({                              barmax: ma xOfRows,                             barmin: minOfRows,                             baravg: avgOfRows                         });   }); }); }); I had to guess at the names of your attributes. Change barmax , barmin , and baravg to whatever they should be.
1539775434
Axel
Pro
Sheet Author
Unfortunately, I'm not getting the attribute to display propery. The fieldset has a bar attribute like this: <input type="hidden" name="attr_bar" value="@{1ob}+@{2ob}+@{3ob}+@{4ob}"/> The [n]ob attributes are all checkboxes with a value of "1". Outside the fieldset, I've defined the barmax attribute and put a disabled input to show it, like so: <input type="hidden" name="attr_barmax" value="0"/> <input type="number" name="attr_showbarmax" value="@{barmax}" disabled="true"/> But the showbarmax attribute doesn't change when the fieldset changes.
1539777797
GiGs
Pro
Sheet Author
API Scripter
sheet workers cant modify disabled attributes, so for that bit to work you can drop the showbarmax input, and change the barmax to: <input type="number" name="attr_barmax" value="0" readonly/> readonly means it appears like a disabled input, and players cant edit it, but sheetworkers can. Your bar attribute is another problem. It currently is just a text field. To get it calculate as a number, you probably need to add the disabled=true, and maybe add [[ ]] around the calculation. BUT that wont work with your sheet worker. Sheet workers don't play nice with autocalc fields. You are better off setting the bar attribute value with another sheet worker. A fairly simple one: on ( "sheet:opened change:repeating_foo:1ob change: repeating_foo: 2ob change: repeating_foo: 3ob change: repeating_foo: 4ob " , function () { getAttrs ([ " repeating_foo_1ob", " repeating_foo_2ob", "repeating_foo_3ob", "repeating_foo_4ob"], function (v) { setAttrs ({ "bar" : parseInt("v.repeating_foo_1ob",10)||0 + parseInt("v.repeating_foo_2ob",10)||0 + parseInt("v.repeating_foo_3ob",10)||0 + parseInt("v.repeating_foo_4ob",10)||0 }); }); }); A question just occurred to me. Jakob used "repeating_foo" as an example and I've been using it here. What is your repeating section called? If your repeating section is called, for example, " mybars " you would need to replace every instance of foo with mybars in the above scripts.
1539781853
Axel
Pro
Sheet Author
I'm using the attribute names given in the examples in this discussion. I might change them once I have everything working. I know that sheet workers can't handle disabled inputs, that's why I was using hidden inputs. This is currently what my code looks like, and it's still not producing the desired result. HTML: <fieldset class="repeating_foo">      <input type="checkbox" name="attr_1ob" value="1"/>      <input type="checkbox" name="attr_2ob" value="1"/>      <input type="checkbox" name="attr_3ob" value="1"/>      <input type="checkbox" name="attr_4ob" value="1"/>      <input type="hidden" name="attr_bar" value="0"/>      <hr/>  </fieldset> <input type="number" name="attr_barmax" value="0" readonly /> SCRIPTS: on("sheet:opened change:repeating_foo:1ob change:repeating_foo:2ob change:repeating_foo:3ob change:repeating_foo:4ob", function() {     getAttrs(["repeating_foo_1ob", "repeating_foo_2ob", "repeating_foo_3ob", "repeating_foo_4ob"], function(v) {         setAttrs({             "bar": parseInt("v.repeating_foo_1ob",10)||0 + parseInt("v.repeating_foo_2ob",10)||0 + parseInt("v.repeating_foo_3ob",10)||0 + parseInt("v.repeating_foo_4ob",10)||0         });     }); }); on("change:repeating_foo remove:repeating_foo", function (eventInto) {     getSectionIDs("repeating_foo", (idArray) => {         // Constructs an array containing attribute names for all rows         const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar`);         getAttrs(sourceAttrs, (values) => {             // constructs an array containg attribute values for all rows             const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0);                          const numberOfRows = idArray.length;             const maxOfRows = Math.max(...rowValues);             const minOfRows = Math.min(...rowValues);             const sumOfRows = rowValues.reduce((m, x) => m + x, 0);                         let avgOfRows = Math.round(sumOfRows / numberOfRows);             // Do more stuff with these values             setAttrs({                 barmax: maxOfRows,                 barmin: minOfRows,                 baravg: avgOfRows             });         });     }); });
1539783521
GiGs
Pro
Sheet Author
API Scripter
just noticed an omission in my code That first sheet worker, the setAttrs line should be   "repeating_foo_bar": parseInt("v.repeating_foo_1ob",10)||0 + parseInt("v.repeating_foo_2ob",10)||0 + parseInt("v.repeating_foo_3ob",10)||0 + parseInt("v.repeating_foo_4ob",10)||0
1539783590

Edited 1539783868
GiGs
Pro
Sheet Author
API Scripter
I'm curious about what this repeating section is for. Can you describe what you actually want to use it for? Edit: also meant to add, its not good practice to start attribute names with a number: your 1ob, 2ob, etc., would be better written as ob1, ob2, etc. Also, i suggest you change this line in your repeating fieldset from hidden to number <input type="hidden" name="attr_bar" value="0"/> Just during testing, so you can see if it is actually being updated. When you have your scripts working, you can change it back to hidden.
1539784827
Jakob
Sheet Author
API Scripter
Just a note that the code by GG described above will not work when triggered by the sheet:opened  event, since you're not in the context of a repeating row. So you should probably remove the sheet:opened  from the list of events.
1539785388
Axel
Pro
Sheet Author
I'm making a private version for the Masks rpg sheet. I hasn't been updated in quite a while, so maybe I'll publish my version at some point. One of the playbooks has "contacts", and the PC has a number of "obligations" to each of these NPCs. You need to be able to roll [[2d6]] modified by the highest number of oblications to your contacts. For example, if you have the following four contacts: A = 1 obligation; B = 2 obligations; C = 2 obligations; D = 3 obligations, you would need to roll [[2d6 + 3]]. For testing purposes, I'm using repeating_foo instead of repeating_contacts . The 1ob attribute is because the original author of the sheet published for Masks used that practice. The code still isn't giving me the desired outcome though.
1539820946
GiGs
Pro
Sheet Author
API Scripter
"The code still isnt giving me the desired income" is not very informative. Does anything happen on any of the scripts? (Also Jakob is of course right. I quickly copied that bulk of that script and didnt think about it.)
1539846609
Axel
Pro
Sheet Author
I'm sorry I didn't include more info, but I made the changes you suggested. Nothing seems to be happening with the scripts, and no attributes are being updated. Can I get a log of what the scipt is doing to figure out where the error is? Is there something wrong with the order of operations perhaps? This is what I've got now. I have included two versions of bar for testing. <fieldset class="repeating_foo">     <input type="checkbox" name="attr_ob1" value="1"/>     <input type="checkbox" name="attr_ob2" value="1"/>     <input type="checkbox" name="attr_ob3" value="1"/>     <input type="checkbox" name="attr_ob4" value="1"/>     <input type="number" name="attr_barshow" value="@{ob1}+@{ob2}+@{ob3}+@{ob4}" disabled="true"/>     <input type="hidden" name="attr_bar1" value="[[@{ob1}+@{ob2}+@{ob3}+@{ob4}]]"/>     <input type="number" name="attr_bar2" value="0" readonly />     <hr/> </fieldset> <input type="hidden" name="attr_barmax1" value="0"/> <input type="number" name="attr_barmax1show" value="@{barmax}" disabled="true"/> <input type="number" name="attr_barmax2" value="0" readonly /> on("change:repeating_foo:ob1 change:repeating_foo:ob2 change:repeating_foo:ob3 change:repeating_foo:ob4", function() { getAttrs(["repeating_foo_ob1", "repeating_foo_ob2", "repeating_foo_ob3", "repeating_foo_ob4"], function(v) { setAttrs({ "repeating_foo_bar1": parseInt("v.repeating_foo_ob1",10)||0 + parseInt("v.repeating_foo_ob2",10)||0 + parseInt("v.repeating_foo_ob3",10)||0 + parseInt("v.repeating_foo_ob4",10)||0 }); }); }); on("change:repeating_foo:ob1 change:repeating_foo:ob2 change:repeating_foo:ob3 change:repeating_foo:ob4", function() { getAttrs(["repeating_foo_ob1", "repeating_foo_ob2", "repeating_foo_ob3", "repeating_foo_ob4"], function(v) { setAttrs({ "repeating_foo_bar2": parseInt("v.repeating_foo_ob1",10)||0 + parseInt("v.repeating_foo_ob2",10)||0 + parseInt("v.repeating_foo_ob3",10)||0 + parseInt("v.repeating_foo_ob4",10)||0 }); }); }); on("change:repeating_foo remove:repeating_foo", function (eventInto) { getSectionIDs("repeating_foo", (idArray) => { // Constructs an array containing attribute names for all rows const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar1`); getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); let avgOfRows = Math.round(sumOfRows / numberOfRows); // Do more stuff with these values setAttrs({ barmax1: maxOfRows, barmin1: minOfRows, baravg1: avgOfRows }); }); }); }); on("change:repeating_foo remove:repeating_foo", function (eventInto) { getSectionIDs("repeating_foo", (idArray) => { // Constructs an array containing attribute names for all rows const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar2`); getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); let avgOfRows = Math.round(sumOfRows / numberOfRows); // Do more stuff with these values setAttrs({ barmax2: maxOfRows, barmin2: minOfRows, baravg2: avgOfRows }); }); }); });
1539851165
GiGs
Pro
Sheet Author
API Scripter
Aha, found the error, and it was mine. I'm not sure why I had so many quotes on this line, must have been a sloppy cut & paste error. "repeating_foo_bar2": parseInt("v.repeating_foo_ob1",10)||0 + parseInt("v.repeating_foo_ob2",10)||0 + parseInt("v.repeating_foo_ob3",10)||0 + parseInt("v.repeating_foo_ob4",10)||0 To find the error, i rewrote the script as follows: on("change:repeating_foo:ob1 change:repeating_foo:ob2 change:repeating_foo:ob3 change:repeating_foo:ob4", function() {     console.log("================ Testing ============="); getAttrs(["repeating_foo_ob1", "repeating_foo_ob2", "repeating_foo_ob3", "repeating_foo_ob4"], function(v) {     const ob1 = parseInt(v.repeating_foo_ob1,10)||0,         ob2 = parseInt(v.repeating_foo_ob2,10)||0,         ob3 = parseInt(v.repeating_foo_ob3,10)||0,                 ob4 = parseInt(v.repeating_foo_ob4,10)||0;     let ob = ob1 + ob2 + ob3 + ob4;     console.log(ob1 + ob2 + ob3 + ob4 + ob); setAttrs({ repeating_foo_bar2: ob }); }); }); The console.log statements print to the browsers console, which you can usually find by pressing f12. Anyway, I've tested this and it works, and so does this version of Jakob's with it. on("change:repeating_foo remove:repeating_foo", function (eventInto) { getSectionIDs("repeating_foo", (idArray) => { // Constructs an array containing attribute names for all rows const sourceAttrs = idArray.map(id => `repeating_foo_${id}_bar2`); getAttrs(sourceAttrs, (values) => { // constructs an array containg attribute values for all rows const rowValues = sourceAttrs.map(attr => parseInt(values[attr], 10) || 0); const numberOfRows = idArray.length; const maxOfRows = Math.max(...rowValues); const minOfRows = Math.min(...rowValues); const sumOfRows = rowValues.reduce((m, x) => m + x, 0); let avgOfRows = Math.round(sumOfRows / numberOfRows); // Do more stuff with these values setAttrs({ barmax2: maxOfRows, barmin2: minOfRows, baravg2: avgOfRows }); }); }); });
1539859137
Axel
Pro
Sheet Author
Thank you so much to both of you! It's now working perfectly!