I am trying to make a function in javascript that returns an array from range(start,end) and im supposed to make an optional argument that defaults to one when it is undefined. I can get the function to work when I provide all the arguments but returns an empty array when I only pass two arguments. Here is the question:
Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.
Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.
As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].
And here is my code:
function range(start, end, increment){
var array = [];
var current = start;
var counter;
if (increment == undefined){
counter = 1;
}
else {
counter = increment;
}
if (increment > 0){
while(current <= end){
array.push(current);
current += counter;
}
}
else if (increment < 0){
while(current >= end){
array.push(current);
current += counter;
}
}
return array;
}
can someone explain why its breaking? I know some c# and I used to being able to jump into the debugger in visual studio when something goes wrong unlike javascript.
I am trying to make a function in javascript that returns an array from range(start,end) and im supposed to make an optional argument that defaults to one when it is undefined. I can get the function to work when I provide all the arguments but returns an empty array when I only pass two arguments. Here is the question:
Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.
Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.
As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].
And here is my code:
function range(start, end, increment){
var array = [];
var current = start;
var counter;
if (increment == undefined){
counter = 1;
}
else {
counter = increment;
}
if (increment > 0){
while(current <= end){
array.push(current);
current += counter;
}
}
else if (increment < 0){
while(current >= end){
array.push(current);
current += counter;
}
}
return array;
}
can someone explain why its breaking? I know some c# and I used to being able to jump into the debugger in visual studio when something goes wrong unlike javascript.
Share Improve this question edited Jan 25, 2021 at 0:23 peterh 1 asked Jun 1, 2016 at 12:30 user6127082user6127082 6 | Show 1 more comment9 Answers
Reset to default 6A very simple unidirectional (ascending), inclusive range – goes from x
to y
incrementing by 1
each time.
// range :: (Int, Int) -> [Int]
const range = (x,y) =>
x > y ? [] : [x, ...range(x + 1, y)];
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // []
A slight adaptation that supports bidirectional (ascending or descending) range – still increments or decrements by 1
// range :: (Int, Int) -> [Int]
const range = (x,y) => {
if (x > y)
return range(y,x).reverse();
else
return x === y ? [y] : [x, ...range(x + 1, y)];
}
console.log(range(1,4)); // [1,2,3,4]
console.log(range(3,3)); // [3]
console.log(range(6,3)); // [6,5,4,3]
Another adaptation that uses higher-order functions for more control over the range – this effectively gives you the stepping/incrementing behaviour some of you are looking for – tho this is more powerful because it lets you use a function, t
, to choose the next value.
const gte = x => y => y >= x;
const lte = x => y => y <= x;
const add = x => y => y + x;
const sub = x => y => y - x;
// range :: (Int, (Int -> Bool), (Int -> Int)) -> [Int]
const range = (x, p, t) => {
if (p(x))
return [x, ...range(t(x), p, t)];
else
return [];
};
console.log(range(2, lte(8), add(2))); // [2,4,6,8]
console.log(range(9, gte(0), sub(3))); // [9,6,3,0]
console.log(range(9, gte(0), sub(5))); // [9, 4]
// very power. wow.
const double = x => x + x;
console.log(range(2, lte(50), double)); // [2,4,8,16,32]
This function has the same risks inherent with for
and while
– it's up to you to make sure you don't put it into an infinite loop.
functional overload
Warning: Esoteric, impractical functionals ahead. The following information is provided for your academic pleasure only.
The range
function also happens to be one of my favourite demonstrations of the Y
combinator. I'll show you two examples here.
naïve range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const range = Y (f => acc => x => y =>
x > y ? acc : f ([...acc, x]) (x + 1) (y)
) ([]);
console.log(range (3) (6)); // [3,4,5,6]
console.log(range (6) (6)); // [6]
console.log(range (9) (6)); // []
and the higher-order range
const U = f => f (f);
const Y = U (h => f => f (x => h (h) (f) (x)));
const lt = x => y => y < x;
const gt = x => y => y > x;
const add1 = x => x + 1;
const sub1 = x => x - 1;
const range = Y (f => acc => x => p => t =>
p(x) ? f ([...acc, x]) (t(x)) (p) (t) : acc
) ([]);
console.log(range (3) (lt(6)) (add1)); // [3,4,5]
console.log(range (6) (lt(6)) (add1)); // []
console.log(range (9) (gt(6)) (sub1)); // [9,8,7]
What a thing of beauty that is.
You could simplify the code a bit and use the increment
variable for incrementing. But before, I suggest to test if the value is falsy (0
, null
, undefined
, etc) and assign then 1
to it.
Not implemented: check if start
and end
is appropriate.
function range(start, end, increment) {
var array = [];
var current = start;
increment = increment || 1;
if (increment > 0) {
while (current <= end) {
array.push(current);
current += increment;
}
} else {
while (current >= end) {
array.push(current);
current += increment;
}
}
return array;
}
console.log(range(1, 3, 0));
console.log(range(2, 5));
console.log(range(1, 9, 1));
console.log(range(5, 2, -1));
First you check if increment
is undefined and set counter
accordingly, but later you check if (increment > 0){
again. While it is undefined
none of your cases matches, so nothing happens.
Change your checks to this:
if (counter > 0){
// ...
}
else if (counter < 0){
// ...
}
Very compact range function that handles float and negative numbers:
const range = (lower,upper,step)=>{
return Array.from(new Array(Math.floor(upper/step-lower/step)+1),(_,i)=>lower/step+i).map(x=>x*step)
}
For example you can use it like:
range(10,30,3) // [10, 13, 16, 19, 22, 25, 28]
range(0,0.5,0.01) // [0, 0.01, 0.02, ... , 0.48, 0.49, 0.5]
range(1,10,2) // [1, 3, 5, 7, 9]
range(-5,10,0.5) // [-5, -4.5, -4, ... , 1, 1.5, 2]
range(5,2,-0.5) // [5, 4.5, 4, 3.5, 3, 2.5, 2]
Here a more understandable version:
const range = (lower, upper, step) => {
end = upper / step // Upper bound
start = lower / step // Lower bound
n = Math.floor(end - start) + 1 // Size that includes upper bound as well
zeros_arr = Array(n).fill(0) // Init array with n zeros
unscaled_arr = zeros_arr.map((_, i) => (start + i)) // Setting each zero to the upper bound + the index
range_arr = unscaled_arr.map(x => (x * step)) // Scaling every numbers with the step
return range_arr
}
I realize this is an extremely old question and you probably don't need it anymore, but just in case someone reads this, my favorite way to do this is using a generator:
function* rangeGenerator(start, end = null, step = 1) {
if (end == null) {
end = start
start = 0
}
if (Math.sign(end - start) !== Math.sign(step)) {
step *= -1
}
while (Math.sign(step) === 1 ? start < end : start > end) {
yield start
start += step
}
}
const range = (start, end = null, step = 1) => [...rangeGenerator(start, end, step)]
This way, the range
function will generate an array that goes from start
(inclusive) to end
(exclsive). step
should always be provided as a positive number because the sign is automatically handled by the generator according to whiche of start
and end
is bigger. You can pass it a negative step
, but it's unnecessary.
This gives you the ability to use a for of
loop over the range
for (let i = 0; i < 100; i++) {}
// Is the same as
for (const i of range(100)) {}
The given answers are great. I just wanted to give you an idea of how a more functional approach could solve the task:
// auxiliary functions:
const append = (x, xs) => xs.concat([x]);
const prepend = (x, xs) => [x].concat(xs);
// main function
const range = (x, y, step, acc = [], op = append) =>
step && step < 0
? range(y, x, -step, acc, prepend)
: step && x <= y
? range(x + step, y, step, op(x, acc), op) // tail call
: acc;
console.log(range(1,5,1)); // [1,2,3,4,5]
console.log(range(1,5,2)); // [1,3,5]
console.log(range(1,5,6)); // [1]
console.log(range(5,1,1)); // []
console.log(range(1,5,0)); // []
console.log(range(5,1,-1)); // [5,4,3,2,1]
console.log(range(5,1,-2)); // [5,3,1]
console.log(range(5,1,-6)); // [1]
console.log(range(1,5,-1)); // []
Algorithm:
acc = []
andop = append
default parameter values (are taken if omitted during the function invocation)step && step < 0
short circuits ifstep
is zero, otherwise checks if step is negativerange(y, x, -step, acc, prepend)
is called whenstep
is negative and convertsrange
's parameterization so thatstep
can be positive (note that-step
is equivalent with-(-1)
, which is evaluated to1
)range(x + step, y, step, op(x, acc), op)
recursive case that means, the function calls itself (notice thatop
can be eitherappend
orprepend
depending on the initial sign ofstep
, thatx
is increased bystep
and appended/prepended toacc
)acc
base case that stops the recursion and returns the accumulated array
I solved this problem as part of the eloquent javascript course. Based on the problem statement I chose default parameters for the increments and used a while loop to include the second argument.
function range(start, stop, increment=1)
{
let range_arr = [];
if(start < stop)
{
while( start <= stop)
{
range_arr.push(start);
start += increment;
}
}
else{
while( start >= stop)
{
range_arr.push(start);
start += increment;
}
}
return range_arr;
}
I decided to use the for loop to create the increments as follows.
function range(start,end) {
let array = [];
for (let counter = 0; counter < end; counter++) {
array.push(start);
start += 1;
}
return array;
}
console.log(range(1,10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I've using the snippet for range, with help of iterative callback theory.
That repeat the same function function with respected range attribute i.e 10-100. also 10-100 with 2 or 5 different.
function range(n, m, callback, k){
if(!k) k=1;
while(true){
if(n<=m){
callback(n);
n+=k;
if(n>=m) break;
}else{
callback(n);
n-=k
if(n<=m) break;
}
}
}
you can execute the snippet by
range(10, 100,function(n){
// do here
},10)
you can extends the snippets by
function range_arr(n, m,callback,k) {
let a = []
range(n,m,function(n){
a.push(callback(n))
},k)
return a;
}
you can use this snippet by
let arr=range_arr(0,10,function(n){
return n=n*n;
},2);
(5)[0, 4, 16, 36, 64]
var counter = increment || 1;
– castletheperson Commented Jun 1, 2016 at 12:35?
operator? – user6127082 Commented Jun 1, 2016 at 12:37undefined
,null
,0
,""
,false
orNaN
) then it will assign the value on the right. – castletheperson Commented Jun 1, 2016 at 12:38