Chapter 4 - Exercises

How are you all doing ??!?!?!?!?

Here is my version of the “sum of the range” exercise

It works but I feel like I could shorten a lot more. I tried to use a ternary operator to change the beginning and end value of i and use only one “for” operation for the range function but kept failing :sweat_smile:

function sum(arr){
  var ans = 0;
  for (var i=0;i<(arr.length);i++){
    ans += arr[i];
  }
  console.log(ans);
  return ans
}

function range(start,end,step=1){
  var arr = [ ];
  if (start<end){
    for(let i=start; i<(end+1); i+=Math.abs(step))
      {arr.push(i)}
    }
  else {
    for(let i=end; i<(start+1); i+=Math.abs(step))
    {arr.unshift(i)}
}
  console.log(arr);
  sum(arr);
  }
1 Like

Hi, I posted right after you and peaked at your code ^^
For the arguments of your range function you can write:
range (start,end,step=1) instead of having !step and step=1 in your code
Hope i’m not saying something wrong :sweat_smile:
Good luck!

2 Likes

thanks for reminding me. i forgot i came across that feature during the course.

1 Like

Hi @jonsax and @daz3d,

Well done @daz3d for spotting the issue in the end :+1:

The only problem with the original code posted is that the start and end input numbers are assigned to the variables numOne and numTwo as strings and not number values.
This is causing the for loop condition that generates ascending ranges to evaluate to false after the first iteration, and the for loop condition that generates descending ranges to never evaluate to false, thus entering the permanent loop that you’ve been experiencing.

So the only amendment necessary to the first version posted is:

// using Number()
var numOne = Number($("#firstNum").val());
var numTwo = Number($("#SecondNum").val());

// using parseInt()
var numOne = parseInt($("#firstNum").val());
var numTwo = parseInt($("#SecondNum").val());

You don’t need to make any of the other amendments that @daz3d mentioned to this version.

However, the second version posted is different, as it incorporates an additional function range() which is outside of the button-click event-handler function. In this version you need to:

  1. move the assignment of the start and end input numbers to within this button-click function, otherwise you are assigning empty string values (not null, by the way) to the start and end variables, because they haven’t been input yet; and
  2. again convert the string values to number values with either of the two methods shown above:
$("#makeArray").click(function() {
   let start = parseInt($("#firstNum").val());
   let end = parseInt($("#secondNum").val());
   range(start, end); /* now calls range() with input numbers as number values */
});

and

  1. the function range() needs to log its output to the console rather than return it, unless of course you are intending to record it in a different way.

Sorry, I didn’t get to this earlier. But I hope this clears up any remaining questions for you @jonsax, and that you can now continue with and complete your program for the exercise Sum of a Range :slightly_smiling_face:

Let us know if either of you have any more questions about any of these issues.

1 Like

Hey @JanjoHR1!

That’s great that you’ve been following things up from this exercise, and developing things further :+1:

Your analysis is good, and you are absolutely right about the 2 bugs in the program:

We could only avoid these bugs (before your extra work) by not inputting a step value of 0, and by always making sure we input a negative step value for descending ranges (or omitted it completely).

Your solution is a good start, but I think we can improve it:

/* We can just return the text, as this will be logged to the console
   together with the function call anyway. */

if (step === 0) {
   return "0 is not a valid step for this function";
}

/* We also need to handle this returned string value in our sum() function,
   as our function call invokes both functions consecutively. You could
   add the following if statement immediately after the function header. */

if (range === "0 is not a valid step for this function") {
   return "Sum unvailable";    // or some other similar message of your choice
}

What do you think?

I encourage you to continue experimenting with your exercise solutions and other small programs you’ve written during the course, in order to develop them further. This is great practice and you’ll continue to learn and improve in the process.

You may also be interested in having a look at some extended programs that other students have written for this exercise, which include a user interface for the inputs, and a click button to execute the calculations.

I also mention this, because ensuring the input data meets certain constraints (e.g. no zeros, only negative numbers for descending ranges etc.) can be handled directly in the input fields when the user initially enters the data. This input validation can be coded using HTML attributes (e.g. pattern) and regular expressions. If you are interested I’d encourage you do your own research about these, and then to experiment with them.

1 Like

//Chapter 4 - Exercise 1

// // Write a function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end

