shuffle two consecutive blocks of data - javascript

I have data file (php) with 24 blocks of data, structured as below.
//ts1
$P[] = [0];
$S[] = [[1,95.8172406762736],[2,104.97526726371],[3,112.03839938973],[4,101.70457396977],];
$F[] = [];
$FB[] = [];
//ts2
$P[] = [0];
$S[] = [[1,103.190939795922],[2,105.297198378469],[3,105.829786652111]];
$F[] = [];
$FB[] = [];
//ts3
$P[] = [0];
$S[] = [[1,107.285278217373],[2,103.557795069809],[3,105.686758569246],[4,103.748341353355]];
$F[] = [];
$FB[] = [];
I need to shuffle the first 12 blocks and then shuffle block 13-24. The code I have does not appear to work as it still shuffles all 24 blocks at once. I'm not sure how it should be written otherwise..
// DATA INITIALISATION
// - Reads all data sets from server
// - Generates list of objects
// - Randomises list of objects
function DataInit1()
{
SeriesList = [];
CurrentGraph.Series = 0;
// load all the data sets from server
$.getJSON("datademo.php",
function(Data1)
{
for(var i=0; i<Data1.length; i+=4)
{
var P = Data1[i+0];
var S = Data1[i+1];
var F = Data1[i+2];
var FB = Data1[i+3];
var NewSeries = new SeriesClass(P,S,F,FB);
NewSeries.SeriesNumber = (i/4)+1;
SeriesList.push(NewSeries);
}
}
);
// shuffle each of the series lists to a random order
s1 = SeriesList.length/2;
s2 = SeriesList.length;
for(var i=0; i<s1; i++)
{
var j = Math.floor(Math.random() * (i+1));
var x = SeriesList[i];
SeriesList[i] = SeriesList[j];
SeriesList[j] = x;
}
for(var i=s1; i<s2; i++)
{
var j = Math.floor(Math.random() * (i+1));
var x = SeriesList[i];
SeriesList[i] = SeriesList[j];
SeriesList[j] = x;
}
}
edit: I've changed it now to the following (not pretty but I don't have time to tidy it). Now it randomizes series 1-12, but series 13-24 is now not randomized. I don't code that often and I can't see why it would work in the first bit but not the second one.
// shuffle the series list to a random order
for(var i=SeriesList.length-13; i>0; i--)
{
var j = Math.floor(Math.random() * (i+1));
var x = SeriesList[i];
SeriesList[i] = SeriesList[j];
SeriesList[j] = x;
}
for(var i={from: 12, to: 23}; i>0; i--)
{
var j = Math.floor(Math.random() * (i+1));
var x = SeriesList[i];
SeriesList[i] = SeriesList[j];
SeriesList[j] = x;
}

I think the easiest way is to break it into two arrays, shuffle them separately, then push them back together. I've used the shuffle method from this question: How can I shuffle an array?, and the method of joining arrays from How to extend an existing JavaScript array with another array, without creating a new array?
See the fiddle at https://jsfiddle.net/u25ta64x/1/
var SeriesList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26];
var firstHalf = SeriesList.slice(0,13);
var secondHalf = SeriesList.slice(13,26);
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
}
shuffle(firstHalf);
shuffle(secondHalf)
var shuffledList = firstHalf;
Array.prototype.push.apply(shuffledList, secondHalf);
console.log(shuffledList);

Related

Why do I get a NaN value and the end of my rubiks cube scramble generator when I remove duplicates?

