How to randomly fill the rows of the matrix? - javascript

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))

Related

randomize items in an array and keep original order

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

Paired values in array

Hi I am trying to create an array with paired values from 1-size in random order example : [3,1,1,2,3,2] for size = 3
So far I've done sth like this :
I fill up both arrays with random numbers when the number isn't already in array
Repeat for second array
And then return concatenation of them
I wonder how I can improve the solution of my problem
let arr1 = [];
let arr2 = [];
let number;
let i = 0;
let k = 0;
while (i < size) {
number = Math.floor(Math.random() * size + 1);
if (!arr1.includes(number)) {
arr1.push(number);
i++;
}
}
while (k < size) {
number = Math.floor(Math.random() * size + 1);
if (!arr2.includes(number)) {
arr2.push(number);
k++;
}
}
return arr1.concat(arr2);
your way requires too much unnecessary operations. You depend on how many time random values will repeat till cover all numbers. Better just create one array, and find random index, then remove chosen item, moving it to result array. So no repeatings, very effective.
And also method includes is not effective for big arrays. My solution is
const createRandomArrays = size => {
const res = Array.from([1, 2]).flatMap(() => {
const numbers = Array.from({ length: size }, (v, ind) => ind + 1)
const res = []
while (numbers.length) {
const ind = Math.floor(Math.random() * numbers.length)
res.push(numbers[ind])
numbers.splice(ind, 1)
}
return res
})
console.log(res)
}
PS But you can not get [3,1,1,2,3,2] from your code as each array has its own set, you can get [3,2,1,1,3,2] or something, two digits 1 can not be together in first half of result array.
If you want them mixed than solution is
const createRandomArrays = size => {
const numbers = Array.from({ length: size * 2 }, (v, ind) => (ind % size) + 1)
const res = []
while (numbers.length) {
const ind = Math.floor(Math.random() * numbers.length)
res.push(numbers[ind])
numbers.splice(ind, 1)
}
console.log(res)
}
I think you are on the right track. But the solution can be simplified quite a bit with improved performance.
If you think about it, you really only need two operations. First lets pretend your array is a pack of cards. What you are really doing is just shuffling two packs of cards together.
Lets pretend you are given an array A containing all elements up to N say [1,2,3]. We can just concat A with A to get A' = [1,2,3,1,2,3]. Now to complete our card shuffling example, we just need to shuffle everything. Say we have a function S that shuffles an array, applying it to our array A' to get our random permutation [2,1,2,3,1,3] or whatever.
const pairs = (n) => {
return Array.from({ length: n * 2 }, (_, i) => {
return (i + 1) % n + 1;
});
};
const shuffle = (array) => {
const n = array.length;
const result = array.slice();
for (let i = 0; i < array.length - 1; i++) {
const j = Math.floor(Math.random() * (n - i) + i);
const t = result[i];
result[i] = result[j];
result[j] = t;
}
return result;
};
console.log(shuffle(pairs(3)));
pairs() will generate an array of 2 * n length containing the range [1, n] twice. shuffle performs a fisher-yates shuffle over the array returning your results.

Concise way to return a new array of N elements filled with iterated values from another array? Vanilla JavaScript

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);

Find averages in an array of different numbers