function listArray (startingNumber, endNumber) {

let finalArray = [startingNumber]



for(let i = 1; i <= (endNumber - startingNumber); i++) {

        finalArray.push(startingNumber + i)

}

console.log(finalArray)

}

listArray (1, 10)

// // Write a function that takes an array of numbers and returns the sum of these numbers

function sumArray (startingNumber, endNumber) {

let finalArray = [startingNumber]

let totalArray = startingNumber



for(let i = 1; i <= (endNumber - startingNumber); i++) {

    finalArray.push(startingNumber + i)

    totalArray += finalArray[i]

}

console.log(totalArray)

}

sumArray (1, 10)

// // Bonus - Step function

function stepArray (startingNumber, endNumber, increment) {

let finalArray = [startingNumber]



if(increment < 0) {

    for(let i = increment; i >= (endNumber - startingNumber); i += increment) {

            finalArray.push(startingNumber + i)

    }

    console.log(finalArray)

}

if(increment >= 0) {

    for(let i = increment; i <= (endNumber - startingNumber); i += increment) {

            finalArray.push(startingNumber + i)

    }

    console.log(finalArray)

}

}

stepArray (5, 2, -1)

// write two functions, reverseArray and reverseArrayInPlace

// #1 reverseArray, takes an array as argument and produces a new array that has the same elements in the inverse order.

function reverseArray (array) {

let newArray = []

for(let i = 1; i <= array.length; i++) {

    newArray.push(array[array.length - i])

}

console.log(newArray)

}

let array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

reverseArray(array1)

// #2 reverseArrayInPlace, does what the reverse method does: it modifies the array given as argument by reversing its elements

function reverseArrayInPlace(array) {

for (let i = 0; i < Math.floor(array.length / 2); i++) {

  let old = array[i];

  array[i] = array[array.length - 1 - i];

  array[array.length - 1 - i] = old;

}

console.log(array);

}

let array1 = [1, 2, 3, 4]

reverseArrayInPlace (array1)

1 Like

You’ve got how the method works now @jonsax :muscle:

However, if you use the condition…

i <= Math.floor((array.length - 1) / 2);

…then, with odd-numbered arrays, you are performing an additional, unnecessary loop which just swaps the middle element with itself e.g. 7 elements (index 0 to index 6):

Condition becomes:
i <= round down (7 - 1 / 2)  =>
i <= round down (3)  =>
i <= 3  =>  loop exits at index 4
Index 3 is the last position to be swapped, but this is the middle element which doesn’t need to be swapped. However, it does still work.

This condition does work correctly with even-numbered arrays e.g. 8 elements (index 0 to 7):

Condition becomes:
i <= round down ((8 -1) / 2)  =>
i <= round down (3.5)  =>
i <= 3  =>  loop exits at index 4
This is correct because index 3 needs to be swapped with index 4 to complete the reversal.


In order for the condition to prevent the additional, unnecessary loop with odd-numbered arrays (and still work for even-numbered arrays) we can simplify it as follows:

i < Math.floor(array.length / 2);

With odd-numbered arrays e.g. 7 elements (index 0 to index 6):

Condition becomes:
i < round down (7 / 2)  =>
i < round down (3.5)  =>
i < 3  =>  loop exits at index 3
Index 2 is the last position to be swapped (with index 4). This is correct, because index 3 is the middle element and doesn’t need to be swapped.

And this simplified condition also works with even-numbered arrays e.g. 8 elements (index 0 to 7):

Condition becomes:
i < round down (8 / 2)  =>
i < round down (4)  =>
i < 4  =>  loop exits at index 4
This is correct because index 3 needs to be swapped with index 4 to complete the reversal.

I hope that makes sense! :sweat_smile:
You’ve understood the basic method and logic, so it should do.

thanks @jon_m!! That is my understanding exactly. Just curious why the i <= math.floor((array.length - 1)/2) would ever be chosen for this exercise? That’s what’s shown in the example solution, however, as you’ve shown, the simplified i < math.floor(array.length/2) is clearly more CPU efficient (by an infinitesimally tiny amount). So why wouldn’t this more efficient boolean be exemplified rather than the less efficient one shown as the solution in eloquent javascript?

1 Like

Hi @Wevlin,

Nice solutions :ok_hand:
It’s always nice to see a new alternative method :slightly_smiling_face:

A couple of comments…

The Sum of a Range

Your solution handles a default ascending step of 1 , but we also need to handle a default negative (or reverse) step of -1  e.g. when our function call is:

