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

Sheet Worker NaN Issue

Hello. I've been working on this sheet all weekend, and I'm basically teaching myself html and javascript from scratch (I had a little CSS experience from work before I started.) One of the common complaints about this sheet (the L5R sheet) is that it allows you to have a 0 insight rank, which the game doesn't allow for. The problem is that the original designer set it up as a simple equation and first insight rank is larger than the others, making the math difficult without if/then statements. So it seems like a good opportunity to teach myself sheet workers. I started with this. <table> <tr> <td><label style=display:inline><span data-i18n="rank-u">Rank</span></label></td> <input type="hidden" name="attr_foo_rank" value="1" /> <td><input type="number" disabled="true" name="attr_Rank" value="@foo_rank" /></td> <script type="text/worker">   on("change:Insight", function() {     getAttrs(["Insight"], function(values) {       setAttrs({ foo_rank: Math.min(1, (((Insight - 112) / 25))) });     });   }); </script> Where insight is a huge equation autocalculated from the rest of the sheet. When I try that, it tells me: Uncaught Error: Firebase.update failed: First argument contains NaN in property 'campaign-2255347-jUnJlr1INiKphJdb3_qUCA.char-attribs.char.-Kk3mcF88SdpiHEggG0P.-KknGvtMPZRCleFixvpi.current'. Thinking that the insight was coming in as a non-integer, I tried this: <table> <tr> <td><label style=display:inline><span data-i18n="rank-u">Rank</span></label></td> <input type="hidden" name="attr_foo_rank" value="1" /> <td><input type="number" disabled="true" name="attr_Rank" value="@foo_rank" /></td> <script type="text/worker">   on("change:Insight", function() {     getAttrs(["Insight"], function(values) {       var tempinsight = parseInt(values.Insight);         setAttrs({ foo_rank: Math.min(1, (((tempinsight - 112) / 25))) });       });     }); </script> Same thing, although if I take out the setAttrs line, there are no errors, so assuming I'm allowed to using the parseInt line in a sheetworker, it's not throwing the NaN error. It's just the setAttrs. The equation for insight, btw is: <td><input type="number" disabled="true" name="attr_Insight" value="(((@{Air} + @{Water} + @{Fire} + @{Earth} + @{Void}) * 10 ) + (@{rank_acting} + @{rank_artisan} + @{rank_calligraphy} + @{rank_courtier} + @{rank_divination} + @{rank_etiquette} + @{rank_games} + @{rank_investigation} + @{rank_lore} + @{rank_Lore2} + @{rank_Lore3} + @{rank_Medicine} + @{rank_Meditation} + @{rank_Perform} + @{rank_Sincerity} + @{rank_Spellcraft} + @{rank_Tea_Ceremony} + @{rank_rhigh} + @{rank_Athletics} + @{rank_Battle} + @{rank_Defense} + @{rank_Horsemanship} + @{rank_Hunting} + @{rank_Iaijutsu} + @{rank_Jiujutsu} + @{rank_Chain_Weapons} + @{rank_Heavy_Weapons} + @{rank_Kenjutsu} + @{rank_Knives} + @{rank_Kyujutsu} + @{rank_Ninjutsu} + @{rank_Polearms} + @{rank_Spears} + @{rank_Staves} + @{rank_WarFan} + @{rank_AnimalHandling} + @{rank_Commerce} + @{rank_Craft} + @{rank_Engineering} + @{rank_Sailing} + @{rank_Forgery} + @{rank_Intimidation} + @{rank_SleightofHand} + @{rank_Stealth} + @{rank_Temptation} + @{BCourtier} + @{BEtiquette} + @{AddInsight}))">addl SPs></td> I inherited a lot of this, of course. So I'm guessing I just have a basic misunderstnading here and I need someone to set me straight on some fundamentals. Thanks in advance.
1495514784
chris b.
Pro
Sheet Author
API Scripter
you cannot use the result of autocalc fields in a sheetworker, those come back just as a string, literally the first chaacter will be "@" or whatever yours is. you have to replicate the entire calculation in javascript, getting all the fields needed in your getAttrs. you could then change your Insight field to a readonly number and set it via sheetworker , if it is useful t have and not have to keep calculating.
With an equation that big, is there a practical way to do it as a sheet worker, or do I just set it to "on change" and list every field in the equation?" Thanks for answering my newb question. I knew I was running into a basic fundamentals question. This afternoon I spent an hour and a half figuring out you couldn't use a boolean in an autocalc. :)
1495520613
Lithl
Pro
Sheet Author
API Scripter
Robert D. said: With an equation that big, is there a practical way to do it as a sheet worker, or do I just set it to "on change" and list every field in the equation?" You would have to listen to changes to any of the component attributes (and if any of them are autocalc, all of their  component attributes) and do the calculation in JS. Alternatively, you can try  sheetworker-autocalc . Using it would be: on('change:Insight', function() { resolveAutocalc('Insight', function(values) { // note that values.Air, values.Water, etc. will all be present in addition to values.Insight setAttrs({ foo_rank: Math.min(1, (((values.Insight - 112) / 25))), }); }); });
Do I just paste the sheetworker-autocalc.js code directly into the sheet, above where I want to use it?
1495559488
Lithl
Pro
Sheet Author
API Scripter
You can put it below your code if you want, as well, since the callback function to your on('...') even handler won't run until long after the sheetworker-autocalc code regardless of whether you put it above or below. You can also use the contents of sheetworker-autocalc.min.js to save space, if you like (they do the same thing, but one is 143 lines including comments, while the other is 25). It does need to be within the same <script> tag, though.
1495606933

