Randomly swap 2 elements of array with another 2 elements - javascript

I have a list of team members and a list of 2 substitutes:
team = Samson, Max, Rowan, Flynn, Jack
subs = Struan, Harry
I need to randomly swap the 2 subs into the team. I'm getting stuck because I need to ensure that only these 2 elements are swapped and that both are swapped in. I tried just looping through the subs array and randomly swapping each element with an element from the team array, but too frequently it swapped Struan with Max and then Harry with Struan, so that in the end Struan was still in the subs array and not in the team.
So: I need to exclusively swap the 2 elements in the sub array with random elements from the team array. How can I do that?

You can do this with this function. Give the function current team and substitudes and get new team with random substitution done.
const team = ['Samson', 'Max', 'Rowan', 'Flynn', 'Jack']
const subs = ['Struan', 'Harry']
const substitudePlayers = (team, subs) => {
const newTeam = [...team]
// get 2 random numbers which are less than team members count
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max-1);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 2 random numbers
let num1 = getRandomInt(0, team.length)
let num2 = getRandomInt(0, team.length)
// make sure that numbers are different
while(num2 === num1) num2++
// substitude
newTeam[num1]=subs[0]
newTeam[num2]=subs[1]
return newTeam
}
const substitudedTeam = substitudePlayers(team, subs);
console.log(substitudedTeam)

