- Create Unique Repeating Section ID
A recent thread on the Roll20 forums raised an interesting question, while asking a different question. When creating new rows in a repeating section, you need to create a new row id and assign it to the new row. The problem is that when creating several rows fairly rapidly, the new row id might be a repeat of an earlier row id.
This means you don’t actually create a new row, you overwrite an earlier one, losing the data it contained.
This is a bad thing, but the roll20 wiki has a solution for it. Unfortunately it’s not described as well it could be, which means other solutions have been created (the solution there might not have existed when other solutions were created). Evil Hat created probably the best known solution, here. That function works!
I’m going to show another solution in this post, inspired by the one on the roll20 wiki, and explain how to use it (that last step is often overlooked in official documentation).
Create Unique ID
Roll20 has a function for the creation of row ids: generateRowID()
. This always creates a valid id, so we don’t need to check for that. It’s one flaw is it sometimes creates the same id, if multiple ids are created close together. So our goal is create a function that can be used in its place, that does not create repeated IDs. Here’s the function:
const createUniqueID = row_ids => {
row_ids = Array.ids ? row_ids : [];
let generated;
while (!generated || row_ids.includes(generated)) {
generated = generateRowID();
}
row_ids.push(generated);
return generated;
}
Code language: JavaScript (javascript)
We’ll look at how the function works below. For now, just copy it into your script block, near the top.
Using This Function
Use the function like this:
getSectionIDs('repeating_section', id_array => {
// some code
getAttrs(['some attributes'], values => {
// some more code
row_id = createUniqueID(id_array);
// some more code
setAttrs({
/* whatever new attribute you have created */
});
});
});
Code language: JavaScript (javascript)
Basically insert createUniqueID(id_array)
wherever you’d normally use generateRowID()
, with the assumption that getSectionIDs
has already been used. The array used for holding the repeating section ids is passed to createUniqueID
.
How The Function Works
You can use the function without understanding it (in fact, you can do a lot in roll20 without understanding how it works- that’s part of the beauty of the system), but if you want to understand how it works, we’ll go over function line by line.
const createUniqueID = row_ids => {
Code language: JavaScript (javascript)
This line initialises the function, telling us it is called createUniqueID
(make sure to remember the case), and it is expected to recieve a parameter, row_ids
. The function doesn’t tell us this, but it should be an array of ids.
row_ids = Array.ids ? row_ids : [];
Code language: JavaScript (javascript)
This line makes sure the passed parameter is an array, and replaces it with an empty array if an array is not detected. To be clear, this could be replaced with a better method of checking the ids – we know that row_ids should be an array from a repeating section, but we let the sheet author handled that. This line just handles the most basic of error correction.
let generated;
Code language: JavaScript (javascript)
Here we create an empty variable. Later this will hold the final row id, but it doesn’t yet.
while (!generated || row_ids.includes(generated)) {
Code language: JavaScript (javascript)
The while
keyword is used to create a loop. Everything inside the loop runs as long as one of the two tests here is true.
The first test checks if generated is empty. On the first run, this is empty, so the loop should always run once.
The second test checks if the existing row id (generated) exists in the array of row_ids. It should not. If it does, that means we have a duplicate row id – and this loop will run again.
generated = generateRowID();
Code language: JavaScript (javascript)
Here we have the entire contents of the while
loop – a single line which uses the existing generateRowID
function to assign a row id to the generated
variable.
When the while
loop ends, a new iteration will begin – the tests above are repeated. Now generated
is not empty, and as long as it doesn’t already exist in the repeating section row ids, the while
loop is skipped and the code proceeds.
row_ids.push(generated);
Code language: JavaScript (javascript)
If the user creates a sheet worker with one call to getSectionIDs
, but then creates multiple new rows in the same worker, a problem can occur. That check at the start of the while
loop compares the newly created row ids to those already in the repeating section. If you are creating two or thre or more rows, that list isn’t complete. This .push
command updates the original array, so new row ids are added to it.
return generated;
Code language: JavaScript (javascript)
Finally the function ends. The generated
value is returned as the result of the function. Here’s the complete function again.
const createUniqueID = row_ids => {
row_ids = Array.ids ? row_ids : [];
let generated;
while (!generated || row_ids.includes(generated)) {
generated = generateRowID();
}
row_ids.push(generated);
return generated;
}
Code language: JavaScript (javascript)
Final Thoughts
In a repeating section, each ID doesn’t have to be genuinely unique – it just should not be repeated in that section. This can only happen when creating multiple rows in a short space of time – most sheets will never do this. But when it can happen, a function like this is handy – it makes sure you don’t get any repeats.
It only works properly if you pass the row ids of an existing section – the repeating section you are creating a row for. So to use it, you must include a getSectionIDs
command earlier in the same worker, and then pass that sections row ids to the function.
Keep this in mind and you’ll never see a repeated row id.