JavaScript string count loop misunderstanding - javascript

I am having trouble with a basic task. I need to write a JavaScript program that contains an array of at least five strings, loops through the array, and calls a function for each item; this function should check to see how long the string is:
If the string is less than four characters, print the phrase "Less Than Four"
If equal to four characters, print "Exactly Four"
If longer than four, print "More Than Four"
I have tried so many things, but feel like I am looking in the wrong spots. I understand this is basic but I cant seem to wrap my head around this...
My Code right now:
var colors = ["teal", "violet", "silver", "green", "red", "purple"];
var count;
for (count = 0; count < colors.length; count++) {
console.log(colors[count]);
}
if (colors > 4) {
console.log("greater than 4");
}
if (colors < 4) {
console.log("less than 4");
}
if (colors = 4) {
console.log("is equal to 4");
}

Arrays have built-in methods for looping that allow a callback function to be executed upon each iteration of the loop. In your scenario, since you are just needing to examine the string, the .forEach() method is probably the most appropriate.
In the function, you only need a simple if/then/else statement to determine which message to print.
var colors = ["teal", "violet", "silver", "green", "red", "purple"];
colors.forEach(function(color){
if(color.length < 4){
console.log(color + " has less than 4 characters.");
} else if (color.length === 4) {
console.log(color + " has 4 characters.");
} else {
console.log(color + " has more than 4 characters.");
}
});

Newer versions of JavaScript support for..of syntax
const colors =
[ "teal", "violet", "silver", "green", "red", "purple" ]
for (const c of colors)
{ if (c.length > 4)
console.log(c, "greater than 4")
else if (c.length < 4)
console.log(c, "less than 4")
else
console.log(c, "equal to 4")
}
// teal equal to 4
// violet greater than 4
// silver greater than 4
// green greater than 4
// red less than 4
// purple greater than 4
You should separate the concerns of the loop and the length check using a function -
const colors =
[ "teal", "violet", "silver", "green", "red", "purple" ]
const checkLength = str =>
{ if (str.length > 4)
return "greater than 4"
else if (str.length < 4)
return "less than 4"
else
return "equal to 4"
}
for (const c of colors)
console.log(c, checkLength(c))
// teal equal to 4
// violet greater than 4
// silver greater than 4
// green greater than 4
// red less than 4
// purple greater than 4
JavaScript is a multi-paradigm language, so it supports writing the same program in a wide variety of styles -
const colors =
[ "teal", "violet", "silver", "green", "red", "purple" ]
const checkLength = str =>
{ if (str.length > 4)
console.log(`${str} is greater than 4`)
else if (str.length < 4)
console.log(`${str} is less than 4`)
else
console.log(`${str} is equal to 4`)
}
colors.forEach(checkLength)
// teal equal to 4
// violet greater than 4
// silver greater than 4
// green greater than 4
// red less than 4
// purple greater than 4
JavaScript support for expressions is quite good too, removing the need for imperative-style keywords like if, else, switch, for, while, do and even return -
const colors =
[ "teal", "violet", "silver", "green", "red", "purple" ]
const checkLength = x =>
x.length > 4 // if ...
? `${x} is greater than 4`
: x.length < 4 // else if ...
? `${x} is less than 4`
: `${x} is equal to 4` // else
console.log(colors.map(checkLength))
// [ "teal is equal to 4"
// , "violet is greater than 4"
// , "silver is greater than 4"
// , "green is greater than 4"
// , "red is less than 4"
// , "purple is greater than 4"
// ]

Call a function on every element and check the length inside an if-else block
var colors = ["teal", "violet", "silver", "green", "red", "purple"];
var count;
for (count = 0; count < colors.length; count++) {
console.log(colors[count]);
stringLength(colors[count]);
}
function stringLength(string) {
if (string.length > 4) {
console.log("greater than 4");
} else if (string.length < 4) {
console.log("less than 4");
} else {
console.log("is equal to 4");
}
}