console.log(range(5, 2));
console.log(sum(range(5, 2)));

Only adding  step = 1  won’t handle that. Can you resolve that?

Reversing an Array

Are you still working on the second part, where you need to reverse the input array (in place), rather than output a new reversed array (leaving the input array unchanged)?

Just let us know if there is anything you’re unsure of.

You’re very welcome @jonsax,

I’m not sure which version you’re looking at, but the model answer for this exercise in the online version of Eloquent JavaScript (Third Edition) does show the simplified condition…

i < Math.floor(array.length / 2); 

https://eloquentjavascript.net/code/#4.2
(You need to click on the “look at the solution” button at the bottom of the page)

I hope my other post about the issue you had with The Sum of a Range has cleared that up now — sorry I couldn’t get to look at it earler :sweat_smile:

THE SUM OF A RANGE

var NewArray = [];

              function range(start, end, step){
                if (step == undefined){
                  for (var i = start; i <= end; i++){
                    NewArray.push(i);
                  };
                } else if (step < 0){
                  for (var i = start; i >= end; i--){
                    NewArray.push(i);
                  };
                } else {
                  for (var i = start; i <= end; i = i + step){
                    NewArray.push(i);
                  };
                };
              document.write(NewArray + "<br><br>");
            };

            // range(1, 10);
            // range(1, 10, 2);
             range(5, 2, -1);


            var toPrint = 0;

            function sum(array){
                for (i = 0; i < array.length; i++){
                    toPrint = toPrint + array[i];
                    console.log(array[i]);
                };
              document.write(toPrint);
            };

            sum(NewArray);
1 Like

Thnks for letting us know @Wevlin :slightly_smiling_face:

REVERSING AN ARRAY

var start_array = [1,2,3,4,5];
              var new_array = [];

              function reverseArray(array){
                for (var i = 0; i <= array.length-1; i++){
                  new_array.push(array.length-[i]);
                };
                document.write("This is the new array: " + new_array + "<br>");
              };

              document.write("This is the old array: " + start_array + "<br>");
              reverseArray(start_array);


              function reverseArrayInPlace(array){
                for (i = 0; i < (array.length-1)/2; i++){
                  var placeholder = array[array.length-1-i]
                  array[array.length-1-i] = array[i];
                  array[i] = placeholder;
                };
                document.write("This is the reversed array: " + array + "<br>");
              };

              reverseArrayInPlace(new_array);
1 Like

Hey @LaszloD,

Yes, I did work that out in the end, but as I was saying, it took me a while… that’s why I thought it would be good to let you know that, for another developer reading your program for the first time, it would need some explanatory notes to help understand your method quicker.

So, great! I think that’s an improvement, and more intuitive, now that all function calls (whether to generate ascending or descending arrays) can always be made with the first argument ending up as the first element in the array, and the second argument marking the cut-off.

The only remaining issue is that you don’t actually assign the default step values to the step parameter. This means that your current program doesn’t produce the correct ranges for function calls with only 2 arguments.

/* You need to change this...  */
if (!step) {start <= end ? 1 : -1}

// to this...
if (!step) {step = start <= end ? 1 : -1}
/* or include the ternary operator in the function header as a
   default step parameter, as follows...  */
const myRange = (start, end, step = start <= end ? 1 : -1) => {...}

Great! :ok_hand:

OK, but you also need to format the code that you copy-and-paste to here before posting it. You can do that by clicking on the </> icon in the menu at the top of this forum text-editor. That will give you 2 sets of 3 back ticks…
```
```
If you now input your code between these, you will end up with it nicely formatted, which is then also easier for you to organise with the correct spacing, indentation, nesting and brackets etc.

Sum Of A Range

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

function sum(array) {
    let accumulator = 0;
    for (let i = 0; i < array.length; i++)
        accumulator += array[i];
    return accumulator
};

and this is the Reverse Array:

function reverseArray(array) {
    let newArr = [];
    for (let i = array.length - 1; i >= 0; i--)
        newArr.push(array[i]);
    return newArr
};

function reverseArrayInPlace(reverseArray) {
    for (let i = 0; i <= (reverseArray.length / 2); i++) {
        let firstElement = reverseArray[i];
        reverseArray[i] = reverseArray[reverseArray.length - 1 - i];
        reverseArray[reverseArray.length - 1 - i] = firstElement;
    };
    return reverseArray
};
1 Like

