forEach loop assigns the same random number - javascript

In typescript, I am trying to assign a random number to a property of each object in an array. I have tried the following code
uniqueItems.forEach(unique => {
unique.humanCode = Math.floor(1000 + Math.random() * 9000).toString();
});
If I console.log inside the forEach loop, I get a different number but when I console.log the array of objects after the forEach, all of the random numbers are the same.
Edit: I had originally created my array of objects by using
for (let i = 0; i < this.quantity; i++) {
this.uniqueItems.push(uniqueItem);
}
This made an array of the same object. This meant that my array was being assigned the last random number. I fixed this by using the spread operator when I created my array.
for (let i = 0; i < this.quantity; i++) {
this.uniqueItems.push({ ...this.uniqueItem });
}

You can use map to iterate over your items and return a new object for each item in the array. This will also ensure you are no updating the object by reference. You should read about mutability in JavaScript. It's an important concept in JS
uniqueItems.map(unique => ({
...unique,
humanCode: Math.floor(1000 + Math.random() * 9000).toString(),
}));

I have tried the below code and it works for me.
let uniqueItems = [{
humanCode: 0,
},
{
humanCode: 0,
},
{
humanCode: 0,
},
]
uniqueItems.forEach(unique => {
unique.humanCode = Math.floor(1000 + Math.random() * 9000).toString();
});
console.log(uniqueItems);

Related

Create a functions, which creates a new array out of an existing one, which has less or equal amount of elements and no repeats

I have an array like this:
var arr = [0,1,2,3,4,5]
What I need is to write a function, which will generate a new array of random length (but less or equal than arr.length) and won't generate repeated numbers. Something like:
var arrNew = [2,4,0]
but not
var arrNew = [2,4,2,2,0]
I came up with this, but it generates repeated elements:
var list = [0,1,2,3,4,5];
var getRandomNumberRange = function (min, max) {
return Math.floor(Math.random() * (max - min) + min);
};
var getRandomArr = function (list) {
var arr = new Array(getRandomNumberRange(1, 6));
for (var i = 0; i < arr.length; i++) {
arr[i] = list[getRandomNumber(list.length)];
}
return arr;
};
Appreciate your help!
NOTE: there's nothing stopping this code from giving you an empty array as your random array.
var arr = [0,1,2,3,4,5, 36, 15, 25]
function generateRandomArrayFromSeed(arr) {
// Create a new set to keep track of what values we've used
const used = new Set();
// generate a new array.
const newArr = [];
// what random length will we be using today?
const newLength = getRandomNumber(arr.length);
// while our new array is still less than our newly randomized length, we run this block of code
while(newArr.length < newLength) {
// first, we generate a random index
const randomNumber = getRandomNumber(arr.length);
// then we check if the value at this index is in our set; if it is, we continue to the next iteration of the loop, and none of the remaining code is ran.
if(used.has(arr[randomNumber])) continue;
// otherwise, push this new value to our array
newArr.push(arr[randomNumber]);
// and update our used set by adding our random number
used.add(arr[randomNumber]);
}
// finally return this new random array.
return newArr;
}
function getRandomNumber(limit = 10) {
return Math.floor(Math.random() * limit);
}
console.log(generateRandomArrayFromSeed(arr));
Moving the data vrom an array datastructure to a Set could solve the problem.
Sets dont accept duplicate values.
For more on Set in javascript, see MDN here
To convert the Set back to an array, use Array.from()
To avoid duplicates in you final array just use Set and convert it back to an array using Array.from
Save you result inside set as below:
arr = Array.from(new Set(<result array>));

Iterating over every pair of elements in a Map

I have a Map which holds Shape object values with their id as key.
I need to iterate over every pair of Shapes in this Map, but I want to iterate over each pair only once.
I know I can use forEach or for..of, but I can't find a way to prevent duplicate pairs. Also, this should be a as efficient as possible.
shapes.forEach((shape1, shapeId1) => {
shapes.forEach((shape2, shapeId2) => {
// iterating over each pair many times
});
});
I'd suggest first converting the Map to an array of its entries:
const entryArray = Array.from(shapes.entries());
Then you can choose to iterate pairs either via traditional for loop:
console.log("FOR LOOP");
for (let i = 0; i < entryArray.length; i++) {
const [shapeId1, shape1] = entryArray[i];
for (let j = i + 1; j < entryArray.length; j++) {
const [shapeId2, shape2] = entryArray[j];
console.log(shapeId1, shapeId2);
}
}
Or via the functional forEach array methods:
console.log("FOREACH");
entryArray.forEach(([shapeId1, shape1], i) =>
entryArray.slice(i + 1).forEach(([shapeId2, shape2]) => {
console.log(shapeId1, shapeId2);
})
);
In each case you are avoiding duplicates by the inner loop only iterating the elements after the outer loop index. I don't know what your Shape or id types look like, but given this:
interface Shape {
area: number;
}
const shapes: Map<string, Shape> = new Map([
["a", { area: 1 }],
["b", { area: 2 }],
["c", { area: 3 }]
]);
The above code outputs
FOR LOOP
a b
a c
b c
FOREACH
a b
a c
b c
So you can see that you get distinct pairs. Hope that helps; good luck!
Link to code
You can use two for iterations using an index and start the nested iteration from the root index + 1. This will ensure you that you will never process two pairs.
const arr = [1,2,3,4];
for (let i = 0; i<arr.length-1; i++) {
for (let j = i+1; j<arr.length; j++) {
console.log(`${arr[i]} - ${arr[j]}`)
}
}
Use a Set with the two indexes:
let indexes = new Set();
shapes.forEach((shape1, shapeId1) => {
shapes.forEach((shape2, shapeId2) => {
if (set.has(`${shapeId1}-${shapeId2}`) || set.has(`${shapeId2}-${shapeId1}`)) return;
set.add(`${shapeId1}-${shapeId2}`);
});
});