This should work:
function getRandomItem () {
let randomItem = team[Math.floor(Math.random()*team.length)];
if(subs.indexOf(randomItem)<0){
return randomItem;
} else {
return getRandomItem();
}
}
subs.forEach((item,i)=>{
subs.splice(i,1,getRandomItem());
}

Related

Picking Non-duplicate Random Numbers using JavaScript

How would I pick 5 random lottery numbers without having duplicate numbers? The code below is what I have so far and I just can't figure out where to insert the code to loop through to pick out duplicate numbers and reassign new numbers? I've tried adding if and else along with forEach function but it didn't work. This is the code I have so far. Thank you in advance.
let lotto = [];
for(let i = 0; i < 5; i++){
lotto[i] = Math.floor(Math.random() * 69) + 1;
}
const sorting = lotto.sort((a,b) => a - b);
console.log(sorting);
Two solutions:
Create a list of your numbers, then pick (and remove) 5 from them.
Create a loop that keeps generating numbers until it has 5 unique ones.
Your attempt can be adapted for solution 2:
let lotto = [];
while(lotto.length < 5) {
console.log('Got', lotto.length, 'numbers!');
// Changed 69 to 5 to "force" clashes (well, make it very likely)
const num = Math.floor(Math.random() * 5) + 1;
if (!lotto.includes(num)) lotto.push(num);
}
const sorting = lotto.sort((a, b) => a - b);
console.log(sorting);
Considering the process will run at leats one time, the best solution is to use a do while loop and verify if this number already exist in the list.
const lotto = [];
do {
const random = Math.floor(Math.random() * 69) + 1;
if(!lotto.includes(random)) lotto.push(random);
} while(lotto.length < 5);
const sorting = lotto.sort((a, b) => a - b);
console.log(sorting);

How to randomize without replacement with conditions?

I have a number of embedded images, which I randomize 4 times without replacement (once an image is seen, you cannot see it again). I'd like to add a condition, which suggests that a set of additional images cannot be seen (not only the image that was previously selected). These are images that have similar traits to the one selected.
To demonstrate:
Let's say I have the following array of vars:
BF1, BA1, BF2, BA2, BF3, BA3
I want to randomly draw 3 vars (images) out of the array without replacement, AND I want vars that have the number 2 (same set) to be removed from the next array as well. So, if the first drawn var is BF2, the next draw will be from the following array:
BF1, BA1, BF3, BA3 (only one of these options can randomly appear)
Now let's say I drew the var BF1, so the next set of possible vars will be:
BF3, BA3.
I hope this makes sense. This is the code I have so far for the drawing without replacement:
function shuffle(array){
var counter = array.length,
temp, index;
while (counter > 0){
index = Math.floor(Math.random() * counter);
counter = counter-1;
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
var myArray = [BF1,BA1,BF2, BA2, BF3,BA3, BA4, BF4, BA5, BF5, BF6, BA6, BF7, BA7, BA8, BF8, BA9, BF9, BF10, BA10, BA11, BF11, BA12, BF12, BA13, BF13, BA14, BF14, BA15, BF15, BA16, BF16, BA17, BF17, BA18, BF18, BA19, BF19, BA20, BF20, BA21, BF21, BF22, BA23, BF23, BA24, BF24, BA25, BF25, BA26, BF26, BA27, BF27, BA28, BF28, BA29, BF29, BA30, BF30, BA31, BF31, BA32, BF33, BA33, BA34, BF35, BA35, BA36, BF36];
shuffle(myArray)
You can definitely implement this in any number of ways, but no matter what you use, you'll need to perform the following 3 steps in some capacity (I split them out into separate methods, but you can combine them as you see fit):
Shuffle the list
Pick an item
Filter out the items matching the pick (in this case, those with the same number)
You have the shuffle routine covered, so that just leaves the pick and the filter.
For the pick, I just used Math.random to pull a random member of the list:
return array[Math.floor(Math.random() * array.length)];
For the filter, I used Array.prototype.filter to pull out the desired items. In this case, with the strings, I parse the number out of the string and then remove any items in the array that have the same number as the last pick:
return array.filter(el => +el.match(/\d+/).join() != +picked.match(/\d+/).join());
But with actual images, you'll just replace that with however you read the labels of your images.
Example
Here's the full working example, with the list of picks first, followed by a sorted array of the picks showing they were all used.
var imageList = ['BF1', 'BA1', 'BF2', 'BA2', 'BF3', 'BA3', 'BA4', 'BF4', 'BA5', 'BF5', 'BF6', 'BA6', 'BF7', 'BA7', 'BA8', 'BF8', 'BA9', 'BF9', 'BF10', 'BA10', 'BA11', 'BF11', 'BA12', 'BF12', 'BA13', 'BF13', 'BA14', 'BF14', 'BA15', 'BF15', 'BA16', 'BF16', 'BA17', 'BF17', 'BA18', 'BF18', 'BA19', 'BF19', 'BA20', 'BF20', 'BA21', 'BF21', 'BF22', 'BA23', 'BF23', 'BA24', 'BF24', 'BA25', 'BF25', 'BA26', 'BF26', 'BA27', 'BF27', 'BA28', 'BF28', 'BA29', 'BF29', 'BA30', 'BF30', 'BA31', 'BF31', 'BA32', 'BF33', 'BA33', 'BA34', 'BF35', 'BA35', 'BA36', 'BF36'];
var selection = imageList.slice();
var picked = [];
function shuffle(array) {
var counter = array.length, temp, index;
while (counter > 0) {
index = Math.floor(Math.random() * counter);
counter = counter - 1;
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
function pick(array) {
return array[Math.floor(Math.random() * array.length)];
}
function filterPicked(picked, array) {
return array.filter(el => +el.match(/\d+/).join() != +picked.match(/\d+/).join());
}
while (selection.length) {
// 1. Shuffle
shuffle(selection);
// 2. Pick
picked.push(pick(selection));
// 3. Filter
selection = filterPicked(picked[picked.length-1], selection);
}
console.log(`Picks: [${picked.join(', ')}]`);
console.log(`Sorted picks: [${picked.sort((a, b) => +a.match(/\d+/).join() - +b.match(/\d+/).join()).join(', ')}]`);
Step-by-step
Shuffle the selection array (a copy of the full array or all selections)
Pick an item off the selection array, push it onto the array of picks
Filter the selection array to remove items matching the last pick
Repeat 1-3 with each newly filtered array, until no selections remain
You can shuffle array with loop and random numbers, then in another loop extract first image in resulting array, filter array with numbers at the end of string
var myArray="BF1, BA1, BF2, BA2, BF3, BA3, BA4, BF4, BA5, BF5, BF6, BA6, BF7, BA7, BA8, BF8, BA9, BF9, BF10, BA10, BA11, BF11, BA12, BF12, BA13, BF13, BA14, BF14, BA15, BF15, BA16, BF16, BA17, BF17, BA18, BF18, BA19, BF19, BA20, BF20, BA21, BF21, BF22, BA23, BF23, BA24, BF24, BA25, BF25, BA26, BF26, BA27, BF27, BA28, BF28, BA29, BF29, BA30, BF30, BA31, BF31, BA32, BF33, BA33, BA34, BF35, BA35, BA36, BF36";
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
const arr = shuffle(myArray.split(','));
function draw(a, times) {
let res =[]
for (let i = 1; i <= times; i++) {
let str = a[0]
res.push(str)
a = a.filter(a => parseInt(a.match(/\d+$/)[0], 10) !== parseInt(str.match(/\d+$/)[0], 10))
}
return res
}
console.log(draw(arr, 4))

Get pseudo-random item with given probability

I want to give the user a prize when he signs in;
but it needs to be there some rare prizes so I want to appear prizes with different chances to appear using percents
i want to display one of these
[50 : 'flower'], [30 : 'book'], [20 : 'mobile'];
using percents they have
if there any way using Node.js or just javascript functions it would be great
You can create a function to get weighted random results, something like this:
const weightedSample = (items) => {
// cache if necessary; in Chrome, seems to make little difference
const total = Object.values(items).reduce((sum, weight) => sum + weight, 0)
const rnd = Math.random() * total
let accumulator = 0
for (const [item, weight] of Object.entries(items)) {
accumulator += weight
if (rnd < accumulator) {
return item
}
}
}
// check frequencies of each result
const prizes = { flower: 50, book: 30, mobile: 20 }
const results = Object.fromEntries(Object.keys(prizes).map(k => [k, 0]))
for (let i = 0; i < 1e6; ++i) {
const prize = weightedSample(prizes)
++results[prize]
}
// sample results: { flower: 500287, book: 299478, mobile: 200235 }
console.log(results)
This will work regardless of whether the weights add up to 100, whether they're integers, and so on.
'Right off the top of my head'-approach would be to prepare an array where each source item occurs the number of times that corresponds to respective probability and pick random item out of that array (assuming probability value has no more than 2 decimal places):
// main function
const getPseudoRandom = items => {
const {min, random} = Math,
commonMultiplier = 100,
itemBox = []
for(item in items){
for(let i = 0; i < items[item]*commonMultiplier; i++){
const randomPosition = 0|random()*itemBox.length
itemBox.splice(randomPosition, 0, item)
}
}
return itemBox[0|random()*itemBox.length]
}
// test of random outcomes distribution
const outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom({'flower': 0.5, 'book': 0.3, 'mobile': 0.2})),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
.as-console-wrapper{min-height:100%;}
While above approach may seem easy to comprehend and deploy, you may consider another one - build up the sort of probability ranges of respective width and have your random value falling into one of those - the wider the range, the greater probability:
const items = {'flower': 0.5, 'book': 0.2, 'mobile': 0.2, '1mUSD': 0.1},
// main function
getPseudoRandom = items => {
let totalWeight = 0,
ranges = [],
rnd = Math.random()
for(const itemName in items){
ranges.push({
itemName,
max: totalWeight += items[itemName]
})
}
return ranges
.find(({max}) => max > rnd*totalWeight)
.itemName
},
// test of random outcomes distribution
outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom(items)),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
"Certain probability" and "random" could lead to different approaches!
If you want random each time, something like:
let chances = [[0.2,'mobile'],[0.5,'book'],[1.0,'flower']]
let val = Math.random() // floating number from 0 to 1.0
let result = chances.find( c => c[0] <= val )[1]
This will give a random result each time. It could be possible to get 'mobile' 100 times in a row! Rare, of course, but a good random number generate will let that happen.
But perhaps you want to ensure that, in 100 results, you only hand out 20 mobiles, 30 books, and 50 flowers. Then you might want a "random array" for each user. Pre-fill the all the slots and remove them as they are used. Something like:
// when setting up a new user
let userArray = []
let chances = [[20,'mobile'],[30,'book'],[50,'flower']]
changes.forEach( c => {
for(let i = 0; i < c[0]; i++) userArray.push(c[1])
})
// save userArray, which has exactly 100 values
// then, when picking a random value for a user, find an index in the current length
let index = Math.floor(Math.random() * userArray.length)
let result = userArray[index]
userArray.splice(index,1) // modify and save userArray for next login
if(userArray.length === 0) reinitializeUserArray()
There are different approaches to this, but just some ideas to get you started.

Creating an array that is consisted of unique numbers

I'm developing a simple game that allows user to generate from 1 to 5 Cat images from certain Cat Api. Then, after clicking start button the app generates shadow copies of those cats(with low opacity). Game will be later about dragging bottom images and fiting them to their shadow copies, that are randomly positioned(only then game makes sense). Then I'm planning make some futher features like time counter, points etc. etc. just for learning purposes.
But what am struggling with is creating a unique random number(that'll be index of particular cat) an will not be repeated during iteration...
Here is the code
const newArray = []; //
const catsArrayList = [...catBoardCopy.querySelectorAll('.cat')] //Array with cat images
function randomizeIndex() { // randomize an index number
let randomIndex = Math.floor((Math.random() * catsArrayList.length - 1) + 1);
console.log(randomIndex);
return randomIndex;
}
catsArrayList.forEach(catElement => { // here I am iterating over an array with cats which length is for example 5(this is max actually)
newArray.push(catsArrayList[randomizeIndex()]); // and pushing those elements with randomly generated index to the new array
})
newArray.forEach(newCat => {
shadowCatsContainer.appendChild(newCat); // here random cats are finally put to html container
})
And all of this work until the point when one of those random numbers is at least one time repeated... of course this happens actually 90% of time.
Im supposing it won't be simple solution to that. I tried so hard to make it work with different techniques, different loops, different array methods and nothing :( Also please take note that Im beginner so I need exhaustive guidance of what is going on :)
Have a nice day.
Your code is close; you can just remove the items that you're assigning to the new array from the source array so you don't use it twice.
const cats = [...catBoardCopy.querySelectorAll('.cat')]
function randomIndex() {
return Math.floor(Math.random() * cats.length);
}
cats.forEach(catElement => {
const index = randomIndex();
shadowCatsContainer.appendChild(cats[index]);
cats.splice(index, 1);
})
One option is to simply shuffle an array:
const cats = ['Tigger', 'Smokey', 'Kitty', 'Simba', 'Sassy'];
function shuffle(array, n = 500) {
const output = array.slice();
for (let i = 0; i < n; ++i) {
swap(output, getRandomInt(0, output.length), getRandomInt(0, output.length))
}
return output;
}
function swap(array, i, j) {
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
const shadowedCats = shuffle(cats);
console.log(cats);
console.log(shadowedCats);
console.log(shuffle(cats));
An example using array. Here I created an array with the possible numbers, it goes from 0 to the number of elements contained in the 'catsArrayList' array. If for example 'catsArrayList' has 3 elements, then the array with the possible numbers will be equal to: [0, 1, 2]
The idea now is to draw a random number from that array and then remove it from the list, and then we can go on repeating the process without getting repeated values.
e.g.
let catsArrayList = ['value1', 'value2', 'value3', 'value4', 'value5', 'value6'] // example
let numbers = [...Array(catsArrayList.length).keys()]
let lengthnumbers = numbers.length
for(let i = 1; i <= lengthnumbers; i++) {
let randoms = Math.floor(Math.random() * numbers.length)
console.log(i + 'º number: ' + numbers.splice(randoms, 1))
}
Click on 'Run code snippet' a few times and you will see that you will get different, non-repetitive random numbers

Compare arrays in loop - javascript

I'm doing a lottery system and I need to make sure that each Array is ​​different. This is my code:
var intNumberOfBets = 10;
let aLotteryTicket=[];
let aData = [];
for(intI = 0; intI <intNumberOfBets; intI++){
let oCasilla ={};
oCasilla.block=[];
for(intI = 0; intI <intNumberOfBets; intI++){
let oCasilla ={};
oCasilla.block=[];
Each "lottery ticket" has an array with 5 numbers. They can have the same numbers as others but in different positions.
for (let intB=1;intB<=5;intB++)
{
for(let intA=1;intA<=50; intA++){ aLotteryTicket.push(intA); }
oCasilla.block.push(aLotteryTicket.splice(parseInt(Math.random()*aLotteryTicket.length),1)); // ADD 5 NUMBERS RANDOMLY TO ARRAY
};
oCasilla.block.sort(function (a,b){ return (parseInt(a)-parseInt(b));});
aData.push(oCasilla);
alert(aData[intI].block); // show generated arrays
}//END FOR
How can I prevent each array from being the same as another, before adding it to my final Array aData[]?
Example:If i add the array 5,6,7,8,9 to oCasilla.block=[]; , i need to check that there is not another 5,6,7,8,9 in oCasilla.block=[];
Thanks in advance
You can use a set of string representations (numbers separated by comma built using join(',')) of your tickets to keep track of what was added, and only add if a ticket was not previously created.
function generateTicket() {
// generate an array with 5 unique random numbers
let a = new Set();
while (a.size !== 5) {
a.add(1 + Math.floor(Math.random() * 50));
}
return Array.from(a);
}
let oCasilla = {
block: []
};
let addedTickets = new Set(); // add stingified ticket arrays here
// add 10 unique tickets to oCasilla.block
while (oCasilla.block.length !== 10) {
const ticket = generateTicket();
if (!addedTickets.has(ticket.join(','))) {
oCasilla.block.push(ticket);
addedTickets.add(ticket.join(','));
}
}
console.log(oCasilla);

Categories

Resources