Get random value based on probabilities

HI everyone,
my problem is that
User table has field points ( type : number )
Option table contain who take part in ( list of users )
Vote table list of options and date that make voting expired
i want to choose a random user when vote expired ( consider that each user has a probability to be chosen equal to number of point / all users point sum )
Example to simplify it : let A , B, C are users
A → point = 10
A → point = 20
A → point = 30
Sum = 10 + 20 + 30 = 60
now probability of A (probability of A to be choosed ) = 10/60
probability of B (probability of B to be choosed ) = 20/60
probability of C (probability of C to be choosed ) = 30/60
How to achieve this :slightly_smiling_face: ?

You’re going to need to create a JavaScript, run it and then retrieve the values using the Toolbox plugin.

To explain more choosing random one will done in Backend WF because i want choose one after vote time expired so no JS available in Backend WF .

Using JS code will be like following
there is problem here to pass object from bubble like this { text: "Hello", number: 1 }
Code :
function getSumOfNumbers(objects) {

  let sumOfNumbers = 0;

  for (const object of objects) {

    sumOfNumbers += object.number;

  }

  return sumOfNumbers;

}

function getListOfRepeatedTexts(objects) {

  const listOfRepeatedTexts = [];

  for (const object of objects) {

    const text = object.text;

    const number = object.number;

    for (let i = 0; i < number; i++) {

      listOfRepeatedTexts.push(text);

    }

  }

  return listOfRepeatedTexts;

}

function getRandomText(listOfTexts) {

  const randomIndex = Math.floor(Math.random() * listOfTexts.length);

  return listOfTexts[randomIndex];

}

const objects = [

  { text: "Hello", number: 1 },

  { text: "World", number: 2 },

  { text: "JavaScript", number: 3 },

];

const sumOfNumbers = getSumOfNumbers(objects);

const listOfRepeatedTexts = getListOfRepeatedTexts(objects);

// get user_id

const randomText = getRandomText(listOfRepeatedTexts);

As i mentioned before JS work in client-side not server-side :upside_down_face:

no one have a good idea about this problem.

This incorrect assumption is your blocking point :cactus:

@ihsanzainal84 gave you a pretty good starting point, report back when you have made more progress and if you need more tips

@busevv6 looking at your JS code, consider partial sums to build unequal partitions for the random number to pick from.

1 Like

I am thankful for your assistance. @mishav
I didn’t find good solution to save WF and JS code not available to schedule it in backend and User table has field rank and uniqueId how to convert it to list of { text: "Hello", number: 1 }
but, What is your option to achieve this in good way to save WF ?

Yes this is tricky without JS. One way is a recursive Schedule API Workflow which assigns an incrementing number on each loop. It is very slow for large counts of users. And be careful to check condition before it calls itself, runaway recursion is very expensive in WU even in dev environment.

For backend JS there’s Toolbox and possibly other plugins. Hopefully the docs make it less difficult to use.

2 Likes

I tried to avoid recursive WF but It seems inevitable.
I am grateful for your cooperation @mishav .

There are other options, here’s a list that I can think of

  1. Entire solution in Backend javascript (Node.js) using for example Toolbox Serverscript, easiest option, in my biased opinion :crazy_face:

  2. Workflow solution, using one of these to generate sequential numbers to make partial sums work:
    2A. Scheduled API Workflow (recursive to make a loop)
    2B. Scheduled Custom Event (recursive to make a loop)
    2C. API Connector call to an external service of your choosing
    2D. Other backend plugin to generate numbers (I’m not so familiar with the other plugins)
    2E. Use Toolbox to generate numbers on backend.
    2F. In the frontend, pre-assign a sequence to the candidates, and store it in DB or pass it as an API workflow parameter.

So plenty of options, even though each one takes effort to get the complexity right.

Edit - regrouped the options

2 Likes

I’m trying to avoid recursive as possible as i can, but now first solution you mentioned very effective and had two handicaps
1- User table has field rank and uniqueId how to convert it to list of { text: "Hello", number: 1 }
2- extract output only text from object modify its User

Thank you for your answer @mishav . Your answer was very informative. I learned a lot.

1 Like

Server script cannot directly modify the database item, so return a value to the backend workflow and do it there.

“Vanilla” data lists are easier to work with than lists of Bubble data types. So, how about one of the list parameters, setting Search for Users’s points (list of numbers). Make sure the Search has a sort order for the users.

The JS can do

// turn on Asynchronous Function
// retrieve the list of points
var len = properties.thinglist1.length();
var arraypoints = await properties.thinglist1.get(0, len);
// [10,30,20]

Then JS can sum the points, build an array of partial sums, generate a random number, and return the position number of the winning points.

Back in Bubble, do again Search for Users # item number (winning user position).

I’m interested to see how it goes :eye: :eye:

Edit - I find it quicker to test server scripts in a page workflow, then move it to a backend workflow once it’s ready.

1 Like

Its very smart scenario i will tell you when i get result .
I appreciate your response @mishav ;

I thinking what if you did a list of texts, and A you listed their unique ID 10 times, B listed it 20 times, C listed it 30 times, then picked a random unique id from that entire list. But not sure how you could duplicate it X amount of times.

2 Likes

Yes that would work too. It’s equivalent to the partial sums partitioning of a random number: both methods give weights to the sample space to pick from.

Edit - didnt show sums earlier

2 Likes

Suppose points like 2k and 4k … and there about 10k Users each user need a huge WF to achieve it and list will be enormous thanks @tylerboodman this will be perfect in small scale

1 Like