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

Summing Total Number of Checkboxes in a Static Array using Sheet Workers

Hello All! I've been been grappling with this for way too long...and getting no where. I've scoured the forums and haven't found an answer yet. I have a sheet that I'm working on that has 37 skills in a Static section. Each of the skills has a checkbox.  I want to total the number of checkboxes that have been checked and put that total in a span linked to a hidden variable. For testing purposes, I tried to do this step by step with only 3 skills. I was able to get to work without using a loop. on("sheet:opened", function() { getAttrs(["accounting", "bullshit", "charm"], function(values) { let ascore = parseInt(values.accounting) || 0; let bscore = parseInt(values.bullshit) || 0; let cscore = parseInt(values.charm) || 0; let total = ascore + bscore + cscore; console.log(total); setAttrs({ iskill_total: total }); }); }); So my next step was trying to get this to work using a loop of an external loop inspired by this code from the Universal Sheetworkers Page: const stats = [ 'str' ,'dex','con',' int ','wis','cha']; stats. forEach ( function (stat) { on ( "change:" + stat + " sheet:opened" , function () { getAttrs ([stat], function (values) { const stat_score = parseInt (values[stat]) | | 0 ; // this extracts the stat, and assumes a stat score of 0 the stat is not recognised as a number. const stat_modifier = Math. floor (stat_score / 2) -5; // this gives a +0 at 10-11, and + / 1 for 2 point difference. setAttrs ({ [stat + '_mod' ]: stat_modifier }); }); }); }); So, I just couldn't get it to work. I tried a bunch of different options...but the problem is I want a running total. I tried some += excitement, but that didn't work. Is there some sort of function that will just total all the values in an array? Or is there some way to initialize an interim number to 0 outside of the for loop, pass that into the for function, have each skill set the Attribute of that number passed in to itself + the skill's value. Or is there a different way to do it? I notice that Aaron made a sheetworker for repeating sections...but I'm looking for a way to do this in non-repeating sections. There must be some sort of fairly easy way to do this.  I accidentally pulled an all-nighter trying to work this out. And I really should go to bed. If I can get this worked out, then I can move onto the next part of the character sheet project...I'm looking forward to this being my first character sheet that I submit to the repository. But I've got to figure this out first.  Thanks for any help y'all might have!
1720889338

Edited 1720907833
GiGs
Pro
Sheet Author
API Scripter
You do want to make a loop, but you have it backwards - you need to do the calculation of all 37 stats in a single sheet worker, instead of creating a sheet worker for each stat. There must be some sort of fairly easy way to do this. I'm tempted to say something like, sweet summer child. Nothing in roll20 is ever easy. But it is possible. There's multiple ways to do this., Unfortunately writing how to do it most easily requires one of the hardest methods to explain, so I won't do that. Here's one way to do it (I'll break it down below): const skills_37 = [ "accounting" , "bullshit" , "charm" , /* the rest of the stats */ ]; on ( ` ${ skills_37 . map ( skill => `change: ${ skill } ` ). join ( ' ' ) } sheet:opened` , () => {     getAttrs ( skills_37 , values => {         let total = 0 ;         skills_37 . forEach ( skill => {             const score = parseInt (values [ skill ]) || 0 ;             total += score ;         });         setAttrs ({             iskill_total : total         });     }); }); You start off with an array of skill names, and can change this as you see fit. The skill names must match the names in the HTML, except they should be in lower case. This line is probablly the most complex and needs some explanation: on ( ` ${ skills_37 . map ( skill => `change: ${ skill } ` ). join ( ' ' ) } sheet:opened` , () => { map() is a function that takes an array, and creates a new array of the same size - but transforms each item in the array. Here we are taking each attribute name, and adding change: to the start of it. join() is a function that takes an array and a separator, and converts that into a string, placing the separator between each item. I normally use a function called reduce() to do those two steps together, but map and join are easier to explain. Now we have a string that looks like change:accounting change:bullshit changecharm sheet:opened, exactly what that sheet worker needs on its first line. Then in getAttrs we use the same array again, since it already exists, and places the stat names and their values in an object called values . We have an array of the skill names, so we can loop that, get the values (which are all stored in the values object), and add them all together to get the total. Make sure you follow how that works, and here's another more complex but shorter worker that does the same thing. const changes = arr => arr . reduce (( all , one ) => ` ${ all } change: ${ one } ` , '' ); const skills_37 = [ "accounting" , "bullshit" , "charm" , /* the rest of the stats */ ]; on ( ` ${ changes ( skills_37 ) } sheet:opened` , () => {     getAttrs ( skills_37 , scores => {         const total = Object . values ( scores ). reduce (( all , one ) => all + ( parseInt ( one ) || 0 ), 0 );         setAttrs ({             iskill_total : total         });     }); }); In this code, the changes() function is a custom user function, and I'd move that to the start of the script block. You can use it any time you have an array of something and want to get a string for the on(change) line. For this to work, you only need change the const skill_37 line, and add extra skills.
1720890088

Edited 1720890100
GiGs
Pro
Sheet Author
API Scripter
PS: if you want to build a character sheet, look here: <a href="https://cybersphere.me/roll20/" rel="nofollow">https://cybersphere.me/roll20/</a> PPS: I wrote the universal sheet worker page.
1720891680
vÍnce
Pro
Sheet Author
Your example should go on your site GiGs.&nbsp; I think this would be a common function for many sheet authors.&nbsp;
1720893613
GiGs
Pro
Sheet Author
API Scripter
Vince, which part(s) do you think would be common functions?
1720895443

Edited 1720895656
vÍnce
Pro
Sheet Author
Just grabbing an array of attributes and summing them up. (similar to RepeatingSum but I think just a working example for handling an array of non-repeating attribute would be fine)
1720895671
GiGs
Pro
Sheet Author
API Scripter
A previous post did make me think that's probably a common desire.
1720895795

Edited 1720895885
vÍnce
Pro
Sheet Author
GiGs said: A previous post did make me think that's probably a common desire. I've most often just grabbed all the attribute values and summed them up, but that seems like a caveman approach vs using an array and iterating over it. ;-) Or maybe RepeatingSum could get a "bolt-on" to handle non-repeats?
GiGs said: PS: if you want to build a character sheet, look here: <a href="https://cybersphere.me/roll20/" rel="nofollow">https://cybersphere.me/roll20/</a> PPS: I wrote the universal sheet worker page. You wrote both those things?! I didn't realize! Thank you for all that work! I'd read your cybershere pages, I just hadn't gotten through all of them...but they've been my guide so far, step by step! I've been messing around with the code you provided and...then reading up on your site to answer some questions I had, so I was sure to understand each piece--including swapping out the int function for that parseInt, and swapping out the changes function you provided with the build_changes version here: <a href="https://cybersphere.me/function-library/" rel="nofollow">https://cybersphere.me/function-library/</a>.&nbsp; After some trial and error (the errors usually belong to missing parentheses), I got it working! Also learned about backticks. But, if you would, I do have a question (I will probably have other questions later...but that will about a different part of the sheet)-- To get the total, the code you provided here uses the Object.values(scores). On your function library page you have two different versions of this: const sum = (scores, int = false, fallback = 0) =&gt;&nbsp; &nbsp; &nbsp;scores.reduce((a, b) =&gt; a + (int ? int(b, fallback) : num(b, fallback)), 0); const sum_obj = (values, int = false, fallback = 0) =&gt;&nbsp; &nbsp; &nbsp;Object.values(values).reduce((a, b) =&gt;&nbsp; &nbsp; &nbsp; &nbsp; a + (int ? int(b, fallback) : num(b, fallback)), 0); Is the reason we are using the sum_obj version because we are inside a getAttrs function?
Oh! A Followup question! In various places I've seen admonitions not to sheet:opening &nbsp;everything. just have defaults and let it go. But a lot these sorts of sheetworkers *do* have sheet:opening. &nbsp;When *shouldn't* we be using that? Or when should we definitely be using it?
1720901596
GiGs
Pro
Sheet Author
API Scripter
TrooperSJP said: After some trial and error (the errors usually belong to missing parentheses), I got it working! Also learned about backticks. But, if you would, I do have a question (I will probably have other questions later...but that will about a different part of the sheet)-- To get the total, the code you provided here uses the Object.values(scores). On your function library page you have two different versions of this: const sum = (scores, int = false, fallback = 0) =&gt;&nbsp; &nbsp; &nbsp;scores.reduce((a, b) =&gt; a + (int ? int(b, fallback) : num(b, fallback)), 0); const sum_obj = (values, int = false, fallback = 0) =&gt;&nbsp; &nbsp; &nbsp;Object.values(values).reduce((a, b) =&gt;&nbsp; &nbsp; &nbsp; &nbsp; a + (int ? int(b, fallback) : num(b, fallback)), 0); Is the reason we are using the sum_obj version because we are inside a getAttrs function? The first one takes an array, which can be any old array, the second is specifically to work with obects (which are different from arrays). This is ideal for the object created by getAttrs, like getAttrs(an_array_here, an_object_here =&gt; { . Arrays are basically lists of things, while objects can be thought of as an indexed list. They have keys, which identify each item or list, whereas arrays have no named key. By the way, if there are any missing parenthesis in my code, I'd like to know so I could correct it.
GiGs said: By the way, if there are any missing parenthesis in my code, I'd like to know so I could correct it. That parenthetical problem was all me! I was typing in the code you gave me by hand so I could practice...I missed a parenthesis. But I got it!
1720901970

Edited 1720902209
GiGs
Pro
Sheet Author
API Scripter
TrooperSJP said: Oh! A Followup question! In various places I've seen admonitions not to sheet:opening &nbsp;everything. just have defaults and let it go. But a lot these sorts of sheetworkers *do* have sheet:opening. &nbsp;When *shouldn't* we be using that? Or when should we definitely be using it? sheet:opened is very handy when you are creating and still testing a sheet. Once you have the sheet finalised, it's best to remove all sheet:opened except those that are needed. This is an event that runs when the sheet is opened, which means when any player in the campaign opens that sheet it will run. When testing a sheet, the sheet is always being opened, so if any sheet workers are faulty, you can see it immediately. But if every sheet worker has the sheet:opened event, it means that when any player opens any sheet, every sheet worker in that sheet runs. This can create a lot of lag. It only happens once for each player , though - when the sheet is first opened. So if you are okay with a lot of lag when the sheet is opened, you can leave them in. There are some sheet workers that need sheet:opened to run properly, but they are very few. Things like checking the sheet is up to its latest version.
1720902275

Edited 1720902414
GiGs
Pro
Sheet Author
API Scripter
TrooperSJP said: GiGs said: By the way, if there are any missing parenthesis in my code, I'd like to know so I could correct it. That parenthetical problem was all me! I was typing in the code you gave me by hand so I could practice...I missed a parenthesis. But I got it! Whew! and parentheses, commas, semi-colons and similar characters can be a pain when programming. I know that pain well :)
Thank you for that answer!