I have three employees: John, Ashley and Mark. During the day we keep receiving jobs to do from our clients. I want that 20% of the jobs go to John, 50% for Ashley and 30% to Mark and for that I'm trying to build a Google Sheets that randomly select according to those weights (20%, 50% and 30%). You can see it in this google sheets file.
The way I found seemed good at first, the problem is that it uses RANDBETWEEN function, and it keeps randomizing everytime anything happens in the sheet, so it might look like this:
And if I simply add one row it changes completely:
Is there a way to fix the randomization (maybe copy-pasting values through script?) or an alternative way to do the same thing?
You can accomplish this via a custom function created in Google Apps Script, and use Math.random() to achieve the randomness. Unlike RANDBETWEEN, this won't refresh every time the sheet is edited, or even when the spreadsheet itself is refreshed. It also wouldn't require the data in A6:B16 (the people and percentages from A2:B4 would be enough).
To accomplish this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function GET_RANDOM_PERSON(values) { // "A2:B4"
const random = Math.random(); // Random number between 0 and 1
const sheet = SpreadsheetApp.getActiveSheet();
// Change range if needed. Range could be a function argument if preferred:
if (!values) values = sheet.getRange("A2:B4").getValues();
let acc = 0;
values = values.map(value => {
acc = acc + value[1];
return [value[0], acc]; // Return accumulated probabilities
});
// Return person getting the job:
return values.find(value => value[1] > random)[0];
}
Now, if you go back to your spreadsheet, you can use this function as if you were using a regular sheets formula, as you can see here:
Reference:
Custom Functions in Google Sheets
With your current formula and setup, and without scripts, you can do it with iterative calculation:
Turn on Iterative Calculation: File > Spreadsheet Settings > Calculation
Make a tickbox. (I will use cell F2)
Change your formulas to look like this:
=IF($F$2;
vlookup(RANDBETWEEN(1;10);$A$6:$B$16;2;0);
E2)
If checked, the cells will calculate new randoms on change. If unchecked, it will prevent any changes until checked again. (Checked = unlocked).
When you first enter the formula, it will initialize to 0. Check the box to generate the first list.
Related
Each time we deploy our Google AppScript Project, our custom function starts returning #ERROR!. This will happen regardless of whether a code change was made.
See photo below:
NOTE: Internal error executing the custom function. is not one of our error strings.
This is very strange because the function does not seem to be executing. I say this because #ERROR! is returned immediately, with 0 processing time. See failures in photo below:
This issue resolves itself after some seemingly arbitrary amount of time. Meaning the custom function will run normally, after some seemingly arbitrary amount of time.
This has become a very large problem because we have uncontrollable downtime after each deployment, and it does not seem to be an issue with our code considering this happens every time we deploy the code, regardless of whether the code actually changed.
This Google document states A custom function call must return within 30 seconds. If it does not, the cell will display an error: Internal error executing the custom function.. Our custom function does not take 30s to run. We actually can't even find an instance where our function runs longer than 5s.
NOTE: the only thing that fails is our custom function, our task pane that interacts with the Google Sheet remains functional.
According to the Optimization section of Custom Functions:
Each time a custom function is used in a spreadsheet, Google Sheets
makes a separate call to the Apps Script server.
Having multiple custom functions means you are making multiple calls to the server simultaneously. This could lead to a slow process or sometimes Error.
Solution:
The workaround to this is to lessen the use of custom functions. Custom function can accept range as parameter and it will be translated as two-dimensional array in Apps Script. Use the array to calculate the values and return a two-dimensional array that can overflow into the appropriate cells in your spreadsheet.
Example:
Code:
function multiplicationTable(row, col){
var table = [];
var rowArr = row.flat();
var colArr = col.flat();
for (var i = 0; i < rowArr.length ; i++) {
table.push([]);
for (var j = 0; j < col.length; j++) {
table[i].push(rowArr[i] * colArr[j]);
}
}
return table;
}
In Google Sheets i have in a cell an array like [27, https://www.example.com/page1.html, false, false, 1]
How can i access its single parts with a formula?
I know a way through =SPLIT(), like =split(split(A2,"["),",") - but i would very like, if its possible, to access each part directly (each array has always the same amount of parts in my data set).
Maybe something like =QUERY(query(A2,",",1)) - cell, divider, item number...? - Result is 27.
=INDEX(SPLIT(A2,"[,]"),1)
SPLIT by each of these characters [,]
INDEX into the resulting array
I would like to take the chance and propose a solution using Google Apps Script. Basically, you can create your own custom function to accomplish this task.
Please follow these steps:
Go to Tools => Script editor from your spreadsheet file:
Clear the default Code.gs file, copy and paste this function and save the changes:
function indexArray(arr,pos) {
return array=arr.slice(1,-1).split(",")[pos-1]
}
You are now able to access the indexArray() function from within your spreadsheet file. It accepts two arguments, the desired cell that contains the array and the position of the element you would like to access, starting from 1:
=indexArray(A2,2)
For example, this will give you the second element of your array which is: https://www.example.com/page1.html.
Check these instructions out if you need more information how custom functions work. They are pretty straightforward.
So I'd like 1/3 (which equals 0.33333 recurring) to return true and 1/8 (which equals 0.125 non-recurring) to be false.
Something like =ISRECURRING(A1) which returns a boolean.
The reason for this is that I want to highlight cells that have had rounding applied to them.
You can build a JavaScript function to check that and use it in your sheet as an Apps Script custom function. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project (credits to Larry Battle, whose answer here this function is based on):
function ISRECURRING(num) {
num = (num || "").toString().replace(/\d$/, '');
var RE_PatternInRepeatDec = /(?:[^\.]+\.\d*)(\d{2,})+(?:\1)$/;
return RE_PatternInRepeatDec.exec(num) ? true : false;
};
Now, if you go back to your spreadsheet, you can use this function as if you were using a regular sheets formula. You just have to provide the appropriate range as an argument, as you can see here:
Note:
As #Ron Rosenfeld noted in a comment below, this function will not work for very long repetends (e.g. 1/97). This is due to the precision limit that spreadsheets have (15 digits, as far as I know). Because of this, repetends longer than 7 digits won't be detected.
Reference:
Custom Functions in Google Sheets
Credit to #Ron Rosenfeld.
=A1-ROUND(A1,14) <> 0 works a treat. Where A1 is the number in question.
I can't guarantee that every rounded number works but it appears to work on all the examples I've tried.
So, I'm setting up student surveys in a college. There are lots of forms to be handed out to lots of groups of students. For one particular survey, I have a template google form.
What I do is a loop that on every iteration creates a copy of this template and then modifies it a little bit. It takes A LOT of time. Google is copying and modifying 220 forms for like 40-50 minutes. I found my way around the time limit put on google script, but it's still too long. Do you see any way to speed this up a little?
Now it looks schematically like this:
for(some range):{
template.makeCopy("template", formsFolder);
var formFile = formsFolder.getFilesByName("template").next();
var form = FormApp.openById(formFile.getId())
// ... do some modifications
}
Thank you!
File#makeCopy already hands you the exact file you want, so you can completely cut out the need to search for the file you create:
for (var f = 0; f < newNames.length; ++f) {
var formFile = template.makeCopy(newNames[f], formsFolder);
var form = FormApp.openById(formFile.getId());
// Do stuff
}
I am surprised that your script actually finishes and does not timeout as the maximum script run time is 30 minutes or so dependent on your edition. I have had a very similar obstacle where I needed to run a script over 200-300 different sheets and copy data. The number of calls to the API was about 3-5 per loop and then some javascript to filter that dataset before writing to a sheet. It would regularly fail.
I found that my best approach was to leverage Triggers and rather than have 300 sheets processed at once have 100 sheets processed every 5 minutes or so. In my circumstances there really was no reason why these sheets had to be processed at the exact same time as long as my parameters for filtering was accurate. I would control all the parameters through inputs on a sheet.
I am creating a google form where I have three sets of values and I want to randomly select one value from all the three sets and print them as a question.
The extension of the script is ".gs"
I tried using RANDBETWEEN(low, high) but the script throws an error. Seems like this is for google sheets.
Requesting for some help on how to create one.
For you random number, you'll need to use the Math library:
var nums = Math.floor(Math.random() * 4) + 1;
That should give you a random number between 1 and 5.
Seems like a little bit of confusion here:
RANDBETWEEN(low, high) is a special Google Spreadsheet function.
Inside a Google Script, you must use plain JavaScript (plus a few custom Google functions, like the FormApp.create() function you're using.)
In JavaScript, Math.random() is a way of getting a (pseudo) random number, but it returns a float between 0 and 1. To convert that into an integer in a range we have to use a bit of math. It might be helpful to define your own getRandomInt function, like this:
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
Then later on you could call getRandomInt(5), returning 0, 1, 2, 3, or 4.