Array created with random numbers as index and objects as elements gives wrong result

I have this small script, rolling random numbers and push them into an array. If an item (an object) doesn't exist it must be added, else it needs to be updated. The id's must be an unique identifier. Somehow I get duplicate id's and I am not sure why that happens.
I was hoping to get this after the rolls (30 rolls example):
[{"id":1,"min":0,"max":18},{"id":2,"min":0,"max":2},{"id":3,"min":0,"max":10}]
instead of:
[{"id":1,"min":0,"max":7},{"id":2,"min":0,"max":2},{"id":3,"min":0,"max":10},{"id":1,"min":0,"max":11}]
Does anyone know's what the problem might be? Thanks in advance.
var coll_ = [];
for (i = 0; i < 30; i++) {
random_number = Math.floor((Math.random() * 3) + 1);
if (!coll_[random_number]) {
// Item doesnt exist so will be added to the array
coll_.push({
id: random_number,
min: 0,
max: 1
});
} else {
// Item exists so it will be updated
coll_[random_number]['max']++;
}
}
document.write(JSON.stringify(coll_));
coll is an array - checking coll_[random_number] will only check whether some index exists, which doesn't necessarily have anything to do with the id. Instead of using an array, use an object indexed by ids, and then get that object's values after you're done iterating:
const obj = {};
for (let i = 0; i < 30; i++)
{
const rand = Math.floor((Math.random() * 3) + 1);
if ( !obj[rand] ) {
// Item doesnt exist so will be added to the array
obj[rand] = {id: rand, min: 0, max: 1};
} else {
// Item exists so it will be updated
obj[rand].max++;
}
}
console.log(Object.values(obj));
Also try to avoid implicitly creating global variables - when using a new variable name, always declare it with const (or let or var) first.
The following code (modified) gives the expected result:
var coll_ = [];
for (let i = 0; i < 10; i++) {
random_number = Math.floor((Math.random() * 3) + 1);
// Item doesnt exist so will be added to the array
if (!coll_[random_number]) {
coll_[random_number] = {
id: random_number,
min: 0,
max: 1
};
} else {
// Item exists so it will be updated
++coll_[random_number].max;
}
}
coll_.shift(); // See note below
console.log(coll_.toSource());
document.write(JSON.stringify(coll_));
The Output:
Console: [{id:1, min:0, max:4}, {id:2, min:0, max:1}, {id:3, min:0, max:5}]
Browser: [{"id":1,"min":0,"max":4},{"id":2,"min":0,"max":1},{"id":3,"min":0,"max":5}]
Note about coll_.shift();:
This statement removes the first element of the coll_ array - which is coll_[0] and its value is undefined. This happens because the array has elements with indexes 1, 2, and 3. The element at zero index is created by the JS.

Checking for matching values in two arrays using for loop, is it faster to iterate through the smaller loop?

