JQuery object shuffle / randomize - javascript

Hi I have a JQuery object with multiple divs inside each containing text:
var object = $(<div>我</div>,<div>喜欢</div>,<div>吃</div>);
I want to copy the object and shuffle the divs inside so that when I display the new object, it will be a shuffled version of the original object. Is this possible with a JQuery object or am I going to have to store the divs in an array, and if so can someone tell me how I could achieve this?

Use a generic shuffle algorithm (e.g. the Fisher-Yates shuffle) and pass your object as the argument, e.g.
function shuffle(array) {
var m = array.length, t, i;
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
};
var object = $('<div>1</div><div>2</div><div>3</div><div>4</div><div>5</div>');
/* use spread operator to pass the array of divs */
var shuffledArray = shuffle([...object]);
/* append divs to the body */
$('body').html($(shuffledArray));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Related

What is wrong with my shuffling program?

I wrote a shuffling program below and ran it through "Will It Shuffle?". The results appear to show that it's working in the console; it's shuffling the array. But the website shows me an all red box, making me think something is wrong with my code, but I don't see it.
function shuffle (array) {
var arr = [],
length = array.length,
el;
while (length > 0) {
var randomEl = Math.floor(Math.random() * (length - 0) - 0);
if (length > 1) {
el = array.splice(randomEl,1);
} else {
el = array.splice(0,1);
}
arr.push(el[0]);
length -= 1;
}
return arr;
}
That page ignores the returned value of the function, because it expects an in-place sort.
If you add this at the end of your code, it works as expected:
array.push(...arr);
You can also do it in-place directly:
function shuffle (array) {
var length = array.length;
while (length) {
var randomEl = Math.floor(Math.random() * length);
var el = array.splice(randomEl, 1);
array.push(el[0]);
--length;
}
}
They alter the array, you do not alter the array.
You need to alter the original array, not return a new array.
function shuffle (array) {
var arr = [],
length = array.length,
el;
while (length > 0) {
var randomEl = Math.floor(Math.random() * (length - 0) - 0);
if (length > 1) {
el = array.splice(randomEl,1);
} else {
el = array.splice(0,1);
}
arr.push(el[0]);
length -= 1;
}
//replace array with the new items
//it is like using concat, but does not produce a new array,
//just appends it to the original which has zero items in it.
Array.prototype.push.apply(array, arr);
}
What you are doing is creating a new array with the elements of the original shuffled around.
However, if you go back and look at the array that you passed in, you'll notice it has not been shuffled, but rather emptied. Apparently, this is not what "Will it Shuffle?" asks you to do.
splice() and push() both always mutate the array you call those methods on.
To answer your question about .push(...arr), an elipses in javascript is a feature that arrived with the latest version, EcmaScript 2015. It is the "spread operator".
When you call a function with a "spread" array, it's like calling the function with the contents of the array as separate arguments. For instance,
array.push(...[1,2,3])
is the same as calling
array.push(1,2,3)
push() can add an arbitrary number of comma-separated arguments to an array. So, after emptying the array argument with your looped splices, you could push the contents of the newly-created arr to the empty array using the spread operator.

What is wrong with javascript?

