"Generate range of integers" one-liner solution - javascript

I was just starting with codewars challenges, one of them is "Generate range of integers" problem. I managed to solve problem with for loop, but i was wondering if i could solve this problem in one line?
I have tried using fill and map methods combined with while and do-while loops. The problem is I am unable get the first number (starting min) inside my returned array. The first time while loop evaluates to true, it returns minimum incremented by step value.
function generateRange(min,max,step) {
return Array(Math.floor((max - min) / step ))
.fill()
.map(() => {while(min <= max) {return min+=step;}});
}
console.log(generateRange(0, 10, 2));
I was expecting number from min to max, with step within them. min = 0, max = 10, step = 2, result => [0,2,4,6,8,10] but i get all the numbers without the first starting minimum [2,4,6,8,10].

You could decrement min by one step and take this value for further mapping.
function generateRange(min, max, step) {
return Array.from(
{ length: Math.floor((max - (min -= step)) / step) },
() => min += step
);
}
console.log(generateRange(0, 10, 2));

Sticking close to your original code:
function generateRange(min,max,step) {
return Array(Math.floor((max - min + step) / step ))
.fill()
.map(() => {while(min <= max) {return (min+=step)-step;}});
}
console.log(generateRange(0, 10, 2));

function generateRange(min, max, step) {
return "0".repeat(Math.floor((max - min) / step) + 1).split("").map((x, i) => min + i * step);
}
console.log(generateRange(0, 10, 2));

Something like this should work:
function range(min,max,step = 1) {
return [...Array(Math.floor((max-min+step)/step)).keys()].map(x => x*step + min);
}
console.log(range(0,10,2))
console.log(range(2,5,1))
console.log(range(1,25,5))
console.log(range(0,100,10))

I think generator functions are nice for these type of things.
Your question asks for a one-liner, although I think it looks way nicer as 2 functions. This is because you could use the generator in other places that you might not require an array.
function* iter_generateRange(min,max,step) {
for (let l = min; l <= max; l += step) yield l;
}
function generateRange(...args) {
return Array.from(iter_generateRange(...args));
}
const generateRange1liner = (min, max, step) =>
[...(function* () {
for (let l = min; l <= max; l += step) yield l;
}())];
//use the generateRange that returns the array
console.log(JSON.stringify(generateRange(10, 20, 2)));
//we can use it here too..
for (const r of iter_generateRange(3, 9, 3)) console.log(r);
//and finally using the 1 liner version
console.log(JSON.stringify(generateRange1liner(10, 20, 2)));

This should work:
function generateRange(min,max,step) {
return Array.from(Array(((max - min) / step) + 1).keys()).map(el => (el * step) + min);
}
console.log(generateRange(20, 40, 2));

Related

how to add random items to an array using a function that generates random numbers

I have this problem I'm not really sure how to do it. I'm having trouble figuring out how to add items to an array using a function that generates random numbers.
First I have to build a function that takes three parameters. one is the minimum random number, the other one is the maximum, the third parameter is the length of the array.
I've tried some stuff like removing the variable x setting it to an empty array.
var newArray = [generateRandomArray(10, 1, 10)]
function getRandomInt(min, max) {
return Math.floor(Math.random() * (
max + 1 - min)) + min;
}
var generateRandomArray = function(max, min, length) {
var x;
for (y = 1; y >= length; y++) {
x = [x.push(getRandomInt(min, max))]
console.log(x)
}
return x;
}
var newArray = [generateRandomArray(10, 1, 10)]
returns [5, 7, 9, 6, 6, 10, 8, 10, 9, 7]
I just get []
You were attempting to call generateRandomArray in the snippet before the function existed, and your for loop in generateRandomArray would never run as y started as 1 and you were comparing it as >= length:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (
max + 1 - min)) + min;
}
var generateRandomArray = function(max, min, length) {
var x = [];
for (var y = 0; y < length; y++) {
x.push(getRandomInt(min,max));
}
return x;
}
var newArray = generateRandomArray(10, 1, 10);
console.log(newArray);
Comments in code to show fixes.
Also just as a side note, I would recommend switching the min and max around on one of the two functions that use them for consistency. Just a Good Practice.
//moved generateRandomArray function to beginning so it is defined before it is called
var generateRandomArray = function(max, min, length) {
var x = []; //initialise array
for (y = 1; y <= length; y++) { //fix the incorrect sign so the loop works
x.push(getRandomInt(min, max)); //fix the fact you kept overriding x and instead just push to the array
console.log(x)
}
return x;
}
var newArray = [generateRandomArray(10, 1, 10)]
console.log("newArray", newArray);
function getRandomInt(min, max) {
return Math.floor(Math.random() * (
max + 1 - min)) + min;
}