You need to put the if statements inside the curly braces of the for loop, so for every color it will run through all the if conditions and print if it matches.
A more idiomatic way of doing what you're currently trying to do is to implement the logic within the body of a forEach function, which is part of the Array object's prototype
var colors = ["teal", "violet", "silver", "green", "red", "purple"];
colors.forEach(function(currentColorToCheck) { //currentColorToCheck is a temporary variable that the forEach function gives you (1 for every item of colors Array)
if (currentColorToCheck.length > 4) { // we use the .length function (part of String prototype and Array prototype) to get the length of the string
console.log("greater than 4");
}
if (currentColorToCheck.length < 4) {
console.log("less than 4");
}
if (currentColorToCheck.length === 4) { // here for an equality comparison, use === instead of =
console.log("is equal to 4");
}
})
The forEach is a more convenient way to express "Iterate over values of an Array". You can look at the documentation for some further guidance.
As a sidenote, there's tons of prototype (builtin) functions for every JavaScript type (Object, Array, String, Number, Date, Math, etc.) which you might want to study in your spare time. Mozilla Developer Network has great resources for that.

Related

How to use Math.random() to generate random from an array while repeating one element more? [duplicate]

For example: There are four items in an array. I want to get one randomly, like this:
array items = [
"bike" //40% chance to select
"car" //30% chance to select
"boat" //15% chance to select
"train" //10% chance to select
"plane" //5% chance to select
]
Both answers above rely on methods that will get slow quickly, especially the accepted one.
function weighted_random(items, weights) {
var i;
for (i = 1; i < weights.length; i++)
weights[i] += weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return items[i];
}
I replaced my older ES6 solution with this one as of December 2020, as ES6 isn't supported in older browsers, and I personally think this one is more readable.
If you'd rather use objects with the properties item and weight:
function weighted_random(options) {
var i;
var weights = [options[0].weight];
for (i = 1; i < options.length; i++)
weights[i] = options[i].weight + weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return options[i].item;
}
Explanation:
I've made this diagram that shows how this works:
This diagram shows what happens when an input with the weights [5, 2, 8, 3] is given. By taking partial sums of the weights, you just need to find the first one that's as large as a random number, and that's the randomly chosen item.
If a random number is chosen right on the border of two weights, like with 7 and 15 in the diagram, we go with the longer one. This is because 0 can be chosen by Math.random but 1 can't, so we get a fair distribution. If we went with the shorter one, A could be chosen 6 out of 18 times (0, 1, 2, 3, 4), giving it a higher weight than it should have.
Some es6 approach, with wildcard handling:
const randomizer = (values) => {
let i, pickedValue,
randomNr = Math.random(),
threshold = 0;
for (i = 0; i < values.length; i++) {
if (values[i].probability === '*') {
continue;
}
threshold += values[i].probability;
if (threshold > randomNr) {
pickedValue = values[i].value;
break;
}
if (!pickedValue) {
//nothing found based on probability value, so pick element marked with wildcard
pickedValue = values.filter((value) => value.probability === '*');
}
}
return pickedValue;
}
Example usage:
let testValues = [{
value : 'aaa',
probability: 0.1
},
{
value : 'bbb',
probability: 0.3
},
{
value : 'ccc',
probability: '*'
}]
randomizer(testValues); // will return "aaa" in 10% calls,
//"bbb" in 30% calls, and "ccc" in 60% calls;
Here's a faster way of doing that then other answers suggested...
You can achieve what you want by:
dividing the 0-to-1 segment into sections for each element based on their probability (For example, an element with probability 60% will take 60% of the segment).
generating a random number and checking in which segment it lands.
STEP 1
make a prefix sum array for the probability array, each value in it will signify where its corresponding section ends.
For example:
If we have probabilities: 60% (0.6), 30%, 5%, 3%, 2%. the prefix sum array will be: [0.6,0.9,0.95,0.98,1]
so we will have a segment divided like this (approximately): [ | | ||]
STEP 2
generate a random number between 0 and 1, and find it's lower bound in the prefix sum array. the index you'll find is the index of the segment that the random number landed in
Here's how you can implement this method:
let obj = {
"Common": "60",
"Uncommon": "25",
"Rare": "10",
"Legendary": "0.01",
"Mythical": "0.001"
}
// turning object into array and creating the prefix sum array:
let sums = [0]; // prefix sums;
let keys = [];
for(let key in obj) {
keys.push(key);
sums.push(sums[sums.length-1] + parseFloat(obj[key])/100);
}
sums.push(1);
keys.push('NONE');
// Step 2:
function lowerBound(target, low = 0, high = sums.length - 1) {
if (low == high) {
return low;
}
const midPoint = Math.floor((low + high) / 2);
if (target < sums[midPoint]) {
return lowerBound(target, low, midPoint);
} else if (target > sums[midPoint]) {
return lowerBound(target, midPoint + 1, high);
} else {
return midPoint + 1;
}
}
function getRandom() {
return lowerBound(Math.random());
}
console.log(keys[getRandom()], 'was picked!');
hope you find this helpful.
Note:
(In Computer Science) the lower bound of a value in a list/array is the smallest element that is greater or equal to it. for example, array:[1,10,24,99] and value 12. the lower bound will be the element with value 24.
When the array is sorted from smallest to biggest (like in our case) finding the lower bound of every value can be done extremely quickly with binary searching (O(log(n))).
Here is a O(1) (constant time) algo to solve your problem.
Generate a random number from 0 to 99 (100 total numbers). If there are 40 numbers (0 to 39) in a given sub-range, then there is a 40% probability that the randomly chosen number will fall in this range. See the code below.
const number = Math.floor(Math.random() * 99); // 0 to 99
let element;
if (number >= 0 && number <= 39) {
// 40% chance that this code runs. Hence, it is a bike.
element = "bike";
}
else if (number >= 40 && number <= 69) {
// 30% chance that this code runs. Hence, it is a car.
element = "car";
}
else if (number >= 70 && number <= 84) {
// 15% chance that this code runs. Hence, it is a boat.
element = "boat";
}
else if (number >= 85 && number <= 94) {
// 10% chance that this code runs. Hence, it is a train.
element = "train";
}
else if (number >= 95 && number <= 99) {
// 5% chance that this code runs. Hence, it is a plane.
element = "plane";
}
Remember this, one Mathematical principle from elementary school? "All the numbers in a specified distribution have equal probability of being chosen randomly."
This tells us that each of the random numbers have equal probability of occurring in a specific range, no matter how large or small that range might be.
That's it. This should work!
I added my solution as a method that works well on smaller arrays (no caching):
static weight_random(arr, weight_field){
if(arr == null || arr === undefined){
return null;
}
const totals = [];
let total = 0;
for(let i=0;i<arr.length;i++){
total += arr[i][weight_field];
totals.push(total);
}
const rnd = Math.floor(Math.random() * total);
let selected = arr[0];
for(let i=0;i<totals.length;i++){
if(totals[i] > rnd){
selected = arr[i];
break;
}
}
return selected;
}
Run it like this (provide the array and the weight property):
const wait_items = [
{"w" : 20, "min_ms" : "5000", "max_ms" : "10000"},
{"w" : 20, "min_ms" : "10000", "max_ms" : "20000"},
{"w" : 20, "min_ms" : "40000", "max_ms" : "80000"}
]
const item = weight_random(wait_items, "w");
console.log(item);
ES2015 version of Radvylf Programs's answer
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] || 0));
return acc;
}, []);
const random = Math.random() * weights[weights.length - 1];
return items[weights.findIndex((weight) => weight > random)];
}
And ES2022
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] ?? 0));
return acc;
}, []);
const random = Math.random() * weights.at(-1);
return items[weights.findIndex((weight) => weight > random)];
}
Sure you can. Here's a simple code to do it:
// Object or Array. Which every you prefer.
var item = {
bike:40, // Weighted Probability
care:30, // Weighted Probability
boat:15, // Weighted Probability
train:10, // Weighted Probability
plane:5 // Weighted Probability
// The number is not really percentage. You could put whatever number you want.
// Any number less than 1 will never occur
};
function get(input) {
var array = []; // Just Checking...
for(var item in input) {
if ( input.hasOwnProperty(item) ) { // Safety
for( var i=0; i<input[item]; i++ ) {
array.push(item);
}
}
}
// Probability Fun
return array[Math.floor(Math.random() * array.length)];
}
console.log(get(item)); // See Console.