I feel like I didn't phrase my title very well, can someone please correct it if you understand my question.
I have an array of
arr = [1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3
2,3,4,5,6
]
And I want to do several things
Split it into chunks with the size of 5
Calculate the number of chunks. In this case, it should be 6 chunks.
Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks. In this case,
(1+6+3+4+5+2)/6, (2+7+4+6+6+3)/6, ..., (5+0+1+3+3+6)/6
Return results as an array
var result = [3.5, 4.66, ..., 3]
I have got the idea, but not sure how to implement it.
Thanks
I believe this code accomplishes what you want.
function averageValues (arr) {
var chunks = Math.ceil(arr.length / 5); // find the number of chunks
var sums = [0, 0, 0, 0, 0]; // keep a running tally
for (var i = 0; i < arr.length; i ++) {
sums[i % 5] += arr[i]; // add each element to the proper part of the sum
}
for (var i = 0; i < sums.length; i ++) {
sums[i] /= chunks; // divide each part of the sum by the number of chunks
}
return sums;
}
You can solve this by maintaining five separate sums to end with five separate averages.
Prepare your sums array of length 5:
var sums = [ 0, 0, 0, 0, 0 ];
For each number in your set, increment the corresponding sum by that number.
for (var x = 0; x < arr.length; x++)
sums[x % 5] += arr[x];
Divide each sum by how many numbers were used:
var numbers = arr.length / 5; // 6 numbers each
var result = sums.map(
function(s) {
return s / numbers; // divide each sum by 6
}
);
This uses the assumption that your set length is always a multiple of 5.
Here is a more functional approach to your problem. This uses the assumption that your set length is always a multiple of 5.
// add extra array helpers
Array.prototype.eachSlice = function (n, fn) {
let slices = [];
for (let i = 0; i < this.length; i += n) {
let slice = this.slice(i, i + n);
slices.push(slice);
}
if (fn) slices.forEach(fn);
return slices;
}
Array.prototype.sum = function (fn) {
let fnReduce = fn ? (acc, ...args) => acc + fn(...args) : (acc, v) => acc + v;
return this.reduce(fnReduce, 0);
}
Array.prototype.avg = function (fn) {
return this.sum(fn) / this.length;
}
// actual solution
let arr = [
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6,
];
let chunkSize = 5;
console.log('--- question #1 ---');
console.log('Split it into chunks with the size of 5.');
console.log('-------------------');
let chunks = arr.eachSlice(chunkSize);
console.log(chunks);
console.log('--- question #2 ---');
console.log('Calculate the number of chunks. In this case, it should be 6 chunks.');
console.log('-------------------');
console.log(chunks.length);
console.log('--- question #3 ---');
console.log('Calculate the sum of numbers of all chunks in each position and divide it by the total number of chunks.');
console.log('-------------------');
let avgChunks = new Array(chunkSize).fill()
.map((_, i) => chunks.avg(chunk => chunk[i]));
console.log('See the result under question #4.');
console.log('--- question #4 ---');
console.log('Return results as an array.');
console.log('-------------------');
console.log(avgChunks);
It could be useful:
//The average method using an array
function average(arr) {
var sum = arr.reduce(function (a,b) { return a + b; },0)
return sum/ arr.length
}
//Chunk array method, it returns an array of the sliced arrays by size
function chunkArray(arr, chunkSize){
var numChunks = arr.length / chunkSize
var chunks= []
for (let index = 0; index < numChunks; index++) {
chunks.push(arr.slice(index * chunkSize, (index * chunkSize) + chunkSize))
}
return chunks
}
//Finally, the average of arrays, it returns the average of each array
function averageArrays(arrs){
return arrs.map(function (arr) {
return average(arr)
})
}
//Example of usage
var chunks = chunkArray([
1,2,3,4,5,
6,7,8,9,0,
3,4,7,2,1,
4,6,1,2,3,
5,6,8,9,3,
2,3,4,5,6
],5)
console.log(averageArrays(chunks))
I think #Aplet123 has the most straight forward and easy to understand approach, though I changed up a little bit to suit my needs.
var chunks = Math.ceil(arr.length / 5) // Find the number of chunks
var sums = new Array(5).fill(0) // Keeps a running tally and fill values 0
arr.map((x, i) => sums[i%5] += arr[i]) // add each element to the proper part of the sum
var avgs = sums.map((x) => x/chunks /divide each part of the sum by the number of chunks

Distributing array elements randomly to new arrays

I have an array of numbers from 1 to 60
var originalArray = [1, 2, 3, 4 .... 58, 59, 60] // etc
I want to - depending on another number between 2 and 4 - split those numbers randomly into the number of arrays specified, and for the result to be unique each and every time.
For example:
distributeArray(2) should result in two arrays, each with 30 numbers randomly selected from the original array.
distributeArray(3) should result in three arrays, each with 20 numbers randomly selected from original array.
I assume this is a reasonably common case so any pointers would be appreciated. Thanks in advance.
You could do something like this, first shuffle and then split array into n parts.
var arr = [...Array(61).keys()].slice(1)
function splitRandom(data, n) {
var seen = [];
var counter = 0;
var shuffle = data.reduce(function(r, e) {
function random() {
var rand = parseInt(Math.random(0, arr.length) * arr.length);
if (seen.indexOf(rand) != -1) {
return random()
} else {
seen.push(rand)
return rand;
}
}
r[random()] = e;
return r;
}, [])
var split = shuffle.reduce(function(r, e) {
var c = counter++;
r[c] = r[c].concat(e)
counter = counter % n;
return r;
}, Array(n).fill([]))
return split;
}
console.log(JSON.stringify(splitRandom(arr, 3)))
console.log(JSON.stringify(splitRandom(arr, 10)))
console.log(JSON.stringify(splitRandom(arr, 50)))
You can create a function which creates an array of n .length, and an array of x .length. Use do..while loop Array.prototype.splice() to remove a random index from originalArray, .push() the element to one of x random arrays, until originalArray.length evaluates to false, return array of arrays containing values.
const randomArrays = (n, x) => {
let [originalArray, result, len] = [
Array.from({length: n}, (_, key) => key)
, Array.from({length: x}, () => [])
, Math.ceil(n / x)
];
do {
let [curr, index] = [
originalArray
.splice(Math.floor(Math.random() * originalArray.length), 1)
.pop()
, Math.floor(Math.random() * result.length)
];
if (result[index].length < len)
result[index].push(curr);
else
for (let i = 0; i < result.length; i++) {
if (result[i].length < len) {
result[i].push(curr);
break;
}
}
} while (originalArray.length);
return result
}
console.log(
randomArrays(60, 3)
, randomArrays(21, 7)
, randomArrays(5, 3)
, randomArrays(27, 5)
);

Categories

Resources