How to create an array of pseudo-random numbers that are spread out over an interval?

I would like to create an array of n (steps) integers within an interval [a (min), b (max)] which can be done like this (there are probably smarter ways):
function randomFromInterval(min, max, steps) {
return new Array(steps).fill(0).map(n => Math.floor(Math.random() * (max - min + 1) + min));
}
console.log(randomFromInterval(1, 100, 10));
Unfortunately, with truly random integers, it could happen that the result of randomFromInterval(1, 100, 5) is for example [1,2,3,4,5] which I would like to mitigate. The values of randomFromIntervalButSpread(min, max, steps) should therefore be spread out over the interval in such a way that
randomFromIntervalButSpread(0, 4, 5) => [0,1,2,3,4]
randomFromIntervalButSpread(10, 60, 5) => [1X,2X,3X,4X,5X] // X being 0-9
.
.
.
To summarize: The numbers should be random in the sense that if steps > |[min, max]| the results differ from iteration to iteration but are never grouped together within the interval.
Iterate through the steps and generate values with the appropriate floor and ceiling. Note that in this function, end is exclusive.
function randomFromIntervalButSpread(start, end, intervals) {
var arr = [];
var range = (end - start) / intervals;
// Generates a value between start and start + range
// Moves start up to the next step
while (start < end) {
arr.push(Math.floor(Math.random() * range + start));
start += range;
}
return arr;
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(1, 100, 10))
Short function:
function randomFromIntervalButSpread(min, max, steps) {
return new Array(steps).fill(0).map((n, i) => Math.floor(Math.random()*((max-min)/steps+1)+(i*(max-min))/steps+min));
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(100, 200, 10))
+ shuffle:
function randomFromIntervalButSpread(min, max, steps) {
return new Array(steps).fill(0).map((n, i) => Math.floor(Math.random()*((max-min)/steps+1)+(i*(max-min))/steps+min)).sort(() => Math.random() - 0.5);
}
console.log(randomFromIntervalButSpread(0, 5, 5))
console.log(randomFromIntervalButSpread(100, 200, 10))

Sum every element in a range in Javascript

I'm trying to make my small function work which adds every number together in a range.
For example when I call the method like: sumAll(3,10) it should do
3+4+5+6+7+8+9+10
It works if I give the function positive integers but if it receives a negative number or a string or an array for example, it doesn't work properly.. I just want to return "ERROR" if the supplied parameter is not a positive integer.
Can I have some help with this please? Is there a more elegant (better) way?
My code:
const sumAll = (...args) => {
let max = Math.max(...args);
let min = Math.min(...args);
if ((min < 0) || (!Number.isInteger(min)) || (!Number.isInteger(max)) || (Array.isArray(...args))) {
return "ERROR";
}
let n = (max - min) + 1;
return ((max + min) * n) / 2;
}
You could use a gaussian formular for getting a count from 1 ... n and subtract the sub count.
For getting only a result if possible, you could add a check for positive integers.
const
isPositiveInt = v => Number.isInteger(v) && v > 0,
sumN = n => n * (n + 1) / 2,
range = (m, n) => isPositiveInt(m) && isPositiveInt(n)
? sumN(Math.max(m, n)) - sumN(Math.min(m, n) - 1)
: 'ERROR';
console.log(range(3, 10));
console.log(range(10, 3));
console.log(range());
console.log(range(3));
console.log(range('', 10));
console.log(range(0.2, 0.3));

javascript: generate random number that are separated by a specific "margin"

