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

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.

Related

randomize objects in array [duplicate]

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>

Copy array --> stack or heap overflow?

I have an array named globalArrayAllTrades as you see below. I simply like to INVERT the date in a new copy of the array. So I loop through, create a new object and add it to the new array - simple.
Then function does exactly as expected. BUT if the array contains too many objects the code fails with a "FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory".
My laptop has 8 GB of memory...When the NODEJS process crashes it uses about 1.5 GB and about 70% of of totally amount of available memory is used.
I do run the NODEJS app with the parameter: --max_old_space_size=5000 which normally fixes every thing. But not this one and i have tried MANY different ways to code the same function - BUT each and every time - it fails...unless the original array is smaller.
How can I fix this issue?
function invertTrades(){
var original = globalArrayAllTrades.slice();
globalArrayAllTrades.length = 0;
globalListAllTrades.length = 0;
for(var i = 0; i < original.length; i++){
var objS = original[i];
var objE = original[original.length-1-i];
var objInv = new TradePoint(objS.number, objS.matchdate, objE.price, objE.size, objE.issell);
globalArrayAllTrades.push(objInv);
globalListAllTrades[objInv.matchdate] = objInv;
}
}
You can save some memory by making original just contain the properties you need to invert, not the whole TradePoint object. Then you don't need to construct new TradePoint objects, you can modify them in place.
var original = globalArrayAllTrades.map(function(trade) {
return {
trade.price,
trade.size,
trade.issell
};
}).reverse();
globalArrayAllTrades.forEach(function(trade, i) {
trade.price = original[i].price;
trade.size = original[i].size;
trade.issell = original[i].issell;
});
And since all the objects were modified in place, there's no need to update globalListAllTrades.
Another way is to swap the price, size, and issell properties in place between the pairs of elements:
var midpoint = Math.floor(globalArrayAllTrade.length/2);
for (var i = 0; i < midpoint; i++) {
var objS = globalArrayAllTrades[i];
var objE = globalArrayAllTrades[globalArrayAllTrades.length-1-i];
var temp = objS.price;
objS.price = objE.price;
objE.price = temp;
temp = objS.size;
objS.size = objE.size;
objE.size = temp;
temp = objS.issell;
objS.issell = objE.issell;
objE.issell = temp;
}
Have you considered just doing this?
// Copy array and then reverse it
var newArray = [].concat(original).reverse();
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
I would suggest avoiding to copy that array:
function getInverse(i) {
var objS = globalArrayAllTrades[i];
var objE = globalArrayAllTrades[globalArrayAllTrades.length-1-i];
var objInv = new TradePoint(objS.number, objS.matchdate, objE.price, objE.size, objE.issell);
globalListAllTrades[objInv.matchdate] = objInv;
return objInv;
}
function invertTrades(){
globalListAllTrades.length = 0;
for (var i = 0, l = Math.floor(globalArrayAllTrades.length/2); i < l; i++) {
var j = globalArrayAllTrades.length-1-i;
var a = getInverse(i);
var b = getInverse(j);
globalArrayAllTrades[i] = a;
globalArrayAllTrades[j] = b;
}
}

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"

Javascript Typed array vs simple array: performance

What I'm basically trying to do is to map an array of data points into a WebGL vertex buffer (Float32Array) in realtime (working on animated parametric surfaces). I've assumed that representing data points with Float32Arrays (either one Float32Array per component: [xx...x, yy...y] or interleave them: xyxy...xy) should be faster than storing them in an array of points: [[x, y], [x, y],.. [x, y]] since that'd actually be a nested hash and all. However, to my surprise, that leads to a slowdown of about 15% in all the major browsers (not counting array creation time). Here's a little test I've set up:
var points = 250000, iters = 100;
function map_2a(x, y) {return Math.sin(x) + y;}
var output = new Float32Array(3 * points);
// generate data
var data = [];
for (var i = 0; i < points; i++)
data[i] = [Math.random(), Math.random()];
// run
console.time('native');
(function() {
for (var iter = 0; iter < iters; iter++)
for (var i = 0, to = 0; i < points; i++, to += 3) {
output[to] = data[i][0];
output[to + 1] = data[i][1];
output[to + 2] = map_2a(data[i][0], data[i][1]);
}
}());
console.timeEnd('native');
// generate data
var data = [new Float32Array(points), new Float32Array(points)];
for (var i = 0; i < points; i++) {
data[0][i] = Math.random();
data[1][i] = Math.random();
}
// run
console.time('typed');
(function() {
for (var iter = 0; iter < iters; iter++)
for (var i = 0, to = 0; i < points; i++, to += 3) {
output[to] = data[0][i];
output[to + 1] = data[1][i];
output[to + 2] = map_2a(data[0][i], data[1][i]);
}
}());
console.timeEnd('typed');
Is there anything I'm doing wrong?
I think your problem is that you are not comparing the same code. In the first example, you have one large array filled with very small arrays. In the second example, you have two very large arrays, and both of them need to be indexed. The profile is different.
If I structure the first example to be more like the second (two large generic arrays), then the Float32Array implementation far outperforms the generic array implementation.
Here is a jsPerf profile to show it.
In V8 variables can have SMI (int31/int32), double and pointer type. So I guess when you operate with floats it should be converted to double type. If you use usual arrays it is converted to doubles already.

Function shift() doesn't work inside loop

I'm trying to manipulate an array inside a for loop, where I want to add an item to the end of the array and delete an element at the beginning of the array, like this:
var internal = new Array();
for(var i = 0; i < 1000; i++) {
internal[i] = Math.floor(Math.random() * 37);
internal.shift();
console.log(internal.length);
}
The problem is that it looks like shift() doesn't work inside the loop, in fact, no element is deleted from the array!
Is there a solution?
Here JsFiddle
It reduces by one every time, but you are growing it every time by accessing it with the array index accessing. Instead of
internal[i] = Math.floor(Math.random() * 37);
use
internal.push(Math.floor(Math.random() * 37));
For example,
var internal = [];
internal[3] = "thefourtheye";
console.log(internal);
Output
[ , , , 'thefourtheye' ]
It made space for the first three elements and added the element at the index specified. So, it will keep the array growing.
Note: Use [] to create a new array, instead of new Array()
because you are using a hard coded index, try
var internal = new Array();
for(var i = 0; i < 1000; i++) {
internal.push(Math.floor(Math.random() * 37));
internal.shift();
console.log(internal.length);
}
Demo: Fiddle
//test
var arr = [];
arr[50] = 1;
console.log('arr.length', arr.length);
will print 51 not 1

Categories