Given a JavaScript array, how to loop through and find a value?

I have a JavaScript array like so:
const colors = ['blue', 'red', 'green'];
Given a number from 0 to Infinity, how can I find a color value like so:
colors[0] === 'blue';
colors[1] === 'red';
colors[2] === 'green';
colors[3] === 'blue';
colors[4] === 'red';
colors[5] === 'green';
// …
After the array is exhausted, finding colors based on a numerical value should loop through the array in order.
Given any number "x" between 0 and infinity:
colors[x % colors.length]
Will get you one of your colours
Seems like a fairly easy task to accomplish, all you have to do is to use modulo % operator when looking for a given index, as follows:
const colors = ['blue', 'red', 'green', 'pink', 'black', 'white', 'anyother'];
function getColor(idx){
return colors[idx % colors.length]
}
getColor(0) // "blue"
getColor(7) // "blue"
getColor(8) // "red"
getColor(13) // "anyother"
getColor(14) // "blue"
getColor(21) // "blue"
This way your array will stay intact and you won't get index higher than your array length.
Use the remainder (%) operator. This will give you the remainder of a division.
1/3 = 0 Remainder 3
1%3 = 3
12/3 = 4 Remainder 0
12%3 = 0
if num % 3 == 0
color = 'blue';
if num % 3 == 1
color = 'red';
if num % 3 == 2
color = 'green';