I am developing a Rubik cube app for fitbit versa and I run into the problem of removing duplicates from arrays as I get a NaN error when combining the arrays once the duplicates have been removed from the end of the list and it only happens when I splice at the end of the array and I cant figure out the reason why this isnt working
function getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble) {
for (var i = 0; i < number_of_moves; i++) {
var sample = faces[Math.floor(Math.random() * faces.length)];
var mod = modifiers[Math.floor(Math.random() * modifiers.length)];
scramble_faces[i] = sample;
scramble_modifiers[i] = mod;
if (scramble_faces[i] == scramble_faces[i - 1]) {
scramble_faces[i] = faces[Math.floor(Math.random() * faces.length)];
}
}
removeDuplicates(scramble_faces, scramble_modifiers)
for (var i = 0; i < number_of_moves - 2; i++) {
scramble[i] = scramble_faces[i] + scramble_modifiers[i]
}
console.log(scramble);
let demotext = document.getElementById("demotext");
demotext.text = scramble;
scramble = [];
scramble_faces = [];
scramble_modifiers = [];
}
function threebythree() {
var faces = ["U", "D", "L", "R", "F", "B"];
var modifiers = ["", "'", "2"];
var scramble_faces = [];
var scramble_modifiers = [];
var scramble = [];
var number_of_moves = 22;
let Title1 = document.getElementById("title");
Title1.text = "3x3"
getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble, Title1)
}
function removeDuplicates(arr, arr2, number_of_moves) {
var t = 0;
var new_arr = arr;
var new_arr2 = arr2;
for (var i = new_arr.length - 1; i >= 0; i--) {
if (new_arr[i] === new_arr[i - 1]) {
new_arr.splice(i, 1);
new_arr2.splice(i, 1);
}
}
arr = new_arr;
arr2 = new_arr2;
new_arr = [];
new_arr2 = [];
new_arr.pop();
new_arr2.pop();
console.log(arr);
console.log(arr2);
}
The lengths of scramble_faces and scramble_modifiers is initially number_of_moves. But after you remove duplicates from them, it can be shorter. But you still use number_of_moves in the limit in the next for loop. So when you try to add the elements that no longer exist you get undefined. undefined + undefined == NaN.
You should use the length of one of the arrays instead:
function getScramble(number_of_moves, faces, modifiers, scramble_faces, scramble_modifiers, scramble) {
for (var i = 0; i < number_of_moves; i++) {
var sample = faces[Math.floor(Math.random() * faces.length)];
var mod = modifiers[Math.floor(Math.random() * modifiers.length)];
scramble_faces[i] = sample;
scramble_modifiers[i] = mod;
if (scramble_faces[i] == scramble_faces[i - 1]) {
scramble_faces[i] = faces[Math.floor(Math.random() * faces.length)];
}
}
removeDuplicates(scramble_faces, scramble_modifiers)
for (var i = 0; i < scramble_faces.length - 2; i++) {
scramble[i] = scramble_faces[i] + scramble_modifiers[i]
}
console.log(scramble);
let demotext = document.getElementById("demotext");
demotext.text = scramble;
scramble = [];
scramble_faces = [];
scramble_modifiers = [];
}

delete duplicates from a sorted multi 3 elements array using indexes

