I have an array of 50 objects as elements.
Each object contains an array of 4 elements:
var all = [{
question: "question 1 goes here",
options: ["A", "B", "C", "D"]
}, ... {
question: "question 50",
options: ["A", "B", "C", "D"]
}]
I want to select 10 elements randomly and save to two other arrays, one of the array I want to shuffle options. but when shuffling both arrays are affected.
var selected = [];
var shuffled = [];
for(let i = 0; i < 10; i++) {
let rand = Math.floor(Math.random() * all.length);
selected.push(all[rand]);
shuffled.push(all[rand]);
all.splice(rand, 1);
for(let j = 3; j > 0; j--) {
let rand2 = Math.floor(Math.random() * j);
[
shuffled[i].options[j],
shuffled[i].options[rand2]
] = [
shuffled[i].options[rand2],
shuffled[i].options[j]
];
}
}
console.log(selected); // is shuffled too
console.log(shuffled);
How do I prevent that?
I feel like I'm missing something pretty simple, but I can't spot it.
You need to create new instances for the chosen objects and their options arrays:
// shuffle array a in place (Fisher-Yates)
// optional argument len (=a.length):
// reduced shuffling: shuffling is done for the first len returned elements only,
// array a will be shortened to length len.
function shuffle(a, len=a.length){
for(let m=a.length,n=Math.min(len,m-1),i=0,j;i<n;i++){
j=Math.floor(Math.random()*(m-i)+i);
if (j-i) [ a[i],a[j] ] = [ a[j],a[i] ]; // swap 2 array elements
}
a.length=len;
return a;
}
const all=[...new Array(50)].map((_,i)=>({question:"Question "+(i+1), options:["A","B","C","D"]}));
const selected = shuffle([...all],10), // return first 10 shuffled elements only!
shuffled = selected.map(o=>({...o,options:shuffle([...o.options])}));
console.log(selected) // is no longer shuffled!
console.log(shuffled);
I consigned the shuffle algorithm to a separate function (shuffle()) and applied it twice: first to the all array to make sure we don't get any duplicates in our "random" selection and then to the options arrays contained within their sliced-off objects. The function shuffle(a,len) sorts array a in place. I made it return the array reference again purely out of convenience, as it helps me keep my code more compact. The optional argument len will cause the array a to be shortened to len shuffled elements (still "in place": the input array will be affected too!).
So, in order to preserve my "input" arrays I created new array instances each time I called the function by applying the ... operator:
shuffled = shuffle([...all],10);
...
shuffle([...o.options])
Try using the spread function that makes a hard copy from you object all
var selected = [];
var shuffled = [];
// Make a copy of all by using the spread function:
// You can later use this variable since it will contain the content of the
// initial all variable
const allCopy = [...all];
for (let i = 0; i < 10; i++) {
let rand = Math.floor(Math.random() * all.length);
selected.push(all[rand]);
shuffled.push(all[rand]);
all.splice(rand, 1);
for (let j = 3; j > 0; j--) {
let rand2 = Math.floor(Math.random() * j);
[shuffled[i].options[j], shuffled[i].options[rand2]] = [shuffled[i].options[rand2], shuffled[i].options[j]];
}
}
console.log(selected); // is shuffled too
console.log(shuffled);
Related
New to javascript. Need some guidance on the problem below:
I have these arrays:
array1 = [['a1'], ['a2'], ['a3']];
array2 = [['a1',4], ['a3',3], ['a6',2]];
How can i get the matching arrays whereby array1 first col = array2 first col?
Example expected result:
[['a1',4], ['a3',3]]
I hope the question makes sense. Not really sure how to approach this since the structure of these two arrays are different.
You can use filter to filter out the elements. Inside filter method check if elements exist in array1. You can flat array1 to check effeciently.
let flatArr1 = array1.flat(); //["a1", "a2", "a3"]
const result = array2.filter(([x,y]) => flatArr1.includes(x));
console.log(result) // for instance
This is my solution for your problem:
const compare = (array1, array2) => {
let matched = [];
const keys = [].concat(...array1);
for(let i = 0; i < array2.length; i++) {
for(let j = 0; j < array2.length; j++) {
let result = array2[i][j];
if(keys.includes(result)) {
matched.push(array2[i])
}
}
}
console.log("matched: ", matched);
};
The question in the title is the one that I've seen on SO before many times, and I like to share my code snipped to do that. If anyone has any improvements, I like to see them.
I used a different(?) approach.
Basically, counting from 0 to choices product, and converting to a "number" with each "digit" with it's own "base"...
var arr=[
[1,2,3,4],
[5,6,7],
[8,9,10],
[11,12],
[13,14,15]
];
var lengths=arr.map(subArr=>subArr.length);
var lengthsProd=lengths.reduce((acc,cv)=>acc*cv,1);
var masterIndexes=[];
for(var i=0; i<lengthsProd; i++) {
var indexes=[];
var index=i;
for(var j=lengths.length-1; j>=0; j--) {
var jlength=lengths[j];
indexes.unshift(index%jlength);
index=Math.floor(index/jlength);
}
masterIndexes.push(indexes);
};
var combinations=masterIndexes.map(index=>
index.map((i, idx)=>arr[idx][i])
);
console.log("masterIndexes:");
console.log(masterIndexes.join(", "));
//masterIndexes.forEach(mi=>console.log(mi.join(", ")));
console.log("-".repeat(50));
console.log("combinations:");
console.log(combinations.join(", "));
//combinations.forEach(c=>console.log(c.join(", ")));
.as-console-wrapper { max-height: 100% !important; top: 0; }
let arraySet = [
[1,2,3,4],
[5,6,7],
[8,9,10],
[11,12],
[13,14,15]
]
// We need to know how many unique combinations can be created.
// In this example it's 216.
let outputArrayLength = 1;
arraySet.forEach(array => {
outputArrayLength = outputArrayLength * array.length
});
// Let's create the output array that contains empty arrays for now.
// The number of items should be equal to outputArrayLength;
let outputArray = Array.from(Array(outputArrayLength), () => []);
// To fill the empty arrays properly, we need to know 3 things.
// 1. How many iterations are needed per item?
// We need to calculate the number of unique combinations of the arrays before the current one.
// 2. How many times should that item be pushed in each iteration?
// We need to calculate the number of unique combinations of the arrays after the current one.
// For example, when we reached the 3rd array, which is [8,9,10] in the loop,
// each array item will be pushed 6 (2 * 3) times in each iteration of 12 (4 * 3).
// 3. Does the current item belong to which array in outputArray?
// By incrementing lastIndex after each item push, we will know which array is our new target.
let previousCombinations = 1;
let remaningCombinations = outputArrayLength;
let lastIndex = 0;
arraySet.forEach(array => {
// Calculate 2
remaningCombinations = remaningCombinations / array.length;
// Push the item to the target array.
for (let i = 0; i < previousCombinations; i++) {
array.forEach(num => {
for (let i = 0; i < remaningCombinations; i++) {
outputArray[lastIndex].push(num);
if (lastIndex < outputArrayLength -1) {
lastIndex++;
} else {
lastIndex = 0;
}
}
})
}
// Calculate 1 before the next loop.
previousCombinations = previousCombinations * array.length;
})
console.log(outputArray)
What is the simplest way to get 50 random unique elements from an array of 1000 elements ?
text = new Array();
for(i=0;i<1000;i++){ text[i]=i; } //array populated
// now I need to get 50 random unique elements from this array.
The obvious (to me) way is to shuffle the array, then take the first fifty elements. This question has a good way to shuffle an array, and you can then slice the first fifty elements. This guarantees the elements will be unique.
So, using the function there:
fisherYates(text);
text = text.slice(0, 50);
Good algorithms explained in this topic (in C but you can easily to do same in JS)
Look into the Fisher-Yates algorithm, I think this will work for you.
This assumes you mean random indexes and not indexes with unique values.
One way is to copy the array and prune off the ones you use:
function getRandomIndexes( arr, cnt){
var randomArr = [],
arrCopy = arr.slice(),
i,
randomNum ;
for (i=0;i<arrCopy.length;i++) {
randomNum = Math.floor( arrCopy.length * Math.random());
randomArr = randomArr.concat( arrCopy.splice(randomNum ,1) );
}
return randomArr;
}
var myNums = [], i, randSet;
for (i=0;i<10;i++){
myNums.push(i);
}
randSet = getRandomIndexes(myNums, 5);
Another way is to keep track of the indexes you use and keep looking until you find one you did not use. I find the while loop to be scary, and personally would not use this solution if random indexes needed approaches close to the array length.
function getRandomIndexes( arr, cnt){
var randomArr = [],
usedNums = {},
x;
while (randomArr.length<cnt) {
while (usedNums[x]===true || x===undefined) {
x = Math.floor( Math.random() * arr.length);
}
usedNums[x] = true;
randomArr.push( arr[x] );
}
return randomArr;
}
var myNums = [], i, randSet;
for (i=0;i<10;i++){
myNums.push(i);
}
randSet = getRandomIndexes(myNums, 5);
In case you meant unique values:
Demo
var old_arr = [0,1,2,3,4,5,6,7,8,9], new_array = [];
for (var i = 0; i < 5; i++) {
var rand_elem = old_arr[Math.floor(Math.random() * old_arr.length)];
if (arrIndex(old_arr[rand_elem], new_array) == -1) {
new_array.push(rand_elem);
} else {
i--;
}
}
function arrIndex(to_find, arr) {//own function for IE support
if (Array.prototype.indexOf) {
return arr.indexOf(to_find);
}
for (var i = 0, len = arr.length; i < len; i++) {
if (i in arr && arr[i] === to_find) {
return i;
}
}
return -1;
}
In case you meant unique indexs:
Generate random indexes and store the indexes in an array and make checks to prevent duplicates
Start removing the elements of the array after you get them, (you might have problems if you cache the length, so don't)
var arr = [];
while(arr.length < 51){
var ind = Math.floor(Math.random()*1000);
if(!(ind in arr))
arr.push(ind)
}
You'll have 50 random unique numbers in the array arr, which you could use as index
EDIT:
As #ajax333221 mentioned, the previous code doesn't do to get unique elements from the array, in case it contains duplicates. So this is the fix:
var result_arr = [];
while(result_arr.length < 51){
var ind = Math.floor(Math.random()*1000);
if(text[ind] && !(text[ind] in result_arr))
result_arr.push(text[ind]);
}
Being 'text' the array populated with 1000 values
Math.random() * 1000;
Generate 50 random numbers and use them as the position in the array.
Okay, just learning JavaScript here, I know I should search for the answer, but I can't even figure out what I should search for, im completely lost as to why this is happening
// string or array
var strr=[1,5,3];
//changed array.
var cha=[];
var t=0;
/*push strr into the cha array
should look like
[
[1,5,3],
[1,5,3],
[1,5,3]
]
*/
for(var i=0;i<3;i++){
cha.push(strr);
}
//shuffle each element one by one
for(a in cha){
cha[a]=cha[a].sort(function()
{return Math.floor(Math.random()*100)-50;
});
}
//each element should be a re arranged array with the same elemnts of strr
// Like 135, 351, 153 for example
console.log(cha);
// But it arranges all of the elements the same. the shuffle changed the order of strr, but not each of cha...
// Like 351,351,351 for example, they are randomized, but all the same.
You are effectively pushing the same array thrice.
push a hollow copy of it (since it is an array of primitives)
cha.push( strr.slice() );
Demo
var strr = [1, 5, 3];
var cha = [];
var t = 0;
for (var i = 0; i < 3; i++) {
cha.push(strr.slice());
}
for (a in cha) {
cha[a] = cha[a].sort(function() {
return Math.floor(Math.random() * 100) - 50;
});
}
console.log(cha);
I'd like to convert:
var people = [1,"Shaw","Tanzania",2,"Nelson","Kazakhstan",3,"Garcia","Madagascar"]
into:
var rows = [
[1, "Shaw", "Tanzania"],
[2, "Nelson", "Kazakhstan"],
[3, "Garcia", "Madagascar"]
];
I've seen this answer to a similar question, but I don't understand how that works and extend it to every nth element
Use a for loop with Array#slice. You iterate the original array using the require chunk size as the step. On each iteration you slice the relevant part from the original array (slice doesn't mutate the array), and push it into the result array.
var people = [1,"Shaw","Tanzania",2,"Nelson","Kazakhstan",3,"Garcia","Madagascar"];
var result = [];
var chunkSize = 3;
for(var i = 0; i < people.length; i+= chunkSize) {
result.push(people.slice(i, i + chunkSize));
}
console.log(result);