Finding the decimal exponent based on the index of a for-loop

The user chooses a number and then d3.js should display as many circles.
I have an array that is used to assigned color to the circles:
var color =["red","blue", "yellow", "orange",....., ]
● If the user choose 593, the first 500 circle should be red (color[0]) , the next 90 should be blue (color[1]) and the last 3 (color[2]) should be yellow Because
593= 500+90+3 = 5*10^2+9*10^1+3*10^0
or
with
var number = 593
var number_as_array = number.toString().split('');
Then
593 = 5*number_as_array[0]*10^number_as_array.length-0-1 + 9*number_as_array[1]*10^number_as_array.length-1-1+ 3*number_as_array[2]*10^number_as_array.length-2-1
● If the user choose 4168 the first 4000 circle should be red, the next 100 should be blue, the next 60 yellow and the last 8 orange
To assign the color to each circle I use to create an array of JS object build with a for loop
var data=[]
for (index =0; index< number; index++){
circle= {};
circle.cx = circle_x;
circle.cy = circle_y;
circle.color = color[????]
data.push(circle);
How can I assign the color to circle.color based on the condition above ?
Not to diminish from the other answer, here's an alternative approach.
Taking a given total number of circles, it checks to see how many significant digits of the total are required (rounding down) so that any given index is less than rounded total.
I'm not sure if that makes complete sense, so I'll use an example:
If there are 132 circles in total:
Indexes 0 through 99 will be less than 100 (132 rounded down with one significant digit).
Indexes 100 through 129 will be less than 130 (132 rounded down with two significant digits).
Indexes 130 and 131 will be less than 132 (132 with all significant digits).
Here's a quick demonstration (rows are 50 circles across):
var svg = d3.select("body")
.append("svg")
.attr("width",510)
.attr("height",510);
var n = 377;
var color = d3.scaleOrdinal()
.range(["steelblue","orange","crimson","lawngreen","pink"])
var digits = Math.floor(Math.log10(n));
var circles = svg.selectAll("circle")
.data(d3.range(n))
.enter()
.append("circle")
.attr("cx",function(d,i) { return i%50 * 10 + 5 })
.attr("cy",function(d,i) { return Math.floor(i/50) * 10 + 5 })
.attr("r",5)
.attr("fill", function(d,i) {
var exp = digits;
while (i < Math.floor(n/Math.pow(10,digits-exp))*Math.pow(10,digits-exp)) {
exp--;
}
return color(exp);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
var color = ["red","orange", "yellow", "green", "blue", "indigo", "violet"];
var circleCount = "4192"; // use string
var length = circleCount.length;
var counter = [];
for (var i = 0; i < length; i++) {
var digit = circleCount.substring(i, i+1);
var exponent = length - i - 1;
var number = digit * Math.pow(10, exponent);
counter.push(number); // might have to use .unshift instead of .push
}
console.log(counter);
for (var i = 0; i < counter.length; i++) {
for (var j = 0; j < counter[i]; j++) {
drawCircle(color[i]);
}
}
This is a perfect case for using D3's Threshold Scales: you give it N numbers where you want breaks between the colors, and N+1 colors to return for any input value. Here is the example from the docs:
var color = d3.scaleThreshold()
.domain([0, 1])
.range(["red", "white", "green"]);
color(-1); // "red"
color(0); // "white"
color(0.5); // "white"
color(1); // "green"
color(1000); // "green"
So the challenge for your case is how to convert (for instance) your example input of 593 into the array of the two numbers [500, 590]:
var sinput = 593 + ""; // make the input a string
var digits = sinput.split("").slice(0, -1); // use all digits but the last one
var breaks = digits.map((d, i, a) =>
+(a.slice(0, i+1).join("")) * Math.pow(10, a.length-i)
);
var colors = ["red", "blue", "yellow", "orange"];
var tScale = d3.scaleThreshold()
.domain(breaks)
.range(colors);
Anything < 500 maps to "red", from 500 - 589 maps to "blue", and ≥ 590 maps to "yellow". The additional range color ("orange") is not used, unless a 4-digit number is used as input.
Note: this logic assumes the input number will have at least 2-digits.
You can now assign the color at the time of creating the circle -- rather than pre-populating it in the data array -- using syntax like .attr("color", (d, i) => tScale(i))
The other approaches seem overly complex. You can split the number into its digits, then create the required 10^digitIndex circles with colour based on the index. I've included a line to check that the number isn't too big.
function mapColors(num) {
var color =['red','blue', 'yellow', 'orange'];
// If the number is longer than the color array, return undefined
if ((''+num).length > color.length) return;
return (''+num).split('').reduce(function (acc, n, i, arr) {
for (var j=n*Math.pow(10, arr.length-i-1); j; --j) {
acc.push({'color':color[i]});
// Add more circle properties here
}
return acc;
}, []);
}
console.log(mapColors(23));

How to choose a weighted random array element in Javascript?

For example: There are four items in an array. I want to get one randomly, like this:
array items = [
"bike" //40% chance to select
"car" //30% chance to select
"boat" //15% chance to select
"train" //10% chance to select
"plane" //5% chance to select
]
Both answers above rely on methods that will get slow quickly, especially the accepted one.
function weighted_random(items, weights) {
var i;
for (i = 1; i < weights.length; i++)
weights[i] += weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return items[i];
}
I replaced my older ES6 solution with this one as of December 2020, as ES6 isn't supported in older browsers, and I personally think this one is more readable.
If you'd rather use objects with the properties item and weight:
function weighted_random(options) {
var i;
var weights = [options[0].weight];
for (i = 1; i < options.length; i++)
weights[i] = options[i].weight + weights[i - 1];
var random = Math.random() * weights[weights.length - 1];
for (i = 0; i < weights.length; i++)
if (weights[i] > random)
break;
return options[i].item;
}
Explanation:
I've made this diagram that shows how this works:
This diagram shows what happens when an input with the weights [5, 2, 8, 3] is given. By taking partial sums of the weights, you just need to find the first one that's as large as a random number, and that's the randomly chosen item.
If a random number is chosen right on the border of two weights, like with 7 and 15 in the diagram, we go with the longer one. This is because 0 can be chosen by Math.random but 1 can't, so we get a fair distribution. If we went with the shorter one, A could be chosen 6 out of 18 times (0, 1, 2, 3, 4), giving it a higher weight than it should have.
Some es6 approach, with wildcard handling:
const randomizer = (values) => {
let i, pickedValue,
randomNr = Math.random(),
threshold = 0;
for (i = 0; i < values.length; i++) {
if (values[i].probability === '*') {
continue;
}
threshold += values[i].probability;
if (threshold > randomNr) {
pickedValue = values[i].value;
break;
}
if (!pickedValue) {
//nothing found based on probability value, so pick element marked with wildcard
pickedValue = values.filter((value) => value.probability === '*');
}
}
return pickedValue;
}
Example usage:
let testValues = [{
value : 'aaa',
probability: 0.1
},
{
value : 'bbb',
probability: 0.3
},
{
value : 'ccc',
probability: '*'
}]
randomizer(testValues); // will return "aaa" in 10% calls,
//"bbb" in 30% calls, and "ccc" in 60% calls;
Here's a faster way of doing that then other answers suggested...
You can achieve what you want by:
dividing the 0-to-1 segment into sections for each element based on their probability (For example, an element with probability 60% will take 60% of the segment).
generating a random number and checking in which segment it lands.
STEP 1
make a prefix sum array for the probability array, each value in it will signify where its corresponding section ends.
For example:
If we have probabilities: 60% (0.6), 30%, 5%, 3%, 2%. the prefix sum array will be: [0.6,0.9,0.95,0.98,1]
so we will have a segment divided like this (approximately): [ | | ||]
STEP 2
generate a random number between 0 and 1, and find it's lower bound in the prefix sum array. the index you'll find is the index of the segment that the random number landed in
Here's how you can implement this method:
let obj = {
"Common": "60",
"Uncommon": "25",
"Rare": "10",
"Legendary": "0.01",
"Mythical": "0.001"
}
// turning object into array and creating the prefix sum array:
let sums = [0]; // prefix sums;
let keys = [];
for(let key in obj) {
keys.push(key);
sums.push(sums[sums.length-1] + parseFloat(obj[key])/100);
}
sums.push(1);
keys.push('NONE');
// Step 2:
function lowerBound(target, low = 0, high = sums.length - 1) {
if (low == high) {
return low;
}
const midPoint = Math.floor((low + high) / 2);
if (target < sums[midPoint]) {
return lowerBound(target, low, midPoint);
} else if (target > sums[midPoint]) {
return lowerBound(target, midPoint + 1, high);
} else {
return midPoint + 1;
}
}
function getRandom() {
return lowerBound(Math.random());
}
console.log(keys[getRandom()], 'was picked!');
hope you find this helpful.
Note:
(In Computer Science) the lower bound of a value in a list/array is the smallest element that is greater or equal to it. for example, array:[1,10,24,99] and value 12. the lower bound will be the element with value 24.
When the array is sorted from smallest to biggest (like in our case) finding the lower bound of every value can be done extremely quickly with binary searching (O(log(n))).
Here is a O(1) (constant time) algo to solve your problem.
Generate a random number from 0 to 99 (100 total numbers). If there are 40 numbers (0 to 39) in a given sub-range, then there is a 40% probability that the randomly chosen number will fall in this range. See the code below.
const number = Math.floor(Math.random() * 99); // 0 to 99
let element;
if (number >= 0 && number <= 39) {
// 40% chance that this code runs. Hence, it is a bike.
element = "bike";
}
else if (number >= 40 && number <= 69) {
// 30% chance that this code runs. Hence, it is a car.
element = "car";
}
else if (number >= 70 && number <= 84) {
// 15% chance that this code runs. Hence, it is a boat.
element = "boat";
}
else if (number >= 85 && number <= 94) {
// 10% chance that this code runs. Hence, it is a train.
element = "train";
}
else if (number >= 95 && number <= 99) {
// 5% chance that this code runs. Hence, it is a plane.
element = "plane";
}
Remember this, one Mathematical principle from elementary school? "All the numbers in a specified distribution have equal probability of being chosen randomly."
This tells us that each of the random numbers have equal probability of occurring in a specific range, no matter how large or small that range might be.
That's it. This should work!
I added my solution as a method that works well on smaller arrays (no caching):
static weight_random(arr, weight_field){
if(arr == null || arr === undefined){
return null;
}
const totals = [];
let total = 0;
for(let i=0;i<arr.length;i++){
total += arr[i][weight_field];
totals.push(total);
}
const rnd = Math.floor(Math.random() * total);
let selected = arr[0];
for(let i=0;i<totals.length;i++){
if(totals[i] > rnd){
selected = arr[i];
break;
}
}
return selected;
}
Run it like this (provide the array and the weight property):
const wait_items = [
{"w" : 20, "min_ms" : "5000", "max_ms" : "10000"},
{"w" : 20, "min_ms" : "10000", "max_ms" : "20000"},
{"w" : 20, "min_ms" : "40000", "max_ms" : "80000"}
]
const item = weight_random(wait_items, "w");
console.log(item);
ES2015 version of Radvylf Programs's answer
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] || 0));
return acc;
}, []);
const random = Math.random() * weights[weights.length - 1];
return items[weights.findIndex((weight) => weight > random)];
}
And ES2022
function getWeightedRandomItem(items) {
const weights = items.reduce((acc, item, i) => {
acc.push(item.weight + (acc[i - 1] ?? 0));
return acc;
}, []);
const random = Math.random() * weights.at(-1);
return items[weights.findIndex((weight) => weight > random)];
}
Sure you can. Here's a simple code to do it:
// Object or Array. Which every you prefer.
var item = {
bike:40, // Weighted Probability
care:30, // Weighted Probability
boat:15, // Weighted Probability
train:10, // Weighted Probability
plane:5 // Weighted Probability
// The number is not really percentage. You could put whatever number you want.
// Any number less than 1 will never occur
};
function get(input) {
var array = []; // Just Checking...
for(var item in input) {
if ( input.hasOwnProperty(item) ) { // Safety
for( var i=0; i<input[item]; i++ ) {
array.push(item);
}
}
}
// Probability Fun
return array[Math.floor(Math.random() * array.length)];
}
console.log(get(item)); // See Console.