I am creating a function that will accept two parameters, which are each arrays, and return a separate array of matching values between the two arrays.
I made two versions of the function,
Defaults to iterate through the first parameter in a for
loop, and checks the second parameter for a matching value using .includes()
ex:
var matches = [];
for (let i = 0, len = array1.length; i < len; i++) {
var a = array1[i];
if (array2.includes(a)) {
matches.push(a)
}
Measures and compares the lengths of the two arrays, and chooses
the shorter array to iterate through in the for loop
ex:
if (array1.length <= array2.length) {
var itArr = array1;
var checkArr = array2 }
else { var itArr = array2
var checkArr = array1 };
var matches = [];
for (let i = 0, len = itArr.length; i < len; i++) {
var a = itArr[i];
if (checkArr.includes(a)) {
matches.push(a)
}
My question is whether or not this actually improves the performance, or makes no difference, or harms the performance (by adding more variable definitions, calculations, etc.)?
It wouldn't make significant difference since the worst case complexity would be O(n*m) where n and m are the length of the arrays.
You can sort the 2 arrays and find the intersection using 2 pointers, The time complexity in that case would be O(nlogn + mlogm + n + m) subject to the sorting algorithm used
I think I would go for the first approach, due to the .includes function will try to iterate the array and return true when it finds the element, so if the element is in the end of the array it iterates completely on it. Hence your attempt for chosing the small one shouldn't make too much difference.
Here is an example of using a Set for seeing if one array contains values from the other.
If your not ES6, (and why not?), you could use a simple object literal.
This should be faster than doing a double for loop, as that's kind of what includes would be doing.
function makeRandom(count, max) {
return Array.from(new Array(count),
(a,ix) => Math.round(Math.random() * max));
}
function union(a,b) {
var aSet = new Set(a), oSet = new Set();
b.forEach((v) => { if(aSet.has(v)) oSet.add(v) });
return Array.from(oSet);
}
const
random1 = makeRandom(5, 10),
random2 = makeRandom(5, 10);
const
unionArray = union(random1, random2);
console.log(random1.join(':'));
console.log(random2.join(':'));
console.log(unionArray.join(':'));

Get a random item from a JavaScript array [duplicate]

This question already has answers here:
Getting a random value from a JavaScript array
(28 answers)
Closed 7 years ago.
var items = Array(523, 3452, 334, 31, ..., 5346);
How do I get random item from items?
var item = items[Math.floor(Math.random()*items.length)];
1. solution: define Array prototype
Array.prototype.random = function () {
return this[Math.floor((Math.random()*this.length))];
}
that will work on inline arrays
[2,3,5].random()
and of course predefined arrays
var list = [2,3,5]
list.random()
2. solution: define custom function that accepts list and returns element
function get_random (list) {
return list[Math.floor((Math.random()*list.length))];
}
get_random([2,3,5])
Use underscore (or loDash :)):
var randomArray = [
'#cc0000','#00cc00', '#0000cc'
];
// use _.sample
var randomElement = _.sample(randomArray);
// manually use _.random
var randomElement = randomArray[_.random(randomArray.length-1)];
Or to shuffle an entire array:
// use underscore's shuffle function
var firstRandomElement = _.shuffle(randomArray)[0];
If you really must use jQuery to solve this problem (NB: you shouldn't):
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
var items = [523, 3452, 334, 31, ..., 5346];
var item = jQuery.rand(items);
This plugin will return a random element if given an array, or a value from [0 .. n) given a number, or given anything else, a guaranteed random value!
For extra fun, the array return is generated by calling the function recursively based on the array's length :)
Working demo at http://jsfiddle.net/2eyQX/
Here's yet another way:
function rand(items) {
// "~~" for a closest "int"
return items[~~(items.length * Math.random())];
}
Or as recommended below by #1248177:
function rand(items) {
// "|" for a kinda "int div"
return items[items.length * Math.random() | 0];
}
var random = items[Math.floor(Math.random()*items.length)]
jQuery is JavaScript! It's just a JavaScript framework. So to find a random item, just use plain old JavaScript, for example,
var randomItem = items[Math.floor(Math.random()*items.length)]
// 1. Random shuffle items
items.sort(function() {return 0.5 - Math.random()})
// 2. Get first item
var item = items[0]
Shorter:
var item = items.sort(function() {return 0.5 - Math.random()})[0];
Even shoter (by José dB.):
let item = items.sort(() => 0.5 - Math.random())[0];
var rndval=items[Math.floor(Math.random()*items.length)];
var items = Array(523,3452,334,31,...5346);
function rand(min, max) {
var offset = min;
var range = (max - min) + 1;
var randomNumber = Math.floor( Math.random() * range) + offset;
return randomNumber;
}
randomNumber = rand(0, items.length - 1);
randomItem = items[randomNumber];
credit:
Javascript Function: Random Number Generator
If you are using node.js, you can use unique-random-array. It simply picks something random from an array.
An alternate way would be to add a method to the Array prototype:
Array.prototype.random = function (length) {
return this[Math.floor((Math.random()*length))];
}
var teams = ['patriots', 'colts', 'jets', 'texans', 'ravens', 'broncos']
var chosen_team = teams.random(teams.length)
alert(chosen_team)
const ArrayRandomModule = {
// get random item from array
random: function (array) {
return array[Math.random() * array.length | 0];
},
// [mutate]: extract from given array a random item
pick: function (array, i) {
return array.splice(i >= 0 ? i : Math.random() * array.length | 0, 1)[0];
},
// [mutate]: shuffle the given array
shuffle: function (array) {
for (var i = array.length; i > 0; --i)
array.push(array.splice(Math.random() * i | 0, 1)[0]);
return array;
}
}

Categories