I want to store 200 choices from 3 players in an array in groups of three ex [1,4,3],[2,1,5]....
Afterwards, I need to delete the duplicates, and since in JS we cannot compare 2 arrays I was thinking in comparing the indexes inside the 3 choices single array after sorting the array and push the ones who are equal to a new array. I dont know why this is not working. Any ideas?
var choices = [1, 2, 3, 4, 5, 6];
var player1 = [];
var player2 = [];
var player3 = [];
var store = [];
var unique = [];
for (i = 0; i < choices.length; i++) {
player1.push(choices[i]);
player2.push(choices[i]);
player3.push(choices[i]);
}
function values() {
for (i = 0; i < 500; i++) {
var rand1 = player1[Math.floor(Math.random() * player1.length)];
var rand2 = player2[Math.floor(Math.random() * player2.length)];
var rand3 = player3[Math.floor(Math.random() * player3.length)];
var choice = [rand1, rand2, rand3];
store.push(choice);
store.sort();
}
for (j = 1; j <= store.length; j++) {
if (store[j][0] == store[j + 1][0] && store[j][1] == store[j + 1][1] && store[j]
[2] == store[j + 1][2]) {
unique.push(store[j]);
console.log(store);
}
}
}
values();
Your problem here is that you are sorting store instead of choice in each iteration.
And another way to compare the arrays is to convert them to strings, and check for their existence in the array, then remove duplicates and convert them back to an array.
This is how can be your code:
function getUniqueValues(arr) {
//Convert the sub arrays to strings
arr = arr.map(function(a) {
return a.join(",");
});
//Remove duplicates
var result = arr.filter(function(subArray, pos) {
return arr.indexOf(subArray) == pos;
});
//Convert back the strings to arrays
result = result.map(function(a) {
return a.split(",").map(Number);
});
return result;
}
And in the end of your values function call it like this:
unique = getUniqueValues(store);
Demo:
This is a working Demo using this function in your code:
function getUniqueValues(arr) {
//Convert the sub arrays to strings
arr = arr.map(function(a) {
return a.join(",");
});
//Remove duplicates
var result = arr.filter(function(subArray, pos) {
return arr.indexOf(subArray) == pos;
});
//Convert back the strings to arrays
result = result.map(function(a) {
return a.split(",").map(Number);
});
return result;
}
var choices = [1, 2, 3, 4, 5, 6];
var player1 = [];
var player2 = [];
var player3 = [];
var store = [];
var unique = [];
for (i = 0; i < choices.length; i++) {
player1.push(choices[i]);
player2.push(choices[i]);
player3.push(choices[i]);
}
function values() {
for (i = 0; i < 50; i++) {
var rand1 = player1[Math.floor(Math.random() * player1.length)];
var rand2 = player2[Math.floor(Math.random() * player2.length)];
var rand3 = player3[Math.floor(Math.random() * player3.length)];
var choice = [rand1, rand2, rand3];
choice.sort();
store.push(choice);
}
unique = getUniqueValues(store);
}
values();
//console.log(store);
console.log(unique);
values method can be updated to store strings (3 numbers concatenated into one) instead of array of number in a Set
function values() {
var store = new Set(); //set instead of array
for (i = 0; i < 500; i++) {
var rand1 = player1[Math.floor(Math.random() * player1.length)];
var rand2 = player2[Math.floor(Math.random() * player2.length)];
var rand3 = player3[Math.floor(Math.random() * player3.length)];
store.add(rand1 + "-" + rand2 + "-" + rand3); //add value to set, set will remove duplicates automatically
}
return [...store].sort().map(s => s.split("-").map(t => +t)); //convert set to array, sort that array and convert the string to array of numbers
//convert to set
}
Demo
var choices = [1, 2, 3, 4, 5, 6];
var player1 = choices.slice();
var player2 = choices.slice();
var player3 = choices.slice();
function values() {
var store = new Set(); //set instead of array
for (i = 0; i < 500; i++) {
var rand1 = player1[Math.floor(Math.random() * player1.length)];
var rand2 = player2[Math.floor(Math.random() * player2.length)];
var rand3 = player3[Math.floor(Math.random() * player3.length)];
store.add(rand1 + "-" + rand2 + "-" + rand3);
}
return [...store].sort().map(s => s.split("-").map(t => +t));
//convert to set
}
console.log(values());

Calling all possible arrays from the length of another array