Edited 1495609247
Robert D.
Sheet Author
This will be a long one. Sorry. If you get me through it, I bet my future questions will be much simpler. :) Okay, I wasn't able to get the Sheetworker Autocalc to work, it kept telling me "val" was undefined, but instead of coming back here to help, I decided that I'd be better served, in the long run as a sheet author, if I buckled down and just started converting all the autocalcs on this sheet to sheet workers. At least the ones that only change when you spend XP. The way I understand it, that's best practices on a character sheet anyway (I believe this sheet is older than sheet workers, so no bad reflection on the original author). So I've just started with the chain directly up from equation I originally asked about. I didn't have much trouble with the stats themselves. Now if you raise an attribute it checks and resets a person's relevant ring using Math.min. I hit my next big issue one level up from that, calculating insight. I need to add together 10 points for every ring (that's working) and one point for every skill. My problem is the repeating skill sections. So I believe I need sheet worker section that adds together all the static fields, then does a getID on the four sets of repeating skill sections and tallies the ranks adding that to the total. I'll start with my attempt, which isn't working (it breaks on the repeating section, the other works fine): <script type="text/worker"> on("change:foo_air change:foo_water change:foo_fire change:foo_earth change:void change:rank_acting change:rank_artisan change:rank_calligraphy change:rank_courtier change:rank_divination change:rank_etiquette change:rank_games change:rank_investigation change:rank_lore change:rank_lore2 change:rank_lore3 change:rank_medicine change:rank_meditation change:rank_perform change:rank_sincerity change:rank_spellcraft change:rank_tea_ceremony change:rank_rhigh", function(eventInfo) { getAttrs(["foo_air", "foo_water", "foo_fire", "foo_earth", "void", "rank_acting", "rank_artisan", "rank_calligraphy", "rank_courtier", "rank_divination", "rank_etiquette", "rank_games", "rank_investigation", "rank_lore", "rank_lore2", "rank_lore3", "rank_medicine", "rank_meditation", "rank_perform", "rank_sincerity", "rank_spellcraft", "rank_tea_ceremony", "rank_rhigh"], function(values) { setAttrs({ foo_insight:((values.foo_air * 10)+(values.foo_water * 10)+(values.foo_fire * 10)+(values.foo_earth * 10)+(values.void * 10) + parseInt(values.rank_acting) + parseInt(values.rank_artisan) + parseInt(values.rank_calligraphy) + parseInt(values.rank_courtier) + parseInt(values.rank_divination) + parseInt(values.rank_etiquette) + parseInt(values.rank_games) + parseInt(values.rank_investigation) + parseInt(values.rank_lore) + parseInt(values.rank_lore2) + parseInt(values.rank_lore3) + parseInt(values.rank_medicine) + parseInt(values.rank_meditation) + parseInt(values.rank_perform) + parseInt(values.rank_sincerity) + parseInt(values.rank_spellcraft) + parseInt(values.rank_tea_ceremony) + parseInt(values.rank_rhigh) + parseInt(values.rank_rehigh)) }); }); }); on("change:repeating_rehigh:rank_rehigh remove:repeating_rehigh", function (e) { getSectionIDs("repeating_rehigh", function (idarr) { getAttrs(["foo_insight"], function(values) { var totalranks = 0; var rankTally = _.after(idarr.length, function () { setAttrs({ foo_insight: Math.round(foo_insight+totalranks) }); }); for (var i = 0; i < idarr.length; i++) { getAttrs(["repeating_rehigh_" + idarr[i] + "_rank_rehigh"], function (v) { totalranks += parseFloat(v[Object.keys(v)[0]]); rankTally(); }); }; }); }); }); </script> I'll need to do this for "rehigh", "rweapon", "rcraft", and "rlow". I suspect I'm hitting a problem when I trying to add foo_insight to itself plus the new total, but I'm not sure. That might be all you need but here are the sections where I have the repeating fields set up, for reference. That's the rest of this post, if you have enough to stop reading. :) <div> <fieldset> <div style="width: 100%;"> <table style="width:100%;"> <td><input style="width:75px;" type="text" name="attr_rehigh" value=""></td> <td><input type="checkbox" name="attr_rehigh_school" value="1"/></td> <td><input type="number" name="attr_rank_rehigh" value="0"></td> <td><select name="attr_rehigh_stat"> <option value="@{Agility}" data-i18n="agility-u">Agility</option> <option value="@{Awareness}" data-i18n="awareness-u">Awareness</option> <option value="@{Intelligence}" data-i18n="intelligence-u">Intelligence</option> <option value="@{Perception}" data-i18n="perception-u">Perception</option> <option value="@{Reflexes}" data-i18n="reflexes-u">Reflexes</option> <option value="@{Stamina}" data-i18n="stamina-u">Stamina</option> <option value="@{Strength}" data-i18n="strength-u">Strength</option> <option value="@{Void}" data-i18n="void-u">Void</option> <option value="@{Willpower}" data-i18n="willpower-u">Willpower</option> </select></td> <input type="hidden" name="attr_roll_rehigh" value="@{rank_rehigh}+@{rehigh_stat}" disabled> <input type="hidden" name="attr_keep_rehigh" value="@{rehigh_stat}" disabled> <td><input type="number" name="attr_roll_bonus_rehigh" value="0"></td> <td><input type="number" name="attr_keep_bonus_rehigh" value="0"></td> <td><input type="number" name="attr_skill_bonus_rehigh" value="0"></td> <td><input type="text" name="attr_rehigh_Emphases" value=""></td> <td><button type="roll" value="/em ... //just roll stuff// </button></td> <td><button type="roll" value="/em ... //just roll stuff// </button></td> <td><button type="roll" value="/em ... //just roll stuff// </button></td> <td><input type="text" name="attr_rhigh_type" value=""></td> </table> </div> </fieldset> </div> <div> <fieldset> <div style="width: 100%;"> <table style='border-collapse: collapse; border-spacing: 0; width:100%'> <td><input style="width:85px;" type="text" name="attr_rweap" value="Weapons"></td> <td><input type="checkbox" name="attr_rweap_school" value="1"/></td> <td><input type="number" name="attr_rank_rweap" value="0"></td> <td><select name="attr_rweap_stat"> <option value="@{Agility}" data-i18n="agility-u">Agility</option> <option value="@{Awareness}" data-i18n="awareness-u">Awareness</option> <option value="@{Intelligence}" data-i18n="intelligence-u">Intelligence</option> <option value="@{Perception}" data-i18n="perception-u">Perception</option> <option value="@{Reflexes}" data-i18n="reflexes-u">Reflexes</option> <option value="@{Stamina}" data-i18n="stamina-u">Stamina</option> <option value="@{Strength}" data-i18n="strength-u">Strength</option> <option value="@{Void}" data-i18n="void-u">Void</option> <option value="@{Willpower}" data-i18n="willpower-u">Willpower</option> </select></td> <input type="hidden" name="attr_roll_rweap" value="@{rank_rweap}+@{rweap_stat}" disabled> <input type="hidden" name="attr_keep_rweap" value="@{rweap_stat}" disabled> <td><input type="number" name="attr_roll_bonus_rweap" value="0"></td> <td><input type="number" name="attr_keep_bonus_rweap" value="0"></td> <td><input type="number" name="attr_skill_bonus_rweap" value="0"></td> <td><input type="text" name="attr_rweap_Emphases" value=""></td> <td><button type="roll" value="/em @{Name} makes an Untrained @{rweap} roll! \n\n/roll [[@{roll_rweap}+@{roll_bonus_rweap}]]d10k[[@{keep_rweap}+@{keep_bonus_rweap}]]"></button></td> <td><button type="roll" value="/em @{Name} makes a Trained @{rweap} roll! \n\n/roll [[@{roll_rweap}+@{roll_bonus_rweap}]]d10k[[@{keep_rweap}+@{keep_bonus_rweap}]]!!"></button></td> <td><button type="roll" value="/em @{Name} makes an @{rweap} roll with Emphasis! \n\n/roll [[@{roll_rweap}+@{roll_bonus_rweap}]]d10k[[@{keep_rweap}+@{keep_bonus_rweap}]]!!r1"></button></td> </table> </div> </fieldset> </div> You get the gist. rcraft, and rlow are much the same.
1495610584

Edited 1495620324
Jakob
Sheet Author
API Scripter
So, here's one tip without getting into the meat of your problem: if you have a long list of stats that you want to use in some repeating manner, like adding them up, you can use some array manipulation to make everything more readable. Like this: // Add as many attribute names as you want var elements = ['foo_air', 'foo_water', 'foo_earth']; var ranks = ['rank_acting', 'rank_artisan']; // [...elements, ...ranks] is ['foo_air', 'foo_water', 'foo_earth', 'rank_acting', 'rank_artisan'] // array.map(s => `change:${s}`).join(' ') first prepends "change:" to every string in array, then joins them by a space on([...elements, ...ranks].map(s => `change:${s}`).join(' '), function() { getAttrs([...elements, ...ranks], function(values) { // Adds together the elements (multiplied by 10) and the ranks (but first perform parseInt on them). // The 0 (second argument of reduce) is the starting value for the reduction. let sum = elements.reduce((s, k) => (s + parseInt(values[k])*10), 0) + ranks.reduce((s, k) => (s + parseInt(values[k])), 0); setAttrs({foo_insight: sum}); }); }); Okay, for your second part, instead of your complicated asynchronous solution (by the way, one reason it fails is that foo_insight is undefined... you should use values.foo_insight), use something more direct, with only one getAttrs: on("change:repeating_rehigh:rank_rehigh remove:repeating_rehigh", function (e) { getSectionIDs("repeating_rehigh", function (idarr) { let allRehighs = idarr.map(id => "repeating_rehigh_" + id + "_rank_rehigh"); getAttrs(["foo_insight", ...allRehighs], function (values) { let sum = parseFloat(values.foo_insight) + allRehighs.reduce((s, k) => (s + parseFloat(values[k])), 0); setAttrs({foo_insight: Math.round(sum)}); }); }); });
That, Jakob , is brilliant. Thank you, sir! This is exactly the sort of thing I need in my neophyte state, people pointing out to me better ways to do things. So I'm assuming that when I add a second repeating in there, it would grow to look like this: on("change:repeating_rehigh:rank_rehigh remove:repeating_rehigh", function (e) { getSectionIDs("repeating_rehigh", function (idarr) { let allRehighs = idarr.map(id => "repeating_rehigh_" + id + "_rank_rehigh"); getAttrs(["foo_insight", ...allRehighs], function (values) { let sum = parseFloat(values.foo_insight) + allRehighs.reduce((s, k) => (s + parseFloat(values[k])), 0); setAttrs({foo_insight: Math.round(sum)}); }); }); }); on("change:repeating_rweapon:rank_rweapon remove:repeating_rweapon", function (e) { getSectionIDs("repeating_rweapon", function (idarr) { let allRweapons = idarr.map(id => "repeating_rweapon_" + id + "_rank_rweapon"); getAttrs(["foo_insight", ...allRehighs], function (values) { let sum = parseFloat(values.foo_insight) + allRwapons.reduce((s, k) => (s + parseFloat(values[k])), 0); setAttrs({foo_insight: Math.round(sum)}); }); }); }); One issue. It's not reseting between increments. So if insight is 200 and there are 10 ranks in the repeating section, and I raise it one, it goes to 210. If I lower it one to 9, it then goes to 219, if I lower it again, it goes to 227. So I need to add a line in there to reset the variable. is it "sum" I need to 0 out and do I need to zero it between each section for repeating fields? I'm guessing that's the variable keeping that running total.
1495643538

Edited 1495643608
Jakob
Sheet Author
API Scripter
In principle correct for the second section. Small oversight: you still use allRehighs in the getAttrs argument, not allRweapons. However, this approach does not work, as you have seen, because: sum is reset between iterations, because its scope is the function block it is running in. BUT: The problem is that each change does not recalculate insight from the ground up, it just adds to the current value. Solution: you need to calculate foo_insight from scratch every time. (you could  also store the result of the first calculation in a secondary foo_insight_base variable and use that one instead for the calculation, but that brings other problems). This type of syntax works for two repeating sections, but gets increasingly cumbersome the more sections you add, because it becomes deeper and deeper nested. So if you want more sections, this needs to be written in a better way. var elements = ['foo_air', 'foo_water', 'foo_earth'],   ranks = ['rank_acting', 'rank_artisan'],   eventString = [...elements, ...ranks].map(s => `change:${s}`).join(' ') +   ' change:repeating_rehigh:rank_rehigh remove:repeating_rehigh' +   ' change:repeating_rweapon:rank_rweapon remove:repeating_rweapon'; // This function does the actual calculation and attribute-setting after we have all the names var calcFooInsight = function (repeatingNames) {   getAttrs([...elements, ...ranks, ...repeatingNames], function (values) {     let sum = elements.reduce((s, k) => (s + (parseInt(values[k]) * 10 || 0)), 0) +       ranks.reduce((s, k) => (s + (parseInt(values[k]) || 0)), 0) +       repeatingNames.reduce((s, k) => (s + (parseFloat(values[k]) || 0)), 0);     setAttrs({       foo_insight: Math.round(sum)     });   }); }; // Collect necessary data for calculation on(eventString, function () {   getSectionIDs('repeating_rehigh', function (idArrayRehigh) {     getSectionIDs('repeating_rweapon', function (idArrayRweapon) {       let attrNamesRehigh = idArrayRehigh.map(id => "repeating_rehigh_" + id + "_rank_rehigh"),         attrNamesRweapon = idArrayRweapon.map(id => "repeating_rweapon_" + id + "_rank_rweapon");       calcFooInsight([...attrNamesRehigh, ...attrNamesRweapon]);     });   }); });
1495645655

Edited 1495647554
Lithl
Pro
Sheet Author
API Scripter
Robert D. said: Okay, I wasn't able to get the Sheetworker Autocalc to work, it kept telling me "val" was undefined Looks like I've got a typo: line 78 of sheetworker-autocalc.js should be  getAttrs([name], function(val) {  instead of  getAttrs([name], function(Val) { I've got no idea how that didn't cause problems when I was initially testing it. =/ Edit: Fixed now
Nevermind,  Jakob , I read more carefully and saw that the rweap was in there too. I also had a copy/paste error in my first test. So it all works. I've added the other repeating sections and it tests out. Now I can start adding in the individual items to the arrays.
1495650780
Jakob
Sheet Author
API Scripter
Robert D. said: Nevermind,  Jakob , I read more carefully and saw that the rweap was in there too. I also had a copy/paste error in my first test. So it all works. I've added the other repeating sections and it tests out. Now I can start adding in the individual items to the arrays. It should still have the problem that changing it again increments each time unless you change the way it's calculated somehow (like in my example or otherwise).
Well, if I change it down now, it goes down, it doesn't go up, so I don't seem to need to reset any variables. It seems to be creating an accurate number, not an inflationary one.
Jakob , here' my final implementation, after looking at your very nice code. I don't FULLY understand it. I have to spend some time on w3schools reading their section on arrays, I think. :) var elements = ['foo_air', 'foo_earth', 'foo_fire', 'foo_water', 'void'], ranks = ['rank_acting', 'rank_artisan', 'rank_calligraphy', 'rank_courtier', 'rank_divination', 'rank_etiquette', 'rank_games', 'rank_investigation', 'rank_lore', 'rank_Lore2', 'rank_Lore3', 'rank_Medicine', 'rank_Meditation', 'rank_Perform', 'rank_Sincerity', 'rank_Spellcraft', 'rank_Tea_Ceremony', 'rank_rhigh', 'rank_Athletics', 'rank_Battle', 'rank_Defense', 'rank_Horsemanship', 'rank_Hunting', 'rank_Iaijutsu', 'rank_Jiujutsu', 'rank_Chain_Weapons', 'rank_Heavy_Weapons', 'rank_Kenjutsu', 'rank_Knives', 'rank_Kyujutsu', 'rank_Ninjutsu', 'rank_Polearms', 'rank_Spears', 'rank_Staves', 'rank_WarFan', 'rank_AnimalHandling', 'rank_Commerce', 'rank_Craft', 'rank_Engineering', 'rank_Sailing', 'rank_Forgery', 'rank_Intimidation', 'rank_SleightofHand', 'rank_Stealth', 'rank_Temptation', 'BCourtier', 'BEtiquette', 'AddInsight'], eventString = [...elements, ...ranks].map(s => `change:${s}`).join(' ') + ' change:repeating_rehigh:rank_rehigh remove:repeating_rehigh' + ' change:repeating_rweap:rank_rweap remove:repeating_rweap' + ' change:repeating_rcraft:rank_rcraft remove:repeating_rcraft' + ' change:repeating_rlow:rank_rlow remove:repeating_rlow'; // This function does the actual calculation and attribute-setting after we have all the names var calcFooInsight = function (repeatingNames) { getAttrs([...elements, ...ranks, ...repeatingNames], function (values) { let sum = elements.reduce((s, k) => (s + (parseInt(values[k]) * 10 || 0)), 0) + ranks.reduce((s, k) => (s + (parseInt(values[k]) || 0)), 0) + repeatingNames.reduce((s, k) => (s + (parseFloat(values[k]) || 0)), 0); setAttrs({ foo_insight: Math.round(sum) }); }); }; // Collect necessary data for calculation on(eventString, function () { getSectionIDs('repeating_rehigh', function (idArrayRehigh) { getSectionIDs('repeating_rweap', function (idArrayRweap) { getSectionIDs('repeating_rcraft', function (idArrayRcraft) { getSectionIDs('repeating_rlow', function (idArrayRlow) { let attrNamesRehigh = idArrayRehigh.map(id => "repeating_rehigh_" + id + "_rank_rehigh"), attrNamesRweap = idArrayRweap.map(id => "repeating_rweap_" + id + "_rank_rweap"), attrNamesRcraft = idArrayRcraft.map(id => "repeating_rcraft_" + id + "_rank_rcraft"), attrNamesRlow = idArrayRlow.map(id => "repeating_rlow_" + id + "_rank_rlow"); calcFooInsight([...attrNamesRehigh, ...attrNamesRweap, ...attrNamesRcraft, ...attrNamesRlow]); }); }); }); }); }); </script>
1495712831

Edited 1495713799
Jakob
Sheet Author
API Scripter
Nice! If you want some more food for thought, here's a recursive version that avoids the ugly nesting code. This replaces the last on(eventString, ...) block. insightSections = ['rehigh', 'rweap', 'rcraft', 'rlow']; var getMultipleSectionAttributes = function(data, callback) {   let cData = [...data], // shallow clone data so we do not modify it     attributes = [],     // recWork is the recursion function     recWork = function(name) {       getSectionIDs(`repeating_${name}`, function(idArray) {         attributes.push(...idArray.map(id => `repeating_${name}_${id}_rank_${name}`));         if (cData.length) {           recWork(cData.shift());         } else {           callback(attributes);         }       });     };   recWork(cData.shift()); // start the recursion } on(eventString, () => getMultipleSectionAttributes(insightSections, calcFooInsight)); This assumes, of course, that the attribute within the repeating section that you want follows the `repeating_${name}_${id}_rank_${name}` naming scheme. This does essentially the same thing as nesting a bunch of getSectionIDs(), but doesn't get more complicated and less readable as you add repeating sections. EDIT: And if there's any particular piece of code you don't understand, feel free to ask about it.
1495734693
Lithl
Pro
Sheet Author
API Scripter
Jakob said: Nice! If you want some more food for thought, here's a recursive version that avoids the ugly nesting code. You could also leverage Promise.resolve and Promise.all: on(eventString, () => { const proms = []; const sections = ['rehigh', 'rweap', 'rcraft', 'rlow']; sections.forEach((n) => { getSectionIds(`repeating_${n}`, (idArray) => { const val = {}; val[n] = idArray; proms.push(Promise.resolve(val)); }); }); Promise.all(proms).then((idArrays) => { // we use idArrays.find because the order of the elements is not guaranteed const attrNamesRehigh = idArrays.find((o, k) => k === 'rehigh').rehigh .map((id) => `repeating_rehigh_${id}_rank_rehigh`); const attrNamesRweap = idArrays.find((o, k) => k === 'rweap').rweap .map((id) => `repeating_rweap_${id}_rank_rweap`); const attrNamesRcraft = idArrays.find((o, k) => k === 'rcraft').rcraft .map((id) => `repeating_rcraft_${id}_rank_rcraft`); const attrNamesRlow = idArrays.find((o, k) => k === 'rlow').rlow .map((id) => `repeating_rlow_${id}_rank_rlow`); calcFooInsight([...attrNamesRehigh, ...attrNamesRweap, ...attrNamesRcraft, ...attrNamesRlow]); }); });
1495736956
Jakob
Sheet Author
API Scripter
I thought you couldn't use promises in sheet workers? Or something to that effect (I don't actually know how they work).
1495743194
Lithl
Pro
Sheet Author
API Scripter
Derp, I wasn't paying attention to the fact that calcFooInsight is using get/setAttrs. Promises work in sheet worker scripts (they run on the browser, so as long as the user is running Chrome 32+, Firefox 29+, Opera 19+, Safari 7.1+, or Edge, they'll function). However, calling get/setAttrs from within the Promise loses track of which character it's supposed to be applying to, and fails.