Javascript - Array.repeat limitations?

I have an array and I'm looking to create a new array, composed of the original array contents repeated 3 times. For example:
var array = ["red", "green", "blue"];
newArray = ["red red red", "green green green", "blue blue blue"];
My code so far:
var triples = myArray.map(function(){
for (x = 0; x < myArray.length; x++){
return myArray[x].repeat(3);
};
});
console.log(triples);
But this only returns
triples = ["redredred", "redredred", "redredred"];
Any suggestions for a newbie?
You can build a simple function to do this:
var triples = function(xs) {
return xs.map(function(x) {
return [x,x,x].join(' ')
})
}
You can abstract it a bit more, as a transformation to use with map, and allow any number of repetitions:
var repeat = function(n) {
return function(x) {
return Array.apply(0, {length:n})
.map(function(){return x})
.join(' ')
}
}
['foo','bar','lol'].map(repeat(3))
//^ ['foo foo foo', 'bar bar bar', 'lol lol lol']
Here's an obnoxiously simple way of repeating all elements 3 times:
["red", "green", "blue"].map( [].join.bind(['',' ',' ','']) );
// == ["red red red", "green green green", "blue blue blue"]
this method also doesn't need "".repeat(), which is not universally supported yet.
another bonus: if a number, null, or other non-string sneaks in the array it won't break.
if you want it re-usable, that's easy enough:
var repeater = [].join.bind(['',' ',' ','']) ;
["red", "green", "blue"].map( repeater );
// == ["red red red", "green green green", "blue blue blue"]
[1,2,3,4,5].map( repeater );
// == ["1 1 1", "2 2 2", "3 3 3", "4 4 4", "5 5 5"]
There is no reason to use map and the loop. I guess you wanted either
var triples = myArray.map(function(str){
return str.repeat(3);
});
console.log(triples);
or
var triples = [];
for (var x = 0; x < myArray.length; x++) {
triples[x] = myArray[x].repeat(3);
}
console.log(triples);
However, as #digitalfresh mentioned in the comments, String::repeat doesn't insert the spaces, so you rather want to use (" "+…).repeat(3).slice(1) or use the functions presented by #dandavis or #elclanrs. Or, since you seem to use an Ecmascript 6 method anyway, you could do Array.from({length:3}, ()=>…).join(" ").

Categories

Resources