Here's a sheet worker to test. It's very complex, and workers as complex as this might need some revisions to get working properly. // a function to construct a string on the on(change) line const buildChanges = (list, prefix = '', section = '') => list.map(item => `change:${(section ? `repeating_${section.toLowerCase()}:`: '')}${prefix.toLowerCase()}${item.toLowerCase()}`).join(' '); const clog = (message) => console.log(`%c ${message}`, `color:green; font-weight:bold`); const stats = ['Farsense', 'Creativity', 'Redaction', 'PK', 'Coercion', 'Dream']; on(`${buildChanges(stats)} ${buildChanges(stats, 'OperantSkillUses', 'operantskills')} change:repeating_operantskills:operantskilltitle change:repeating_operantskills:operantskillname`, (event) => { // event tells us which attribute changed, and that tells us what we need to do in the worker. getSectionIDs("repeating_operantskills", idarray => { // loop through the repeating section to get the row ides and full names of the relevant repeating attributes const fieldnames = []; idarray.forEach(id => { fieldnames.push(`repeating_operantskills_${id}_OperantSkillTitle`); fieldnames.push(`repeating_operantskills_${id}_OperantSkillRating`); stats.forEach(stat => fieldnames.push( `repeating_operantskills_${id}_OperantSkillUses${stat}`, `repeating_operantskills_${id}_Skill${stat}` )); }); getAttrs([...stats, ...fieldnames], v => { const output = {}; // get the values of the six core stats. const statValues = {}; stats.forEach(stat => { statValues[stat] = parseInt(v[stat]) || 0; }); // check the stat values in the section are correct idarray.forEach(id => { stats.forEach(stat => { const statvalue = parseInt(v[`repeating_operantskills_${id}_Skill${stat}`]) || 0; if(statvalue !== statValues[stat]) output[`repeating_operantskills_${id}_Skill${stat}`] = statValues[stat]; }); }); // now calculate the OperantSkillRating for each row idarray.forEach(id => { const oldvalue = parseInt(v[`repeating_operantskills_${id}_OperantSkillRating`]) || 0; const title = parseInt(v[`repeating_operantskills_${id}_OperantSkillTitle`]) || 0; let maxValue = 0; // loop through stats, see if they are checked, and if so add their value to statValues stats.forEach(stat => { if(parseInt(v[`repeating_operantskills_${id}_OperantSkillUses${stat}`]) && statValues[stat] > maxValue) maxValue = statValues[stat]; }); // now add the highest stat to the title/level const newvalue = maxValue + title; if (newvalue != oldvalue) output[`repeating_operantskills_${id}_OperantSkillRating`] = newvalue; }); setAttrs(output); }); }); });