randomize objects in array [duplicate] - javascript

This question already has answers here:
How to randomize (shuffle) a JavaScript array?
(69 answers)
Closed 5 years ago.
I have an array that is rendering 25 images/pins every time a button is clicked, and it is only rendering the first 25 in the array. I want it to render 25 pins at a time but would like them to be random every time the button is clicked.
It was suggested I use part of an answer previously set in here, but I cannot figure out how to apply it or make sense of what I have.
This is what I was told to use:
randomIndex = Math.floor(Math.random() * currentIndex);
And replace current index with the array I want plus .length.
var pinIdArray = ["448460075380400214", "554857616577037440", "129619295506364205"];
var boardIdArray = [];
var showPins = [];
function(){
res.render("suggested", {pins: showPins});

You could use the Durstenfeld shuffle to shuffle the array and then take the first 25 elements in the array.
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var array = [];
for(var i = 0; i < 500; i ++) {
array[i] = i;
}
console.log(shuffleArray(array).slice(0, 25));

Here is a function implemented in Javascript without any libraries. This also avoids shuffling the array so should be faster.
let pinIdArray = ["448460075380400214", "554857616577037440", "129619295506364205", "3403722138", "8005795986", "7717201977", "6689430878", "7705363504", "3827905985", "9133621064", "9162201846", "2655432017", "0197893312", "7220269979", "3218703261", "3478813716", "7445481990", "9806757977", "9357022147", "3492330721", "3504169963", "9259212333", "6574699545", "9727263383", "0016479256", "1624997250", "2083975447", "5683391989", "3466001901", "4660933902", "5216511997", "8820216343", "8583764035", "4563326839", "5201961267", "3429608185", "5007846054", "7437408815", "3472117054", "1545827364", "3152159572", "7913372317", "2550237417"];
function getRandomSubset(array, setSize) {
let maxValue = array.length, tmpSet = new Set(), randomIndices;
if (maxValue <= setSize) {
randomIndices = [...Array(setSize).keys()];
} else {
while (tmpSet.size < setSize) {
tmpSet.add(Math.floor(Math.random() * maxValue));
}
randomIndices = Array.from(tmpSet)
}
return randomIndices.map(index => array[index]);
}
//function(){
// res.render("suggested", {pins: getRandomSubset(pinIdArray, 25)});
// Test
console.log("25 Random Elements:", getRandomSubset(pinIdArray, 25));

You can randomize/shuffle the values in an array by using Lodash's _.shuffle function. The Lodash _.shuffle function uses a version of the Fisher-Yates shuffle.
var array = [1, 2, 3, 4];
var result = _.shuffle(array)
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Related

How do I repeat an iteration if a condition is true in JavaScript?

I am trying to display up to three recipes from an API that specifically include bacon as an ingredient. The API only has 10 recipes that meet this criteria, so I am running into an issue where the same recipes are sometimes being repeated on the page if the user wishes to view two or three recipes. How can I set a condition to check if the random number that I am generating and storing in an array is a duplicate value? If a duplicate, then I want for the iterator to be subtracted by 1 and the for loop to continue. I've listed the code I've I appreciate any feedback that is provided!
// The number of recipes the user would like to display//
var recipeNumber = $("#recipe-input").val();
var parsedInput = parseInt(recipeNumber);
// creating an empty array that will story the random numbers that are generated//
var ranNumArr = [];
console.log(ranNumArr);
for (i = 0; i < parsedInput; i++) {
// generate a random number based on the length of the recipe API's array of bacon recipes (10) and push it into the ranNumArr//
var randomNumber = Math.floor(Math.random() * 10);
ranNumArr.push(randomNumber);
// If the value of the index in the array is equal to a previous index's value, repeat the iteration//
if (ranNumArr[i] === ranNumArr[i -1] || ranNumArr[i] === ranNumArr[i -2]){
console.log("this is a duplicate number")
i = i - 1
}
// else, display the recipe on the card//
else {
randomRecipe = ranNumArr[i]
// Create cards that will house the recipes//
var recipeCell = $("<div>").attr("class", "cell");
$("#recipes-here").append(recipeCell);
var recipeCard = $("<div>").attr("class", "card");
recipeCell.append(recipeCard);
var recipeSection = $("<div>").attr("class", "card-section");
recipeCard.append(recipeSection);
var cardTitleE1 = $("<h1>");
cardTitleE1.attr("id", "recipe-title");
var cardImageE1 = $("<img>");
cardImageE1.attr("id", "recipe-image");
var cardTextE1 = $("<a>");
cardTextE1.attr("id", "recipe-link");
// Adding the recipe title, url, and image from the API call//
cardTitleE1.text(response.hits[randomRecipe].recipe.label);
cardTextE1.text("Click here for link to recipe");
cardTextE1.attr("href", response.hits[randomRecipe].recipe.url);
cardTextE1.attr("target", "_blank");
cardImageE1.attr("src", response.hits[randomRecipe].recipe.image);
// Display the recipe on the DOM//
recipeSection.append(cardTitleE1);
recipeSection.append(cardImageE1);
recipeSection.append(cardTextE1);
}
}
You can use a Set to store numbers that have already been chosen.
const set = new Set;
//....
if (set.has(randomNumber)){
console.log("this is a duplicate number");
i--;
} else {
set.add(randomNumber);
//...
Alternatively, as Barmar suggested, you can shuffle the array of integers from 0 to 9 beforehand and then loop over the values for better efficiency. Below I have provided an example using the Fisher-Yates shuffle.
const arr = [...Array(10).keys()];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.random() * (i + 1) | 0;
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
for(const num of arr){
//do something with num...
}

Random Selection from Array into new Array

I have seen this question answered here, but I have an additional question that is related.
Im trying to achieve:
the same thing, but, with the output being a selection of more than 1 number, the above works fine if you only want a single value returned.
How can I return (x) amount of outputs #7 in this case into a new var or array ...? Some guidance on best practice will also be appreciated ;)
Thanks a bunch....
Just for fun,
Objective:
Create a teeny weenie web App that returns 7 variable numbers in a range [ 1 - 49 ] into an array.
`
Think return a list of Lotto Numbers
Create new array from selection using _underscore.js [Sample function]
**** I know this is easier, but im trying to get an understanding
of using Vanilla JS to accomplish this
_.sample([1, 2, 3, 4, 5, 6], 3); => [1, 6, 2]
var getLoot = Array.from(Array(50).keys()) // Create array of 49 numbers.
console.info(getLoot);
var pick = getLoot[Math.floor(Math.random() * getLoot.length)];
pick;
// pick returns a single which is fine if you want a single but, ..
// I want something like this :
var pick = function() {
// code that will return 7 numbers from the array into a new Array
// will randomize every time pick is called...
}
If you want to return more than just 1 value you can store your results into a data structure like an array. Here is a solution to the problem
assuming you can pass in your array of 50 numbers into the pick() funciton.:
var getRandomArbitrary = function(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
var pick = function(fiftyNumberArray, numberOfValuesWanted) {
var randomNums = [];
for(var i = 0; i < numberOfValuesWanted; i++) {
randomNums.push(
fiftyNumberArray[getRandomArbitrary(0, 50)]
);
}
return randomNums;
};
var fiftyNumbers = [] // <- set your array of fifty numbers
pick(fiftyNumbers, 7);
Javascript's Math.random() will return a value in between 0 and 1 (exclusive). So to get an index scaled up to the correct value to look into your array, you would want to multiply that by the formula (max - min) + min
You can use Array.prototype.splice(), Math.floor(), Math.random(), for loop to remove elements from input array, return an array containing pseudo randomly picked index from input array without duplicate indexes being selected.
function rand(n) {
var res = []; var a = Array.from(Array(n).keys());
for (;a.length;res.push(a.splice(Math.floor(Math.random()*a.length),1)[0]));
return res
}
console.log(rand(50));
One good way of doing this job is shuffling the array and picking the first n values. Such as;
function shuffle(a){
var i = a.length,
j,
tmp;
while (i > 1) {
j = Math.floor(Math.random()*i--);
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a;
};
var arr = Array(50).fill().map((_,i) => i+1); //[1,2,3...49,50]
randoms = shuffle(arr).slice(0,7) // to get 7 random numbers from arrary arr
console.log(randoms)
This is probably what you want.
$(function()
{
var lol = [1,4,5,6,7,8,9];
function getPart(array,number)
{
var part = [],
index;
while(true)
{
if(part.length == number)
{
break;
}
index = $.random(0,part.length);
part.push(lol.splice(index,1));
}
return part;
}
});
$.random = function(min,max,filter)
{
var i,
n = Math.floor(Math.random()*(max-min+1)+min);
if(filter != undefined && filter.constructor == Array)
{
for(i=filter.length-1;i>-1;i--)
{
if(n == filter[i])
{
n = Math.floor(Math.random()*(max-min+1)+min)
i = filter.length;
}
}
}
return n;
}

how can i make my function more efficient?

hey guys i wrote a function that compares array values and returns the minimum value but i want to know if there are ways to make it more efficient like iterating through all arrays (using one loop) and putting the results in a new array or making individual arrays sub-arrays of a single array, etc. Also the function provides the correct output but prints the answer three times:
var nums1 = [-7528819, 3927361, -6398192];
var nums2 = [1777100, -2299720, -5566643];
var nums3 = [7188445, 3724971, 7699332];
var nums4 = [-8432528, -159836, -1604959];
var nums5 = [2764889, 4681472, 701396];
var nums6 = [-5073513, 599535, 4388457];
var nums7 = [8689640, 8028586, 1022322];
var nums8 = [-1088592, 1211232, -7868192];
var nums9 = [-5848613, -4945165, 631213];
var nums10 = [3218429, -833619, -1495854];
var nums11 = [8007060, 1637562, -7568493];
var nums12 = [-8391131, -6585338, 131787];
var nums13 = [-3957775, -9396892, -6143241];
var nums14 = [-6258442, -7829421, 3696922];
var nums15 = [2136598, 4935467, -1621605];
var nums16 = [-7162005, 9861954, 8977930];
var nums17 = [7226452, 8551594, 7006517];
var nums18 = [-1751226, -2536997, -1782251];
var nums19 = [380582, 1614389, 3272584];
var nums20 = [-8988205, -5167181, -7561034];
var nums21 = [-484059, -7160121, 4076528];
var nums22 = [1947448, -5551253, 7491190];
var numsLength = nums1.length;
var i = 0;
var minNum;
function test(arr) {
for (i; i < numsLength; i++) {
if (arr[0] < arr[1] && arr[2]) {
minNum = arr[0];
} else if (arr[1] < arr[2] && arr[0]) {
minNum = arr[1];
} else if (arr[2] < arr[1] && arr[0]) {
minNum = arr[2];
}
console.log(minNum);
}
}
test(nums1);
You could just use Math.min function.
console.log(Math.min.apply(null, nums1));
Look at his snippet of code and read inline comments:
var nums = [];
// I'm pushing only 3 sets of data, but there can be any number
// Also there can be any number of elements in each array as you can see
nums.push([-7528819, 3927361, -6398192]);
nums.push([1777100, -2299720, -5566643, 380582]);
nums.push([7188445, 3724971, 7699332, 1947448, -5551253, 7491190]);
function produceResults(nums) {
var i,
results = [];
// gathering results
for (i = 0; i < nums.length; i++) {
results.push(Math.min.apply(null, nums[i]));
}
return results;
}
console.log(produceResults(nums));
So 2 suggestions:
use more dynamic structure (array of arrays) instead of
defining 22 arrays.
use built in JS functions and components (Math.min)
Unrolling a loop is actually the most efficient implementation of a loop in most cases. However, practically speaking, unrolling a loop isn't usually feasible. With a small, fixed-size array, like those you have here, each permutation of the loop is obvious, and if your goal is raw speed you can't get much more efficient than what you have. That being said, the loop in your function is useless, as others have pointed out., because you've essentially unrolled the loop already. Also the syntax of the if statement is incorrect, and you are not handling the case where values in the array are equal. For fixed arrays of size three you want something more along the lines of...
if (val1 <= val2 && val1 <= val3) {
minVal = val1;
} else if (val2 <= val1 && val2 <= val3) {
minVal = val2;
} else minVal = val3;
Now if you want to do an arbitrary search for the min value of any size array you would do something similar, but using a loop, like...
var minVal = null;
for (var i = 0; i < arr.length; i++) {
if (minVal === null || minVal > (val = arr[i]))
minVal = val;
}
Depending on what you actually want to accomplish, and the size of the array, it might make sense to sort the array and rerurn the min (0 index) from the sorted array. If you go that route, start with a google search for "sort algorithms"

How to output every number from 1 to 10. Using random numbers in JavaScript [duplicate]

This question already has answers here:
How to randomize (shuffle) a JavaScript array?
(69 answers)
Closed 9 years ago.
I am trying to make a script which is outputting every number from 1-10.
Using a random number generator, in JavaScript.
I want every number to be unique.
Here is an example of what i would like the script to output:
5 9 7 6 1 3 4 8 2 10
This is my attempt:
var test = [];
var amountOfNumbers = 10;
var inArray = false;
var useNumbers = [];
for(var i=0; useNumbers.length<=amountOfNumbers; i++){
var rng = Math.floor((Math.random()*amountOfNumbers)+1);
for(var a=0; a<=test.length; a++){
if(rng == test[a]){
inArray == true;
}
}
if(!inArray){
document.write(rng);
test.push(rng);
useNumbers.push(rng);
}
}
Hope you can help.
for the record I am not interested in jQuery og any other library :)
1) How to fix your code
You have a few errors, among them the fact you don't reset inArray to false and that you don't iterate over the whole test array (use <, not <=). But using a loop to see if you already have the number isn't efficient, it's better to use an object as a map :
var test = [];
var amountOfNumbers = 10;
var useNumbers = {};
for(var i=0; test.length<amountOfNumbers; i++){
var rng = Math.floor((Math.random()*amountOfNumbers)+1);
if(!useNumbers[rng]){
document.write(rng);
test.push(rng);
useNumbers[rng] = true;
}
}
2) How to do it properly
Your algorithm will loop until it is lucky enough to find the remaining numbers. This isn't efficient and isn't predictable. The normal reliable practice is
to generate the array [1..10]
to shuffle it
Generating an array of the integers from 1 to N can be done with a simple loop or in a fancier way :
var arr = Array.apply(0,new Array(N)).map(function(_,i){ return i+1 });
Shuffling an array is usually done with the Fisher-Yates algorithm, for which you'll easily find JS implementations (it's easy to write anyway). A fast (theoretically not guaranteed to work with all future sort implementations) alternative is this one :
arr = arr.sort(function(a,b){ return Math.random()>0.5 });
The whole program
Your approach means to check over all the array in each step, looking if your random number is already inside the array, which means a lot lost time.
Best approach is disordering an ordered array. In each loop, we generate a random number (in the example, a number between 0 and 1) and with a 50% probability we change the item in the current position for other item in a random position (between 0 and the length of the array).
Hope it helps.
function disorder(arg) {
for (var i = 0; i < arg.length; i++) {
if (Math.random() < 0.5) {
var aux = arg[i];
var rndPos = Math.floor(Math.random()) * arg.length;
arg[i] = arg[rndPos];
arg[rndPos] = aux;
}
}
return arg;
}
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var myNewArray = disorder(myArray);
myNewArray.forEach(function(item) {
console.log(item);
});

Stuck using array.splice() to shuffle a deck array

I'm trying to randomly shuffle a deck array of Card objects into a newDeck array of Card objects using array.splice(). I imagine my problem is either something to do with variable scope, or a misapprehension about array.splice().
var deck = [new Card(), new Card(), new Card(), new Card(), new Card(), new Card()];
var newDeck = [];
var shuffle = function(){
var i = "";
for (i = 0; i < deck.length; i++){
newDeck[i] = deck.splice(Math.floor(Math.random() * deck.length, 1));
}
};
shuffle();
Is there a better way to shuffle a deck?
Yes, there is a misapprehension about array.splice(). Read the docs: It will return an array of the removed elements, in your case with one card: [<object Card>]. Also, with deck.splice(Math.floor(Math.random() * deck.length, 1)), the 1 (which i guess should be how many cards to remove) is an argument to Math.floor, not splice - you will remove all elements after the index. So, you seem to want:
function shuffle(deck) {
var newDeck = [];
for (var i = 0; i < deck.length; i++)
newDeck[i] = deck.splice(Math.floor(Math.random() * deck.length), 1)[0];
return newDeck;
}
shuffle([new Card(), new Card(), new Card(), new Card(), new Card(), new Card()]);
You have asked for other ways to shuffle: You could use the very common
deck.sort(function(a, b){ return 0.5-Math.random(); })
or the algorithm from underscore.js:
function shuffle(deck) {
var newDeck = [];
for (var i = 0; i < deck.length; i++) {
var rand = Math.floor(Math.random() * (i + 1));
newDeck[i] = newDeck[rand];
newDeck[rand] = deck[i];
}
return newDeck;
}
You could as well use the Array#shuffle method from underscore.js in case you do not want to reinvent the wheel in that case ;)
Reverse your for cycle:
for (i = deck.length - 1; i >= 0; i--)
newDeck.push(deck.splice(Math.floor(Math.random() * deck.length))[0]);
The problem you're having is that deck.length decreases every time the cycle executes deck.splice. Alternatively, you can save deck.length in a separate variable before running the script:
var length = deck.length;
for (i = 0; i < length; i++){
newDeck[i] = deck.splice(Math.floor(Math.random() * deck.length))[0];
Inside the cycle, though, you have to use deck.length.
Note: Math.floor takes just one argument. What was that ", 1" supposed to be? And initializing i = "" is pointless, since that value is unused. Just put var i;.
Edit: fixed a missing point about the return value of Array.splice.

Categories