I would like to randomize the email addresses that are being output and remove duplicates and have them retain the original order. This works perfectly fine when I do not randomize. I generate the emails, remove dups, and output and have no issues. I also have no issues randomizing. The issue I seem to have is combining the two. Being able to generate the array, randomize, remove dups AND retain the original order. Below is what I have tried already, this is the closest I have gotten. Thanks for any help.
function randomize(arr) {
var i, j, tmp;
for (i = arr.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
}
const sourceArray = [];
var arr = sourceArray;
// we start with an empty source array
// const sourceArray = [];
// the number of emails / 2
const numberOfEmails = 100000;
// first pass we add 100,000 emails
for (let index = 0; index < numberOfEmails; index++) {
sourceArray.push(`test${index}#google.com`);
}
// second pass we create dupes for all of them
for (let index = 0; index < numberOfEmails; index++) {
sourceArray.push(`test${index}#google.com`);
}
// throw in some extra dupes for fun
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
sourceArray.push(`test0#google.com`);
// this serves as a map of all email addresses that we want to keep
const map = {};
// an exact time before we run the algorithm
const before = Date.now();
// checks if the email is in the hash map
const isInHashmap = (email: string) => {
return map[email];
};
// iterate through all emails, check if they are in the hashmap already, if they are we ignore them, if not we add them.
sourceArray.forEach((email) => {
if (!isInHashmap(email)) {
map[email] = true;
}
});
// we fetch all keys from the hashmap
const result = Object.keys(map);
arr = randomize(arr);
console.log(`Randomized here: ${sourceArray}`);
console.log(`The count after deduplicating: ${result.length}`);
// gets the time expired between starting and completing deduping
const time = Date.now() - before;
console.log(`The time taken: ${time}ms`);
console.log(result);
If I understand correctly, to get your random array of emails I would do the following:
const arrayOfEmails = [];
for (let i = 0; i < 100000; i++) {
const randomInt = Math.floor(Math.random() * 100000); // random number between 0 and 999,999
arrayOfEmails.push(`test${randomInt}#google.com`);
}
Then hopefully this helps as far as removing the dupes and keeping the order.
You could do
const array = [2,7,5,9,2,9,5,3,2,9]; // your random array
const set = new Set(array); // {2,7,5,9,3} javascript sets need unique members
const newArray = Array.from(set); // [2,7,5,9,3]
That's the easiest way I can think of.
If you didn't want to remove duplicates in a second step then you could also just write this:
const setOfEmails = new Set();
for (let i = 0; i < 100000; i++) {
const randomInt = Math.floor(Math.random() * 100000); // random number between 0 and 999,999
setOfEmails.add(`test${randomInt}#google.com`); // will only add if the email is unique
}
const arrayOfEmails = Array.from(setOfEmails); // this array will be unique emails
Related
Could someone describe to me what the code does step by step?
and what doing this? const { length } = data;
getQuestions: async (req, res) => {
const data = await Question.find();
const { length } = data;
const ids = [];
for (let i = 0; i < length; i++) {
ids.push(i)
}
const idsTrimmed = ids.sort(() => Math.random() - 0.5).slice(0, 2)
const ans = []
idsTrimmed.forEach(id => {
ans.push(data[id])
})
res.send(ans)
},
This depends on what is the schema of the Question collection. The length must be one of the property of the collection and by doing cost {length} = data you are extracting that value of length property. This is called as Object De-structuring .
getQuestions: async (req, res) => {
// Calling Question.Find function to get some data back
const data = await Question.find();
// Extract the length prop/object from the data
const { length } = data;
// Setup the ids based on the length of the data
const ids = [];
for (let i = 0; i < length; i++) {
ids.push(i)
}
// Shuffle the ids and take first 2
const idsTrimmed = ids.sort(() => Math.random() - 0.5).slice(0, 2)
//build up the ans array and return to the getQuestion function caller.
const ans = []
idsTrimmed.forEach(id => {
ans.push(data[id])
})
res.send(ans)
const data = await Question.find();
We started by retrieving some questions and storing them in a variable called data.
const { length } = data;
This is called destructuring syntax. Basically this is shorthand for const length = data.length.
const ids = [];
for (let i = 0; i < length; i++) {
ids.push(i)
}
This is creating an array called ids which contains the numbers 0 through length - 1 which corresponds to all the valid indexes in data.
const idsTrimmed = ids.sort(() => Math.random() - 0.5).slice(0, 2)
This is shuffling and truncating the ids array.
The array sort method takes a function as an argument which allows for a custom sorting order. That function is passed a pair of elements from the array and should return a negative number if the first argument comes before the second in the sorted order, a positive number if the second argument comes before the first, or 0 if the two arguments are equal. Math.random returns a random number between 0 and 1, so by subtracting 0.5, we get negative numbers and positive numbers roughly 50% of the time each. Since each element is put before or after another at random, we're effectively shuffling the array.
The slice method copies and truncates the array to the items after and including index 0 but before index 2. Essentially, it's creating a new array with just the first 2 elements from the shuffled array.
const ans = []
idsTrimmed.forEach(id => {
ans.push(data[id])
})
And here we take our 2 random indexes from the previous step and grab the elements from data with those indexes. Since the indexes were shuffled, we know these are two random items.
I divide the code to some blocks and add comment for each block.
getQuestions: async (req, res) => {
// query the database to get the list of questions
const data = await Question.find();
// create an array that contains the list of question index in the array
const { length } = data;
const ids = [];
for (let i = 0; i < length; i++) {
ids.push(i)
}
// shuffle the index array, then select 2 first elements
const idsTrimmed = ids.sort(() => Math.random() - 0.5).slice(0, 2)
// get back the question using index
const ans = []
idsTrimmed.forEach(id => {
ans.push(data[id])
})
// send to client
res.send(ans)
(Not directly related to your question, but the code could be improved for clarity, below is my suggestion)
getQuestionsRandomly : async (req, res) {
const NB_QUESTIONS_SELECTED = 2;
const allQuestions = await Question.find();
const questionIndexes = allQuestions.map((item, index) => index);
const randomIndexes = questionIndexes.sort(() => Math.random() - 0.5)
.slice(0, NB_QUESTIONS_SELECTED);
const randomQuestions = randomIndexes.map(item => allQuestions[item]);
res.send(randomQuestions);
}
I have this array of strings.
const numbersArray = ['1000','10000','100000']
My goal is to split each one of them on specific index for example: output of 1000 should be 1,000 and etc...
Here is what i have right now:
const splitArrayHandler = (arr) =>{
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
return splitAtIndex(arr[i],indexOfSymbol)
}
}
const splitAtIndex = (value,index) => {
return value.substring(0,index) + ',' + value.substring(index)
}
splitArrayHandler(numbersArray)
The first function splitArrayHandler loops through my array,finds specific index of the symbol in the string and then function splitAtIndex does the rest of the hard work.
The problem is only first element of the string is passing to the splitAtIndexfunction and I dont understand why. any suggestions please?
const numbersArray = ['1000','10000','100000']
const splitArrayHandler = (arr) =>{
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
return splitAtIndex(arr[i],indexOfSymbol)
}
}
const splitAtIndex = (value,index) => {
return value.substring(0,index) + ',' + value.substring(index)
}
splitArrayHandler(numbersArray)
Use Intl.NumberFormat for the job. No need for string parsing / manipulating:
const numbersArray = ['1000', '10000', '100000', '654654686156', '1000.66', '10e14', '0xFFFF'];
const format = new Intl.NumberFormat('en-US').format;
const formattedNumbers = numbersArray.map(Number).map(format);
console.log(formattedNumbers);
You are breaking the loop by returning the splitAtIndex function. Create another array and push the results to it.
const splitArrayHandler = (arr) =>{
let arr2 = []
for (let i = 0; i < arr.length; i++) {
let indexOfSymbol = Math.round(arr[i].length / 3)
arr2.push(splitAtIndex(arr[i],indexOfSymbol))
}
return arr2
}
You might use regular expression and map function (though there is no real difference between map and hard coded loop)
const numbersArray = ['1000','10000','100000']
function addComa(x) {
return x.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
const resolved = numbersArray.map(addComma)
console.log(resolved) // ['1,000','10,000','100,000']
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)
I have a given array with an undetermined quantity of elements, the array can be numbers or strings, then I need to generate a new array of N elements made from the iterated elements of the first array
I already have a function to do it, but it only works if the original array are consecutive numbers, it doesn't work with strings. I have a gazillion of ideas on how to achieve it. I could just concatenate the array to a new one until its equal or greater than the required quantity of elements, and then set the new array length to the required quantity, but is there a more concise and elegant way to do it?
IDEA 01 codepen
function populateArray(qty) {
// Array to populate from
let array = [1,2,3];
//Determine the Range Length of the array and assign it to a variable
let min = array[0];
let max = array[array.length - 1];
const rangeLength = (max - min + 1);
//Initialize uniqueArray which will contain the concatenated array
let uniqueArray = [];
//Test if quantity is greater than the range length and if it is,
//concatenate the array to itself until the new array has equal number of elements or greater
if (qty > rangeLength) {
//Create an array from the expansion of the range
let rangeExpanded = Array.from(new Array(rangeLength), (x,i) => i + min);
while (uniqueArray.length < qty) {
uniqueArray = uniqueArray.concat(rangeExpanded);
}
}
// Remove additional elements
uniqueArray.length = qty
return uniqueArray;
}
console.log(populateArray(13))
IDEA 02 codepen, but it fills the new array 13 times with the whole original array, not iterated items
// FILL A NEW ARRAY WITH N ELEMENTS FROM ANOTHER ARRAY
let array = [1,2,3];
let length = 13;
let result = Array.from( { length }, () => array );
console.log(result);
the expected result is [1,2,3,1,2,3,1,2,3,1,2,3,1] if the original array were made of strings the expected result would be [dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog]
You can tweak your second idea a bit - calculate the number of times you need to repeat the initial array to come up with the required number of total items, then flatten it and .slice:
let array = [1,2,3];
let length = 13;
const fromLength = Math.ceil(length / array.length);
let result = Array.from( { length: fromLength }, () => array )
.flat()
.slice(0, length);
console.log(result);
I'll go with #CertainPerformance's answer. But here's a different approach, just for thinking-out-of-the-box purposes
// A function for getting an index up to length's size
function getIDX(idx, length){
return idx <= length ? idx : getIDX(idx-length, length);
}
const newArrayLength = 13;
const sourceArray = [1,2,3];
const resultArray = [];
for(let i=0; i< newArrayLength; i++){
resultArray[i]=sourceArray[getIDX(i+1, sourceArray.length)-1];
}
EDIT 1:
I was comparing the performance of this approach versus the others here described and it seems that if you wanted to create a very large new array (ex: newArrayLength= 10000) the getIDX() function takes a lot to finish because of the size of the call stack. So I've improved the getIDX() function by removing the recursion and now the complexity is O(1) check it out:
function getIDX(idx, length){
if (length === 1) {return idx};
const magicNumber = length * (Math.ceil(idx/length)-1);
return idx - magicNumber;
}
With the new getIDX() function this approach seems to be the most performant.
You can take a look to the tests here:
https://jsbench.me/v7k4sjrsuw/1
You can use a generator function that will create a repeating sequence from an input. You can add a limit to the generated sequence and simply turn it into an array:
function* repeatingSequence(arr, limit) {
for(let i = 0; i < limit; i++) {
const index = i % arr.length;
yield arr[index];
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"], 10);
const result = Array.from(generator);
console.log(result);
Alternatively, you can make an infinite repeating sequence with no limit and then generate as many elements as you want for an array:
function* repeatingSequence(arr) {
let i = 0
while(true) {
const index = i % arr.length;
yield arr[index];
i++;
}
}
const generator = repeatingSequence(["dog", "cat", "sheep"]);
const result = Array.from({length: 10}, () => generator.next().value);
console.log(result);
You can use modulo operator. Special thanks to #Vlaz for shorten version:
Array.from({ length:length }, (e, i) => array[ i % array.length ])
An example:
let array = [1,2,3];
let length = 13;
const result = Array.from({ length:length },
(e, i) => array[ i % array.length ]);
console.log(result);
I have a matrix with n-rows and n-columns. I need to make sure that the numbers in each row are unique.
let matrix = [];
let matrixRows = 3;
let matrixColumns = 5;
for ( let i = 0; i < matrixRows; i++ ) {
matrix[ i ] = [];
let j = 0;
while (j < matrixColumns) {
matrix[ i ][ j ] = Math.floor(Math.random() * 5) + 1;
j++;
}
}
console.log( matrix.join('\n') );
It should look something like this
"1,2,3,4,5 \\ here is line break (new row)
1,4,2,5,3 \\ here is line break (new row)
5,4,2,3,1"
You can do that in following steps:
First create a function which takes two parameters rows and cols
Then create a helper function shuffleArray which takes an array as argument and return a new array which is shuffled.
In the main function create an array of number for the no of cols. In the case it will be [1,2,3,4,5]. You can do that using map()
Then create an array of undefined of length equal to the given rows.
Use map() on that and return a new shuffled array that we created before([1,2,3,4,5])
function shuffleArray(arr){
//create a copy of the array
arr = arr.slice();
//create an array on which random items from 'arr' will be added
let res = [];
//create while loop which will run until all the elements from arr are removed
while(arr.length){
//generate a random index in range of length of 'arr'
let i = Math.floor(arr.length * Math.random())
//push element at that index to result array
res.push(arr[i]);
//remove that element from the orignal array i.e 'arr'
arr.splice(i,1);
}
return res;
}
function randMatrix(rows,cols){
//create an array which will shuffled again and again.
let genArr = [...Array(cols)].map((x,i) => i + 1);
return [...Array(rows)] // create an array of undefined of length equal to rows
.map(x => shuffleArray(genArr)) // change that each to another shuffled array.
}
console.log(randMatrix(3,5).join('\n'))
You could create an array of numbers upto matrixColumns using Array.from(). Then shuffle the array randomly in every iteration and create rows (from this answer)
// https://stackoverflow.com/a/18806417/3082296
function shuffle(arr) {
let i = arr.length,
copy = [...arr], // take a copy
output = [];
while (i--) {
const j = Math.floor(Math.random() * (i + 1));
output.push(copy.splice(j, 1)[0]);
}
return output
}
let matrix = [];
let matrixRows = 3;
let matrixColumns = 5;
// Natural numbers upto matrixColumns
const numbers = Array.from({ length: matrixColumns }, (_, i) => ++i)
const output = Array.from({ length: matrixRows }, _ => shuffle(numbers))
console.log(output)
Not the most elegant, but this first creates a flat list of unique randoms and reduces that to a 2d n*m matrix:
function fillRand (n, m) {
let rands = [];
do {
var rand = Math.random ();
} while (!~rands.indexOf (rand) && rands.push (rand) < n*m);
return rands.reduce ((mat, cur, i) => {
let last;
if (i%n==0) {
mat.push ([]);
}
last = mat[mat.length - 1]
last.push (cur);
return mat;
},[])
}
console.log (fillRand (4,5))