I would like to generate x random number between a and b. Each of this x random number should not be closer that y to the others ones:
If y = 100 then I shouldn't have 500 and 555 generated but 500 and 601 would be okay.
More context:
I would like to generate x circles with d3.js, that don't touch each other (so y would be the radius of the the bigger circle). I could use something like this but I would prefer something without using force().
The easiest thing to do would be to loop on Math.random() until you get an answer that isn't in the no-fly zone. The downside is some unnecessary calls to Math.random, but if the no-fly space is small compared to the range it wouldn't be significant.
UPDATE: avoid history of results. fun.
let history = [];
function myRand(range, margin) {
while(true) {
let result = Math.trunc(Math.random() * range);
if (history.every(last => {
return Math.abs(result - last) > margin;
})) {
history.push(result);
return result;
}
}
}
for (let i = 0; i < 5; i++)
console.log(myRand(100, 10));
I would make a simple b-tree and keep track of the nodes as I go along. This should be very fast and dependable.
let MAX = 100
let MIN = 0
let BUFFER = 10
let MAXCOUNT = 6
function randomBetween(min, max) {
return {
val: Math.floor(Math.random() * (max - min + 1) + min)
}
}
function addLeaves(f, min = MIN - BUFFER, max = MAX + BUFFER, arr = []) {
if (arr.length >= MAXCOUNT) return arr
arr.push(f.val)
f.left = (min + BUFFER < f.val - BUFFER) && addLeaves(randomBetween(min + BUFFER, f.val - BUFFER), min, f.val, arr)
f.right = (f.val + BUFFER < max - BUFFER) && addLeaves(randomBetween(f.val + BUFFER, max - BUFFER), f.val, max, arr)
return arr
}
let res = addLeaves(randomBetween(MIN, MAX))
console.log(res)
This will give at most MAXCOUNT values separated by BUFFER. It's possible it will return fewer than MAXCOUNT if there is no room in the range given a large buffer. Because of the nature of the b-tree, it will fill out gaps as necessary.
EDIT:
Since we don't actually use the tree structure(it might be useful in other circumstances thought), this can be rewritten to use the raw numbers. This changes it to a single function call to make reuse easier:
function getRandom(min, max, buffer, maxcount) {
let randomBetween = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
function addLeaves(f, min, max, arr = []) {
if (arr.length < maxcount) {
arr.push(f);
if(min + buffer < f - buffer) addLeaves(randomBetween(min + buffer, f - buffer), min, f, arr);
if(f + buffer < max - buffer) addLeaves(randomBetween(f + buffer, max - buffer), f, max, arr);
}
return arr
}
return addLeaves(randomBetween(min, max), min - buffer, max + buffer)
}
// now you can call with just: min, max, buffer, maxcount
let res = getRandom(0, 100, 10, 6)
console.log(res)
Here's another approach, though it feels a little less random to me. It doesn't run the risk of infinite loops in the case of a narrow space.
function randomWithMargin(start, stop, count, margin) {
let spacing = Math.trunc((stop - start) / count);
let left = start;
let right = start + spacing;
let results = [];
while (count--) {
let r = left + Math.trunc(Math.random() * (right - left));
results.push(r);
left = r + margin; // ensure next number is at least margin from last
right += spacing;
}
return results;
}
console.log(randomWithMargin(10, 110, 10, 7));

Find n logarithmic intervals between two end points

I am trying to find n logarithmic intervals between two numbers.
eg: for a function
logDiv (10, 10000, 3) where 10 is the starting point, 10000 the ending point, and 3 the number of intervals, I would like to get the following output:
(* {10, 100, 1000, 10000} *)
What I have tried:
function logInterval(total_intervals, start, end) {
var index, interval, result = [];
for (index = 0; index < total_intervals; index++) {
interval = (index/total_intervals * Math.log((end - start) + 1) - 1 + start);
result.push(interval);
}
return result;
}
var intervals = logInterval(5, 1, 500);
https://jsfiddle.net/qxqxwo3z/
This was based on my (poor) understanding of the following solution I found in stack exchange mathematica:
logspace [increments_, start_, end_] := Module[{a}, (
a = Range[0, increments];
Exp[a/increments*Log[(end - start) + 1]] - 1 + start
)]
https://mathematica.stackexchange.com/questions/13226/how-can-i-get-exactly-5-logarithmic-divisions-of-an-interval
Please can someone help me with this? Its not necessary to follow any of my above attempts, just explaining what I tried.
The best way is to start with the logarithmic difference of the end value and the start value, divided by the intervals.
x = (Math.log(end) - Math.log(start)) / total_intervals;
For the factor, you need to do the reverse operation
factor = Math.exp(x);
For getting an array you can multiply the start value with the factor and insert it into the array. The next value is the last value multiplied by the factor, until all items are generated.
function logInterval(total_intervals, start, end) {
var x = (Math.log(end) - Math.log(start)) / total_intervals,
factor = Math.exp(x),
result = [start],
i;
for (i = 1; i < total_intervals; i++) {
result.push(result[result.length - 1] * factor);
}
result.push(end);
return result;
}
console.log(logInterval(3, 10, 10000));
console.log(logInterval(5, 1, 500));
console.log(logInterval(12, 220, 440)); // some frequencies
Thanks to the above answer by nina and a lot more googling and stack overflow answers, I found that this works for me:
function logInterval(total_intervals, start, end) {
var startInterVal = 1, endInterval = total_intervals,
minLog = Math.log(start), maxLog = Math.log(end),
scale = (maxLog-minLog) / (endInterval-startInterVal),
result = [];
for (i = 1; i < total_intervals; i++) {
result.push(Math.exp(minLog + scale*(i - startInterVal)));
}
result.push(end);
return result;
}
https://jsfiddle.net/qxqxwo3z/1/

Categories