What I'm working on is a menu that auto updates its entries based on an array length. It adds groups of 10 objects' properties (in this case "IDnumbers") to the menu if a new object is added to the array.
var arraysOfObject = [], obj = {"IDNumber": ""};
for(i = 0; i<42; i++){
arraysOfObject.push({"IDNumber": "Number " + i});}
Above is the array holding 42 objects with a specific property.
var array2 = [];
var leftOver = arraysOfObject.length % 10;
var groupsOfTen = (arraysOfObject.length - leftOver)/10;
for (var i = 0; i < groupsOfTen; i++) {
array2.push([]);
for (var j = i*10; j < i*10 + 10; j++)
array2[i].push(arraysOfObject[j]["IDNumber"]);
}
//now the leftover
if (leftOver > 0) {
array2.push([]);
for (var i = groupsOfTen*10; i < arraysOfObject.length; i++)
array2[array2.length-1].push(arraysOfObject[i]["IDNumber"]);
}
The array2 above is the array that stores all the possible arrays that can be grouped by 10 from arraysOfObject. In this case there are 5 inside of it, because 4 arrays holds 40 objects, and 1 array holds the 2 remainders.
That all works fine, but placing the array2 inside the menu displays all possible IDnumbers grouped together, but not grouped individually. I have to declare each possible array inside of it like so sets = [array2[0], array2[1], array2[2], array2[3], array2[4]]; If there's a 6th possible array because object #51 has been added to arraysOfObject, I have to input it with array2[5].
I don't want it to depend on my input, but that it knows the number of possible arrays and that it displays it automatically in sets. How do I do that?
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
sets = [array2[0], array2[1], array2[2], array2[3], array2[4], array2[5]];
gui.add(data, 'message', 'Dat.Gui Menu!');
gui.add(data, 'system', {
"1": 0,
"2": 1,
"3": 2,
"4": 3,
"5": 4,
"6": 5,
}).name('system #').onChange(function(value) {
updateSets(value);
});
gui.add(data, 'Sets', sets[0]).onChange();
function updateSets(id) {
var controller = gui.__controllers[2];
controller.remove();
gui.add(data, 'Sets', sets[id]).onChange();
data.Sets = 0;
gui.__controllers[2].updateDisplay();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
<script>
var arraysOfObject = [], obj = {"IDNumber": ""};
for(i = 0; i<42; i++){
arraysOfObject.push({"IDNumber": "Number " + i});}
var array2 = [];
var leftOver = arraysOfObject.length % 10;
var groupsOfTen = (arraysOfObject.length - leftOver)/10;
for (var i = 0; i < groupsOfTen; i++) {
array2.push([]);
for (var j = i*10; j < i*10 + 10; j++)
array2[i].push(arraysOfObject[j]["IDNumber"]);
}
//now take care of the leftover
if (leftOver > 0) {
array2.push([]);
for (var i = groupsOfTen*10; i < arraysOfObject.length; i++)
array2[array2.length-1].push(arraysOfObject[i]["IDNumber"]);
}
</script>
Not the issue at hand, but I was playing around with the dat.gui as you posted it and was wondering if the dropdown could be refilled without removing/adding/etc. It seems to work with .options. (NB The initialization code makes heavy use of ES6, but can work without. The system menu is created dynamically from the sets array)
let arraysOfObject =Array.from({length:42}, (o,i) => "Number " + i),
ch =10, sets = Array.from({length:Math.ceil(arraysOfObject.length/ch)}, (a,i) => arraysOfObject.slice(i*=ch, i+ch));
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
gui.add(data, 'message', 'Dat.Gui Menu!');
gui.add(data, 'system', sets.reduce((obj,s,i) => (obj[i+1] = i, obj), {})).name('system #').onChange(updateSets);
let controller = gui.add(data, 'Sets');
updateSets(0);
function updateSets(id) {
controller = controller.options(sets[data.Sets = id]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.1/dat.gui.min.js"></script>
I think the easiest solution would be to use ES2015's spread operator which I don't know if you would want to use yet...
ES2015 method (demo)
sets = [...array2];
There are a few other changes in the demo to set the system variable
But after taking a closer look, you can optimize the code by using the method from this SO answer to chunk your array using slice(). Also, I'm not sure why an object was used to create array entries when it just ends up as a string... demo
var arraysOfObject = [],
system = {},
chunk = 10,
size = 92;
for (var i = 0; i < size; i++) {
arraysOfObject.push("Number " + i);
}
var sets = [];
var index = 0;
for (i = 0; i < size; i += chunk) {
sets.push(arraysOfObject.slice(i, i + chunk));
system[index + 1] = index++;
}
var gui = new dat.GUI();
var guiData = function() {
this.message = "Dat.Gui menu";
this.system = 0;
this.Sets = 0;
};
var data = new guiData();
gui.add(data, 'message', 'Dat.Gui Menu!');
gui
.add(data, 'system', system)
.name('system #')
.onChange(function(value) {
updateSets(value);
});
gui.add(data, 'Sets', sets[0]).onChange();
function updateSets(id) {
var controller = gui.__controllers[2];
controller.remove();
gui.add(data, 'Sets', sets[id]).onChange();
data.Sets = 0;
gui.__controllers[2].updateDisplay();
}

Defining a 3D array in JavaScript

I tried to define a 3D array on Google Sheet, but even though I'm using the .slice() method it keeps passing the array by reference.
var temp = [];
for (var a = 0; a<archetypesAll.length; a++) {temp[a] = [0, a].slice();};
var archRank = [];
for (var a = 0; a<21; a++) {archRank[a]= temp.slice();};
archRank[2][1][0] = 'Test';
I want to edit a single element of the matrix but instead the code above just fills every row with the exact same value ('Test'):
3DMatrix[x][1][0] = 'Test'
You can't just copy a multidimensional array by calling slice at the top level, because that will not deep-copy the whole. You have to write your own deepCopy methid, like this:
function allocate(mainDim, ...dims) {
const result = new Array(mainDim);
for (let i = 0; i < result.length; i++) {
result[i] = dims.length > 0 ? allocate(...dims) : 0;
}
return result;
}
function deepCopy(matrix, dims) {
return dims > 1 ? matrix.map(row => deepCopy(row, dims - 1)) : matrix.slice();
}
function test() {
const mx1 = allocate(3,2,2);
mx1[2][1][0] = "Test";
console.log(JSON.stringify(mx1));
const mx2 = deepCopy(mx1, 3);
mx2[2][1][0] = "Copied";
console.log(JSON.stringify(mx1));
console.log(JSON.stringify(mx2));
}
test();
var array = ["Test", "Test"];
var array3d = [[array.slice(0)],[[array.slice(0)]]];
array3d[0][0][0] = "Changed";
console.log(JSON.stringify(array3d)); //[[["Changed","Test"]],[[["Test","Test"]]]]
Try with this instead of slice to get a new array instead of reference:
var temp = [];
for (var a = 0; a < archetypesAll.length; a++) {
temp[a] = JSON.parse(JSON.stringify([0, a]));
}
var archRank = [];
for (var a = 0; a < 21; a++) {
archRank[a]= temp.slice();
}
archRank[2][1][0] = 'Test';

How could I iterate with a for loop to create many objects with Object.create on the fly in javascript?

I am trying to create a function that will dynamically create objects on the fly based on the input number arguments, but I'm running into an issue with iterating over the Object.create() part. I don't know where to play my i in the for loop, but ideally I would have all the sportsCar objects stored in the sportArray. That is the target at least.
function car(doors, capacity, storage) {
this.doors = doors;
this.capacity = capacity;
this.storage = storage;
};
var van = Object.create(car);
van.doors = 4;
van.storage = "rear storage";
var miniVan = Object.create(van);
miniVan.capacity = "200 LB";
var cargoVan = Object.create(van);
cargoVan.capacity = "800 LB";
var truck = Object.create(car);
truck.doors = 2;
truck.storage = "bed";
truck.capacity = "1500 LB";
var familyCar = Object.create(car);
familyCar.doors = 4;
familyCar.storage = "large trunk";
familyCar.capacity = "300 LB";
var sportsCar = Object.create(car);
sportsCar.doors = 2;
sportsCar.storage = "small trunk";
sportsCar.capacity = '100 LB';
function tally(n1, n2, n3, n4, n5) {
var sportArray = [];
var familyArray = [];
var truckArray = [];
var miniArray = [];
var cargoArray = [];
sportsObjs = for(var i = 0; i < n1; i++){
Object.create(sportsCar);
}
sportArray.push(sportsObjs);
for (var i = 0; i < n2; i++){
Object.create(familyCar);
}
for(var i = 0; i < n3; i++){
Object.create(truck)
}
for(var i = 0; i < n4; i++){
Object.create(miniVan)
}
for(var i = 0; i < n5; i++){
Object.create(cargoVan)
}
return console.log(sportsArray);
}
sportsObjs = for(var i = 0; i < n1; i++){
Object.create(sportsCar);
}
sportArray.push(sportsObjs);
That's a plain syntax error. A loop is a statement in JavaScript, not an expression - it doesn't yield a value. You can't assign it to a variable. What you actually want is to assign each newly created object to the variable, and then push that particular new object to the array:
for (var i = 0; i < n1; i++){
var sportsObj = Object.create(sportsCar);
sportArray.push(sportsObj);
}
You need only to push objects in the array inside the loop:
function tally(n1, n2, n3, n4, n5) {
var sportArray = [];
var familyArray = [];
var truckArray = [];
var miniArray = [];
var cargoArray = [];
for(var i = 0; i < n1; i++){
sportArray.push(Object.create(sportsCar)); // To create a generic Object sportArray.push({});
}
.... // And so on for all the arrays
}
The problem is that you declared the different arrays as var so they are not visible outside the body of the function.
You need to return an object containg all the arrays, something like that:
function tally(n1, n2, n3, n4, n5) {
var sportArray = [];
var familyArray = [];
var truckArray = [];
var miniArray = [];
var cargoArray = [];
...
return {
sportArray: sportArray,
familyArray : familyArray,
truckArray: truckArray,
miniArray: miniArray,
cargoArray: cargoArray
}
}
So you can do something like:
var result = tally(3, 4, 5, 6, 7);
console.log(result.sportArray.length);
To be more succint with parameters:
function tally(parameters) {
...
for (var i = 0; i < parameters.n1; i++) {
...
}
...
}
Calling tally in this manner:
var result = tally({n1: 3, n2:4, n3:5, n4:6, n5:7});

Categories