I wrote some simple javascript code, but it does not return the expected result.
var arr = new Array();
var firstobj = {
cta: 0,
ccc: 0,
crc: 0
}
for (var ta = 1; ta <= 10; ta++) {
arr[ta] = firstobj;
}
arr[6].cta = 1;
console.log(arr);
I only change cta value of 6th element, but it changes for all elements.
Expected:
[{cta:0, ccc:0, crc:0}, ..., {cta:1, ccc:0, crc:0}, {cta:0, ccc:0, crc:0}, ...]
// ^ 6th element
Actual:
[{cta:1, ccc:0, crc:0, ...] // All have 1
Objects in Javascript are assigned by pointer (well technically JS doesn't have pointers, but it works like a pointer does in other languages). So, you have filled your array so that every element contains a pointer-like reference to the exact same object. Thus, when you change that object, you see the change everywhere because there's only one object and you changed it.
If you want to initialize an array with separate objects, you have to create a new object or clone an existing object and assign that separate object into each element of your array.
For example, you could do that like this:
var myArray = [];
var obj;
// Also, arrays start from 0, not from 1.
for (var i = 0; i < 10; i++) {
// create a new object for each item in the array
obj = {cta:0, ccc:0, crc:0};
myArray[i] = obj;
}
myArray[6].cta = 1;
document.write(JSON.stringify(myArray));
you assigned the same object reference to all values in the array
make it (use Object.create)
for (var ta = 1; ta <= 10; ta++)
{
arr [ta] = Object.create( firstobj );
}
When you save the same firstobj to your array it saved by reference so when you changing one of the values it changes all of them, my suggestion is to create a new object for each element in your array.
var arr = new Array();
for (var ta = 1; ta <= 10; ta++) {
arr [ta] = { cta:0,
ccc:0,
crc:0
};
}
arr[6].cta=1;
console.log(arr);
Only the 6th element's cta value must be 1, but all elements cta
values change.
all the elements in your array(apart from element at zero index , which would be undefined in your case) are referring to the same object so if you change property in one element it is refelected across all elements
Each element in array is referring to same object.
So when you change object value it is reflected in all elements.

Javascript/Angular returning array after shuffle

I am creating a simple shuffle/unshuffle function in an Angular app I'm building. The idea is there is a single button, on click it will either clone the array, shuffle the array, then return a shuffled order of the array, or if the array has already been shuffled, it will return the clone of the original array so that the user can revert back to the original order.
The issue I am having is I cannot figure out how to return the original order clone back to the view.
Here is a Fiddle: http://jsfiddle.net/nf6j1qvz/
Here is some function code:
$scope.shuffleThis = function(array) {
if(!$scope.isShuffled){
$scope.isShuffled = true;
$scope.unshuffled = array.slice(0);
var m = array.length, t, i;
// While there remain elements to shuffle
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}else{
console.log('unshuffling');
$scope.isShuffled = false;
array = $scope.unshuffled;
return array;
}
}
You can change your ng-click as follows
<button ng-click="array = shuffleThis(array)">
And you're done!
Plunkr:
http://jsfiddle.net/grmqxx9e/
Use angular.copy instead to clone the array. It's a deep copy and has always worked for me where the method you are using is not reliable.
https://docs.angularjs.org/api/ng/function/angular.copy
var originalArray = [];
angular.copy(array, originalArray);
// Continue doing your stuffs
But also, you are calling a function that has a return, so you are not setting that variable properly.
You could either change your ng-click to
ng-click='array = shuffleThis(array)'
Or instead of
return array
in your function, do
$scope.array = array;
I would do the second method personally.

How do you select a random variable within a function in JavaScript?

I have put multiples variables within a function and I was wondering if there was any way possible in JavaScript to select a variable within that function at random. Any help is greatly appreciated. Thank you so much.
If you use an array instead of multiple variables then you can select a random element from the array:
function test() {
var values = ["test","values","go","here"],
valueToUse = values[Math.floor(Math.random() * values.length)];
// do something with the selected value
alert(valueToUse);
}
Demo: http://jsfiddle.net/XDn2f/
(Of course the array doesn't have to contain simple values like the strings I showed, you could have an array of objects, or references to other functions, etc.)
If one of your parameters is an array you can randomly select one value from it.
function myFunc(arrayInput)
{
var randomIndex = Math.floor((Math.random()*10)+1);
return (arrayInput[randomIndex]);
}
If you have N variables, then it is cleanest to put them in an array and generate a random index into that array.
var items = [1,2,3,4];
var index = Math.floor(Math.random() * items.length);
items[index] = whatever;
If you only have a couple variables, you can generate a random number and use an if/else statement to operate on the desired variable.
var a, b;
var index = Math.random();
if (index < 0.5) {
// operate on a
a = 3;
} else {
// operate on b
b = 3;
}

How would you keep a Math.random() in javascript from picking the same numbers multiple times?

I have an array var words = []//lots of different words in it. I have a Math.floor(Math.random()*words.length) that chooses a random word from the array. This is run in a loop that runs for a random number of times (between 2 and 200 times). I would like to make sure that the random numbers do not get chosen more than once during the time that that loop runs. How would you suggest doing this?
There's multiple ways of doing this.
You can shuffle the entire collection, and just grab items from one end. This will ensure you won't encounter any one item more than once (or rather, more than the number of times it occured in the original input array) during one whole iteration.
This, however, requires you to either modify in-place the original collection, or to create a copy of it.
If you only intend to grab a few items, there might be a different way.
You can use a hash table or other type of dictionary, and just do a check if the item you picked at random in the original collection already exists in the dictionary. If it doesn't, add it to the dictionary and use it. If it already exists in the dictionary, pick again.
This approach uses storage proportional to the number of items you need to pick.
Also note that this second approach is a bit bad performance-wise when you get to the few last items in the list, as you can risk hunting for the items you still haven't picked for quite a number of iterations, so this is only a viable solution if the items you need to randomly pick are far fewer than the number of items in the collection.
There are several different approaches, which are more or less effective depending on how much data you have, and how many items you want to pick:
Remove items from the array once they have been picked.
Shuffle the array and get the first items.
Keep a list of picked items and compare against new picks.
Loop through the items and pick values randomly based on the probability to be picked.
I'd shuffle the array as follows and then iterate over the shuffled array. There's no expensive array splice calls and the shuffling routine consists of swapping two values in the array n times where n is the length of the array, so it should scale well:
function shuffle(arr) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor(i * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled;
}
console.log(shuffle(["one", "two", "three", "four"]));
this is how you can do it without shuffling the whole array
a = "abcdefghijklmnopq".split("");
c = 0;
r = [];
do {
var len = a.length - c;
var rnd = Math.floor(Math.random() * len);
r.push(a[rnd]);
a[rnd] = a[len - 1];
} while(++c < 5);
console.log(r);
the idea is to pick from 0..(length - step) elements and then shift the picked element towards the end.
I'd try using a map (Object literal) instead of an Array with keys being indexes:
var words = [ /* ... */ ] , map = { } , length = words.length ;
for(var n = 0 ; n < length ; n++) map[n] = words[n] ;
then make a function to pick a random entry based on the length, delete the entry (hence the index) and adjust the length:
function pickRandomEntry() {
var random = Math.floor( Math.random() * length ) ;
var entry = map[random] ;
delete map[random] ;
length-- ;
return entry ;
}
with this approach, you have to check for an undefined return value (since random might return the same number) and run the function again until it returns an actual value; or, make an array of picked indexes to filter the random numbers (which will however slow performance in case of long iteration cycles).
HTH
There are several solutions to this.
What you could do is use .splice() on your array to remove the item which is hit by words.
Then you can iterate over your array until it's empty. If you need to keep the array pristine you can create a copy of it first and iterate over the copy.
var words = ['apple', 'banana', 'cocoa', 'dade', 'elephant'];
while (words.length > 0) {
var i = Math.floor(Math.random()*words.length);
var word = words.splice(i, 1)[0];
}
Or something to that effect.
Here is a way with prime numbers and modulo that seems to do the trick without moving the original array or adding a hash:
<html>
<head>
</head>
<body>
shuffle
<div id="res"></div>
<script>
function shuffle(words){
var l = words.length,i = 1,primes = [43,47,53,59,61,67,71,73,79],//add more if needed
prime = primes[parseInt(Math.random()*primes.length, 10)],
temp = [];
do{
temp.push((i * prime) % l);
}while(++i <= l);
console.log(temp.join(','));
console.log(temp.sort().join(','));
}
</script>
</body>
</html>

Categories

Resources