Range and Sum

      function range(start,end){
        var array = [];
        var index = 0;
        for (var a = start; a<=end;a++){
           array[index] = a;
           index++
         }
        return array;
      }

      function sum (array){
        var sum = 0;
        $.each(array,function(index,value){
          sum = sum + value;
        })
      return sum;
      }

      function range2(start,end,step){
        var array = [];
        var index = 0;
        //var b = step;
        if (step==''){
          var b = 1;
        } else {
          var b = step;
        }
        if (step>=1){
          for (var a = start; a<=end ; a = a + b){
              array[index] = a;
              index++;
            }
        }
        if (step<0) {
          for (var a = start; a>=end ; a = a + b){
              array[index] = a;
              index++;
            }
        }
        return array;
      }

      array = range(1,10);
      console.log(array);
      sum = sum(array);
      console.log(sum);
      array2 = range2(11,2,-2);
      console.log(array2);

Reverse

function reverseArray (array){
        var newArray = [];
        var index = 0;
        for (var a = 0; a<= array.length-1; a++){
          newArray[index] = array[array.length-1-index];
          index++;
        }
        return newArray;
      }

      function reverseArrayInPlace (array){
        var index = 0;
        var temp = 0;
          for (var a = 0; a<= (~~(array.length/2))-1; a++){
            temp = array[a];
            array[a] = array[array.length-1-index];
            array[array.length-1-index] = temp;
            index++;
          }
        return array;
      }

      array = ["a1","b2","c3","d4","e5"];
      console.log(reverseArray(array));
1 Like

Here is my take on the reverse functions:

function reverseArray (arr){
  var newArr = [];
  for (let i=0; i<(arr.length);i++){
    newArr[i] = arr.length-i;
  }
  return newArr;
}

function reverseArrayInPlace (a){
  var a
  for (let i=0; i<(a.length);i++){
    a[i]=a.length-i;
  }
  return a
}
1 Like

Sum of a Range

var SumRange = 0;

  function range(start, end, step){
    if(step>=1){
      for(i=start; i<= end; i+=step){
        SumRange = SumRange + i;
        console.log(SumRange);
        }
      }

    else if (step==0){
      console.log("Can't calc with step 0!!!");
    }

    else {
    for(i=start; i>= end; i+=step){
      SumRange = SumRange + i;
       console.log(SumRange);
       }
    }

  }
  range(1,10,0);
  document.write(SumRange);

Reversing an Array

function reverseArray(array){
var newArray = [ ];
for(i=array.length-1; i>=0; i–){

      newArray.push(array[i]);
      console.log(newArray);

    }

  }
reverseArray([1,2,3,4,5]);
1 Like

Hi @matthurd,

You’ve made some good progress with this exercise :+1:

Have a look at my following comments to help you get your current code to execute successfully, and for some guidance with formatting, brackets etc.

The following code is your first version of range() with corrections (see the comments), improved formatting, and extra brackets. In keeping with your approach, I’ve kept the brackets to what I think is the bare minimum, while still maintaining clarity and readability:

var range = function(start, end) {
   var arr = [];
   if (end > start) {
      for (let i = start; i <= end; i++) arr.push(i);    // <=  not  <
      /* If you use < instead of <= in your condition, the final number
         (end) will be omitted from ranges with a step of 1 or -1 */
   }
   else for (let j = start; j >= end; j--) arr.push(j); // condition removed
   /* The condition isn't needed for the else statement, because by default it
      will execute with all values that evaluate to false in the if statement */
   return arr;
}

var sum = function(arr) {
   var sumvar = 0;
   for (let i = 0; i < arr.length; i++) {   // ;  not  .
   // You must end each expression in the for loop header with a semicolon
      sumvar = sumvar + arr[i];   // sumvar + arr[i]    not    sum + arr[i]
      // a more concise alternative is:   sumvar += arr[i]
   }
   return sumvar;
}

In order for your second version range2() to work, you need to add the step parameter to your function header, along with start and end . Then, the final stage of the exercise is to add functionality so that if no step value is passed to the function (i.e. if there is no 3rd argument in the function call) then a default step of 1 (for ascending ranges) or -1 (for descending ranges) will be set.

Good luck with the rest of the exercise! …whether you decide to continue with it now, or leave it for after having spent some time reviewing earlier material.