๐Ÿฅš JS1

What even is JavaScript?

JS1 block viewer

This block viewer lets you flick through all the existing blocks in the JS1 folder so you can choose what parts to add to your pages and what parts you might want to create, revise, or leave out.

It's literally just an alphabetical list of whatever is in this folder.

โ†™๏ธ โ†˜๏ธ Making a choice

Learning Objectives

Our function works for morning inputs like "08:00". In this case, the function returns the target output of "08:00 am" as required. However, at the moment, the output of formatAs12HourClock("23:00") is "23:00 am".

๐Ÿ’ก We need to execute some different logic when the time is beyond midday

We can interpret this behaviour as a question:

flowchart LR A{Is the time before midday?} -- true --> B[Add am to time string] A -- false --> C[???]

We need to work out what to do in the case when the time input is later than midday.

๐Ÿงญ Describing the strategy

Let’s describe the strategy for dealing with an input that is after midday.

Earlier we observed that when the time goes beyond midday then we can subtract 12 from the hours time to get the new hours for the 12 hour clock time.

Before writing code, we can define our approach in steps using a flowchart:

Starting with an input like "23:00":

flowchart LR A[extract the hours, '23', from the time '23:00'] --> B[convert '23' to a number, 23] B --> C{check: Are the hours greater than 12?} C -- false --> D[add am to time string] C -- true --> E[subtract 12 from the hours, to get 11] E --> F[add 'pm' to the rest of the time, to get '11:00 pm'] F --> G[return the new time]

This approach involves running some code conditionally. In this case, we’re only going to continue doing Steps 4-6 if the condition hours are greater than 12 is true.

โ™ป๏ธ Reusing the function

Learning Objectives

Our goal is for convertToPercentage to be reusable for any number. To check this goal, let’s call convertToPercentage with different arguments and check the return value each time:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

const output1 = convertToPercentage(0.5);
const output2 = convertToPercentage(0.231);

console.log(output1);
console.log(output2);

When we execute this code we want to log the target output for each input: 0.5 and 0.231:

50%
23.1%

However, given the function’s current implementation, we get the following logs:

50%
50%

๐ŸŒ Global scope

At the moment, decimalNumber is in the global scope ๐Ÿงถ ๐Ÿงถ global scope Variables declared in the global scope are available everywhere in your program. Variables declared in a { block scope } are only available within that block. Any block within your program can access variables that are defined within the global scope. . Any functions we declare can reference variables in the global scope. If a variable is in the global scope, we say that variable is a global variable.

Play computer and step through the code to check why we get the output below:

50%
50%

โš–๏ธ Comparing current and target output

Learning Objectives

Let’s start with a function declaration of formatAs12HourClock:

1
function formatAs12HourClock() {}

Whenever we call formatAs12HourClock we expect it to return a particular value, which we call the ๐ŸŽฏ target output. For example, we expect formatAs12HourClock("08:00") to return "08:00 am". "08:00 am" is the ๐ŸŽฏ target output.

However, the current output is what the function actually returns right now.

๐Ÿ‘‰๐Ÿผ Work through the exercises

Let’s start with formatAs12HourClock defined like this:

1
function formatAs12HourClock() {}

Suppose we now call the function like this:

formatAs12HourClock("05:30");

For the call above, what is the

a) the current output? b) the ๐ŸŽฏ target output?

๐Ÿ‘‰๐Ÿผ Keep going

Let’s continue with formatAs12HourClock defined like this:

1
function formatAs12HourClock() {}

Suppose we call the function like this:

formatAs12HourClock("20:10");

For the call above, what is the

a) the current output? b) the ๐ŸŽฏ target output?

๐Ÿงฉ Comparing values

We have learned how to log values to the console. We can also compare two values. We check that a function produces some target output with a comparison.

We compare the current output of formatAs12HourClock("08:00") with the target output of "08:00 am" and ask: are these two values the same?. We use a comparison operator to compare two expressions and check if they evaluate to the same value. We use the strict equality operator === to check if two values are the same.

Left equals Right

formatAs12HourClock("08:00") === "8:00 am";

=== checks if the values on the left and right of the operator are the same. We can think of formatAs12HourClock("08:00") === "8:00 am" as the question: “Is the return value of formatAs12HourClock("08:00") equal to "8:00 am" ?” This leads to the question:

What will the expression formatAs12HourClock("08:00") === "8:00 am" evaluate to?

โœ… โŒ Boolean values

Does 1 equal 0? Yes or No? True or False?

Some values are best represented as strings: any piece of text, a name, address, etc will most likely be stored using the string data type. The number data type can store numbers we use in calculations.

If we’re comparing two things, there are only two different states: true or false. This leads us to the boolean datatype, which only has true or false values. Whenever we compare two values with a comparison operator, we end up with a boolean value: true or false. It’s one or the other. It’s boolean.

// using the strict equality comparison expression

console.log(42 === 10 + 32);
// logs true

console.log(10 * 5 === 60);
// logs false

๐Ÿ‘‰๐Ÿพ Now work through the exercises

Look at the code below and then try predicting how the computer will interpret each line. Remember to think about each line and work out if it is a declaration or a statement. You may want to use the Node REPL to help you check your answers.

Look at the code below and predict what each expression will evaluate to. Write your prediction down. Use the Node REPL to check your answers. Check out some relevant documentation if you’ve not seen a particular operator before.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
true === false;
5 == 2 + 4;
4 * 5 == "20";
3 * 2 === 6;
Math.min(3, 4, 5) === 4;
let mhairiName = "Mhairi";
typeof mhairiName === "string";
let mhairiAge = 28;
let isMhairOldEnoughToDrive = true;
let kilometersMhairiDrivesToWork = 9.4;

๐Ÿ‘‰๐Ÿฟ Keep Going

Look at the code below and predict what each expression will evaluate to. Write your prediction down. Use the Node REPL to check your answers. Check out some relevant documentation if you’ve not seen a particular operator before

1
2
3
4
5
6
7
"hello Mhairi" === `hello ${mhairiName}`;
"${mhairiName} is 28" === `Mhairi is ${mhairiAge}`;
isMhairOldEnoughToDrive;
isMhairOldEnoughToDrive === true;
mhairiAge >= 18;
29 <= mhairiAge;
Math.round(kilometersMhairiDrivesToWork) === 9;

๐Ÿ‘‰๐Ÿฟ Keep Going

Checking misconceptions ๐Ÿค”

Look at the code below and then predict what the expression will evaluate to. Be sure to explain your answer too. Use the Node Repl to check your answer. Does the REPL output make sense?

Try using the Node Repl to check your answer. Does the REPL output make sense?

1
console.log(10 + 32) === 42;

โœ… โŒ Interpreting feedback

Learning Objectives

We currently have a project structure like this:

week-4-test-example
โ”œโ”€โ”€ get-ordinal-number.test.js
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ package-lock.json
โ””โ”€โ”€ node_modules

1 directory, 3 files

And get-ordinal-number.test.js looks like this

test("converts 1 to an ordinal number", function () {
  const input = 1;
  const currentOutput = getOrdinalNumber(input);
  const targetOutput = "1st";

  expect(currentOutput).toBe(targetOutput);
});

After running the test above, we should get feedback indicating whether or not the test has passed.

exercise

Predict and explain what the test feedback will be when the test above is executed.

๐Ÿšข Defining the function

At the moment, our test feedback gives the following:

test-reference-error

The test code is throwing a ReferenceError ๐Ÿงถ ๐Ÿงถ ReferenceError A ReferenceError occurs when we try to reference a variable that we’ve not defined in our code.

This means that we haven’t defined a function named getOrdinalNumber, but we’re trying to use it.

To fix this, we can declare getOrdinalNumber.

function getOrdinalNumber() {}

test("converts 1 to an ordinal number", function () {
  expect(getOrdinalNumber(1)).toBe("1st");
});

Now we can run the tests again and check the test feedback.

Assertion errors

We now get the following feedback:

test-feedback-fail

Jest tells us 3 main things:

  1. The test case that failed
  2. The target output and the current output
  3. The line number where error occurred

Jest defines Expected and Received in the test feedback:

  • Expected: “1st”
  • Received: undefined

exercise

What are the values of Expected and Received in the test output?

How do Received and Expected match up with the target output and expected output ?

What line number did the test case fail on?

Passing getOrdinalNumber

We can now pass the test by implementing functionality for the first test case. We could write the following:

get-ordinal-number.test.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function getOrdinalNumber() {
  return "1st";
}

test("converts 1 to an ordinal number", function () {
  const input = 1;
  const currentOutput = getOrdinalNumber(input);
  const targetOutput = "1st";

  expect(currentOutput).toBe(targetOutput);
});

โœ๏ธ Writing an assertion

Learning Objectives

We now have 2 new concepts: booleans and comparisons. From these concepts, we can now write an assertion about the function formatAs12HourClock.

๐Ÿ”‘ An assertion is a check that our code behaves in a particular way: this check can either succeed or fail.

So far we’ve used the log function console.log. We can write assertions using another function: console.assert. The documentation says that console.assert writes an error message to the console if the assertion is false. If the assertion is true, nothing happens. As finalCharacter === "?" evaluates to true, no message will be written to the console.

const calculation = 10 + 32;
const result = 42;
console.assert(calculation === result);

๐ŸŽฎ Work through the exercises

Experiment with assertions

const calculation = 10 + 32;
const result = 42;
console.assert(calculation === result);

Update the final character in the sentence variable string to make the assertion fail. Check the output you get in the console.

  exercise</span>

Let’s look at an example using formatAs12HourClock:

function formatAs12HourClock() {}
console.assert(formatAs12HourClock("08:00") === "08:00 am");

Predict and explain if the assertion will succeed or fail. Pay particular attention to the return value of formatAs12HourClock.

Clarity with arguments

It would be useful to have more information as to why this assertion failed. We can pass an additional argument to console.assert:

function formatAs12HourClock() {}

console.assert(
  formatAs12HourClock("08:00") === "08:00 am",
  `current output: ${formatAs12HourClock("08:00")}, target output: 08:00 am`
);

Let’s break down these arguments to make sense of what’s going on:

  1. first argument - formatAs12HourClock("08:00") === "08:00 am" - the condition we’re checking
  2. second argument - current output: ${formatAs12HourClock("08:00")}, target output: 08:00 am - a message string that will be logged to the console if the condition is false.

๐Ÿงน Refactor

We can tidy up the assertion even further. As weโ€™re reusing the same expressions, we can store their result in variables with meaningful names so we can reuse them:

1
2
3
4
5
6
7
8
function formatAs12HourClock() {}

const currentOutput = formatAs12HourClock("08:00");
const targetOutput = "08:00 am";
console.assert(
  currentOutput === targetOutput,
  `current output: ${currentOutput}, target output: ${targetOutput}`
);

Execute this code; we now get a log in the console:

Assertion failed: current output: undefined, target output: 08:00 am

๐Ÿงฐ Implementing the functionality

On line 4, the function is being passed a single argument "08:00". But our function ignores it - it doesn’t declare any parameters. We can parameterise the function and label the input as time:

function formatAs12HourClock(time) {}

According to our assertion, when we call our function with an input of "08:00" we need to create an output of "08:00 am". We can add "am" to the time to get the target output. We can update our function with a template literal, set the return value and then re-run our code including our assertion to check the function is returning the correct value.

๐Ÿ““ We can and should continually check our assertions to see if our functionโ€™s current output meets our target output.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function formatAs12HourClock(time) {
  return `${time} am`;
}

const currentOutput = formatAs12HourClock("08:00");
const targetOutput = "08:00 am";
console.assert(
  currentOutput === targetOutput,
  `current output: ${currentOutput}, target output: ${targetOutput}`
);

โœ… Nothing is printed to the console, so this assertion is passing ๐Ÿ˜Ž

activity

Create a javascript file on your local machine and execute the code above. Double check you are seeing the same output in your terminal.

๐Ÿ’ผ Checking different cases

So far weโ€™ve only created assertions that check the functionโ€™s behaviour for times after midnight and before midday. In these cases, there is a pattern: take the current time and add "am" to the end.

Now we need to assert that the function behaves correctly when the time is later than midday. Letโ€™s create an assertion for our function when passed an input of "23:00":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function formatAs12HourClock(time) {
  return `${time} am`;
}

const currentOutput = formatAs12HourClock("08:00");
const targetOutput = "08:00 am";
console.assert(
  currentOutput === targetOutput,
  `current output: ${currentOutput}, target output: ${targetOutput}`
);

const currentOutput = formatAs12HourClock("23:00");
const targetOutput = "11:00 pm";
console.assert(
  currentOutput === targetOutput,
  `current output: ${currentOutput}, target output: ${targetOutput}`
);

๐Ÿ”„ Reusing variable names

When we run the file with Node, we get an error in the console:

SyntaxError: Identifier 'currentOutput' has already been declared

In this case, identifier means name of a variable, so in a variable declaration like

const currentOutput = formatAs12HourClock("08:23");

currentOutput is the identifier.

When an error is thrown, it means the execution of the program stops at this point and an error report is sent to the user. However, we want to do multiple assertions.

function formatAs12HourClock(time) {
  return `${time} am`;
}

const currentOutput = formatAs12HourClock("08:00");
const targetOutput = "08:00 am";
console.assert(
  currentOutput === targetOutput,
  `current output: ${currentOutput}, target output: ${targetOutput}`
);

// โŒ this assertion now fails
const currentOutput2 = formatAs12HourClock("23:00");
const targetOutput2 = "11:00 pm";
console.assert(
  currentOutput2 === targetOutput2,
  `current output: ${currentOutput2}, target output: ${targetOutput2}`
);

Now the second assertion fails with the following message:

Assertion failed: current output: 23:00 am, target output: 11:00 pm

exercise

โœ๏ธ Write another assertion that checks formatAs12HourClock returns the target output when passed another time input in the afternoon.

โŒ Errors

Learning Objectives

๐Ÿ—ฃ๏ธ Recall: A programming language is a set of rules for writing computer instructions.

So we need to understand what happens when we break those rules.

Let’s take an example:

1
2
3
const firstName = "Francesco;
const age = 33;
const nationality = "Italian";

On line 1, we have a variable declaration, but the string has a missing " We’re not obeying the syntactic rules for JavaScript: the rules for writing expressions, statements and other parts of the language.

When we execute the code above, we get this:

const firstName = "Francesco;
                  ^^^^^^^^^^^

Uncaught SyntaxError: Invalid or unexpected token

We get a SyntaxError message. This error message is telling us that we’ve broken the rules of the language.

activity

This code is broken. Before you run each block of code:

  1. Predict the error.
  2. Explain why the error happened.
const volunteer = "Shadi";
const volunteer = "Abdi";
const volunteer = "Shadi";
volunteer = "Hinde";
console.log(Math.round(10.3);

Saving return values

We can store the return value of a function in a variable. Function calls are also expressions. This means their value can also be stored in variables, same as the math expressions.

Suppose we have a file arithmetic.js containing this code:

const result = Math.round(10.3);

When this program is executed, it creates a variable called result and assigns to it the return value of the function, in this case the rounded number.

So result will have a value of 10.

๐Ÿ”ญ Logging and returning

Most functions return values we can use in our program.

Math.round takes a single input, does a calculation and then returns a value that we can use when our program is running.

Some functions don’t produce useful return values in our running program; but they can still cause effects.

exercise

const result = console.log("hello world");
  1. Predict what result will evaluate to when the code above runs.
  2. Execute this line in the Node REPL.
  3. Evaluate the value of the result variable to observe what happens.

When this program runs, the variable result will evaluate to undefined. undefined is a data type in JavaScript which usually means no value has been assigned. Unlike the number data type, which contains many possible values (1, 2, 10.3, etc), the undefined data type has exactly one value, undefined.

This can feel confusing as console.log is a function with a set of instructions. console.log does have an effect: it logs values to the console. However, console.log doesn’t produce an output that we can use inside the rest of our running program.

๐Ÿ’ก tip

Key fact: console.log is used to print values to the terminal. It doesnโ€™t produce an output in the running program.

โ“ Conditionally executing code

Learning Objectives

Recall: JavaScript programs are built up from sequences of declarations and statements.

In programming, we can use an if statement to execute some code when a given condition is true. In JavaScript, we can write an if statement as follows:

if (condition) {
  // do some code in here
}

The if statement consists of:

  1. if keyword: this is the start of the if statement
  2. (condition): condition is an expression that evaluates to true or false
  3. {}: a block statement: any code we want to execute if the condition is true goes inside the curly braces here

We can represent this with a diagram too:

flowchart LR IC{condition} IB[Execute code in body] EXIT([End of if statement]) IC --> |true| IB IC -.-> |false| EXIT IB --> EXIT

๐Ÿ•น๏ธ Playing computer

function checkDivisibility(a, b) {
  if (a % b === 0) {
    return `${a} is divisible by ${b}`;
  }

  return `${a} is not divisible by ${b}`;
}

console.log(checkDivisibility(10, 2));
console.log(checkDivisibility(50, 3));

Play computer with the example above, step through each line in the code and work out happens each time the function is called.

  • What is the return value each time the function is called?
  • What value is assigned to the phoneNumber parameter each time the function is called?

If you’ve not encountered any syntax before, remember to look it up in some documentation.

๐Ÿ•น๏ธ Playing computer

function getCountryCode(phoneNumber) {
  if (phoneNumber.startsWith("+44")) {
    return "UK";
  }
}

getCountryCode("+447831620328");
getCountryCode("+989871783972");

Play computer with the example above, step through each line in the code and work out happens each time the function is called.

  • What is the return value each time the function is called?
  • What value is assigned to the phoneNumber parameter each time the function is called?
  • Try describing the purpose of this function - what should it do?

๐Ÿƒ Installing Jest

Learning Objectives

Jest is a package used to help us to write and run test cases in JavaScript. Our next step will be to figure out how to install the Jest package on our machine, so that we can use it in our project.

We can find out more about the Jest framework from the documentation online.

In the Getting started section of the documentation, Jest gives us the following command:

npm install jest --save-dev

Let’s break down the different parts of this command.

  • npm - in the terminal, we can use the npm command to install packages from the npm registry

  • install - download a package on to our machine.

  • jest - this is the name of the package we want to install on our machine

  • --save-dev - this means the package is needed for development but not needed in production

So overall we can think of this command as saying: “Please go to the npm database, find the Jest package and install it on my local machine”

Let’s execute this command in the same directory as the package.json. To double check we’re in the correct directory, we can run pwd:

$ pwd
.../cyf/week-4-example

pwd is telling us we’re in the week-4-example directory.

We need to double check the package.json is also there too.

$ ls
package.json

Now we can execute the command

npm install --save-dev jest

Our project structure will now look as follows:

week-4-example
โ”œโ”€โ”€ node_modules
โ”œโ”€โ”€ package-lock.json
โ””โ”€โ”€ package.json

1 directory, 3 files

After running the command, we now have a directory called node_modules in our project too.

The node_modules directory contains all the code from the dependencies ๐Ÿงถ ๐Ÿงถ dependencies A dependency is a package that your project depends upon. we installed in our project. You won’t need to look inside the node_modules directory - you just need to know it contains the code for Jest and any other dependencies we install in our project.

Running the npm command also updated our package.json file for us:

{
  "name": "week-4-test-example",
  "description": "An example application showing how to write tests using the jest framework",
  "devDependencies": {
    "jest": "^29.5.0"
  }
}

We’ve now got some additional information inside the package.json:

"devDependencies" : {
    "jest":  "^29.5.0"
}

exercise

Install Jest on your local machine. Double check you’ve got the correct files and folders written to your local machine.

๐ŸŽ’ Starting a project

Learning Objectives

Let’s start a brand new project in a directory called week-4-example and create a file called package.json in our project.

  1. Open your terminal and ensure you’re inside the <span class="c-our-name">The Docs</span> directory you created earlier in the course.
  2. Make a new directory on your local machine called week-4-example.
  3. Change directory into week-4-example and double-check your current working directory.
pwd .../<span class="c-our-name">The Docs</span>/week-4-example

๐Ÿ‘‰๐Ÿฝ Now create a package.json file

๐Ÿ’ก tip

A package.json stores useful information about our project, like the name, description, and version. It is written in the JSON format.
  1. Create a package.json in week-4-example.
  2. Make sure it contains a name and description.

๐Ÿ‘‰๐Ÿฝ Need help? Follow step by step instructions

  1. Create a package.json file in your project directory:
touch package.json
  1. Add a name to it like this:
{
  "name": "week-4-test-example"
}
  1. Add a description:
{
  "name": "week-4-test-example",
  "description": "An example application showing how to write tests using the jest framework"
}

We can continue adding more information about our project as the project grows. For now, double-check we only have a package.json in our project:

ls package.json

๐ŸŽฎ Playing computer

Learning Objectives

To understand how convertToPercentage works we must build a mental model of how the computer executes our code. To build this model, we use a method called playing computer ๐Ÿงถ ๐Ÿงถ playing computer .Playing computer means simulating how the computer executes our code. We “step through” the code, line by line, and work out what the computer does when it follows each instruction.

We will use an interactive code visualiser to play computer.

activity

In a JavaScript program, each line is an instruction that will have some effect. For example, a line of code with a variable declaration means “store a new variable with this value in memory”. In the interactive widget, arrows are used to show which line just executed and which line is next to be executed.

Click next to see what happens when the computer executes the following program. Pay particular attention to what happens when the function convertToPercentage is called.

๐Ÿ–ผ๏ธ Global frame

As we step through the program, we keep track of two things: memory and the line that is being currently executed. We keep track of this information using a frame ๐Ÿงถ ๐Ÿงถ frame Think of a frame as the context in which some code gets executed. We use frames to keep track of memory and the line of code that is being currently executed. .

The global frame is always the first frame that gets created when our program starts executing. It is like the starting point for our program, the place where code gets executed first. When we run the code above, decimalNumber and convertToPercentage are both stored in the global frame.

๐Ÿ–ผ๏ธ ย Local frame

๐Ÿ’ก tip

A function call is an instruction to run the code inside a function

Whenever we call a function a new frame is created for executing the code inside that function. In the example above, we call the function convertToPercentage on line 7 and then a new frame is created for convertToPercentage. Inside the convertToPercentage frame, the computer executes the instructions inside convertToPercentage, storing new variables in memory and keeping track of the current line that is being executed.

๐Ÿ’ฌ Declarations and statements

Learning Objectives

A variable declaration is an example of a declaration ๐Ÿงถ ๐Ÿงถ declaration A declaration is an instruction that binds an identifier to a value . It has the effect of creating a variable.

let versionNumber = "2.0.0"; // declaration
versionNumber = "2.0.1"; // statement

The code above has one variable declaration and one statement.

  1. The first line is a declaration - creating a variable versionNumber with a value of "2.0.0"
  2. The second line is a statement - reassignment ๐Ÿงถ ๐Ÿงถ reassignment Reassignment means changing the value associated with an identifier the value of versionNumber to "2.0.1"

In this example, we’ve used the let keyword to declare a new variable. The let keyword allows us to create new variables like the const keyword.

However, we can reassign the value of a variable that is declared with the let keyword.

In JavaScript, we build up programs by combining declarations and statements.

๐Ÿ’ผ First test case

Learning Objectives

๐ŸŽฏ Goal: Write a test for the case below, using Jest:

Case 1 ๐Ÿ’ผ

const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";

We can create a file called get-ordinal-number.test.js and write our first test there. We can use documentation to work out how to write our first test using Jest.

get-ordinal-number.test.js

test("converts 1 to an ordinal number", function () {});

Let’s break down this syntax.

The test function is part of the Jest API, a function we use to perform a particular task. In particular, we’re using test to create a test case. Before, we could use Math.round and console.log because Math and console are provided for us by Node.

test isn’t provided by Node, but when we ask Jest to run our tests, it will make sure the test function exists and that our code can use it.

Let’s break down the arguments we’re passing to test:

  • 1st argument: "converts 1 to an ordinal number", a string which describes the behaviour we’re testing for
  • 2nd argument: function () {}, we will write some assertions in this function () {} to check the behaviour

โš–๏ธ Creating assertions

We need to write an assertion inside the body of function () {} inside get-ordinal-number.test.js

get-ordinal-number.test.js

test("converts 1 to an ordinal number", function () {});

๐Ÿ’ก tip

The assertion is the part of the test code that checks if a particular thing is true or not.

In this example, we want to check that the following is true:

We expect getOrdinalNumber(1) to be "1st"

An assertion in Jest looks like this:

expect(getOrdinalNumber(1)).toBe("1st");

The function toBe is used to check that the current output of getOrdinalNumber(1) and the target output of "1st" are equal to each other.

toBe is just one example of a function called a matcher. A matcher is a function we use to compare values in Jest.

So the whole test looks like this:

test("converts 1 to an ordinal number", function () {
  const input = 1;
  const currentOutput = getOrdinalNumber(input);
  const targetOutput = "1st";

  expect(currentOutput).toBe(targetOutput);
});

๐Ÿ‘Ÿ Running tests

We can try running the file get-ordinal-number.test.js with node in the following way:

node get-ordinal-number.test.js

but we get an error:

ReferenceError: test is not defined

This is because test isnโ€™t defined anywhere in the file.

We need to execute this file so that the Jest API is available in our file. We can do this by running the test file using Jest: we do this using an npm script.

The “scripts” section of the package.json is where we can write useful commands we’ll use in our project. We can add a “scripts” section to the package.json so that it reads as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "name": "week-4-test-example",
  "description": "An example application showing how to write tests using the jest framework",
  "scripts": {
    "test": "jest"
  },
  "devDependencies": {
    "jest": "^29.5.0"
  }
}

Finally, we’ll need to run our tests. Now we can run the command npm test.

When we execute the command, npm test, we will run npm, and npm will look inside the “scripts” section of the package.json and look up the command for “test” - in this case, “jest”. npm will then run “jest”.

๐Ÿ“ Running scripts

Learning Objectives

So far weโ€™ve seen how expressions can be evaluated using the Node REPL. The Node REPL is useful for evaluating expressions quickly.

But usually, our programs have many instructions, and we want to keep and re-run them instead of typing them out each time. So we save our instructions in files. Node can also execute instructions written in a file.

We use the node command to run a JavaScript file in the terminal. A JavaScript file ends with .js - this is the “file extension”.

Letโ€™s suppose we have a file age.js. We run the command node age.js. This terminal command is an instruction to execute the program written inside age.js. Our program has five lines. So the computer will read and execute the program one line at a time:

const yearOfBirth = 1990; // declaration
const currentYear = 2023; // declaration

currentYear++; // statement
`I am ${currentYear - yearOfBirth} years old`; // statement

activity

Check you can run a file with Node:

  1. In your terminal, create a new file called example.js.
  2. Try writing a few lines in the file.
  3. Get Node to run this file. (Don’t use the REPL now - you should run a command to execute the whole file.)

Once the computer executes these statements, the execution of the program is complete. But weโ€™re left with a problem. With the REPL, the user inputs an expression statement or declaration; the computer reads and executes the line and immediately prints feedback to the terminal. With a file, the computer will execute each line sequentially until completion without printing the values of each expression it evaluates.

So this new problem can be expressed as a question:

โ“ Problem

“How can we check what the values evaluated to in our program during execution?”

๐Ÿ“ค Returning from a function

Learning Objectives

We need a way to access the percentage string that is created inside convertToPercentage. To access values created inside functions, we write return statements ๐Ÿงถ ๐Ÿงถ return statements We write a return statement to specify a function’s return value. If your function call is like a question, the return value is the answer. It’s what comes back. .

We can add a return statement to convertToPercentage like this:

1
2
3
4
5
6
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

If we want, we could also remove the variable percentage, since we can return the value of the expression directly:

1
2
3
4
5
const decimalNumber = 0.5;

function convertToPercentage() {
  return `${decimalNumber * 100}%`;
}

๐Ÿ”Ž Checking the output

We can store a function’s return value in a variable.

const result = Math.round(10.3);
console.log(result); // logs 10 to the console

We call Math.round which takes the input 10.3 and then returns the rounded number 10. So result stores a value of 10.

Math.round is a function implemented by other developers and convertToPercentage is a function we’re implementing, but calling convertToPercentage is just like calling Math.round.

Now we want to call the function convertToPercentage and store the return value in a variable.

We can store the return value in a variable in exactly the same way:

1
2
3
4
5
6
7
8
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

const result = convertToPercentage(0.5);

Log out the value of result to the console using console.log.

1
2
3
4
5
6
7
8
9
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

const result = convertToPercentage(0.5);
console.log(result);

This will now print the following when run:

50%

๐Ÿ“ฆ Using packages

Learning Objectives

When writing software, we continually make use of software written by other developers. We can call these packages ๐Ÿงถ ๐Ÿงถ packages A package is some code which is grouped together to provide some functionality.

We use packages so that we don’t have to solve every problem ourselves. Other people have often solved some things we need to do really well. Using other people’s solutions to parts of a problem means we can focus our time and effort on what’s special about our problem.

Imagine we wanted to work out what the time is in a user’s city. Instead of writing code to work out the time for every city’s time zone (and when they change!), we can use a package some “city time” experts have written, and which they keep up to date.

Different programming languages give developers different ways of accessing packages for use in their code. We will use npm ๐Ÿงถ ๐Ÿงถ npm Node Package Manager, or npm, downloads and manages useful packages of code from the npm registry.

๐Ÿ”ญ Scope

Learning Objectives

The function convertToPercentage will only be useful if we can access the percentage string that it creates. Otherwise, we won’t be able to use the result of convertToPercentage in other parts of our code. We can try accessing the percentage variable outside the function body like this:

1
2
3
4
5
6
7
8
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
}

convertToPercentage(0.5);
console.log(percentage);

However if we run the code above, we get an error:

ReferenceError: percentage is not defined

We get an error because of scope ๐Ÿงถ ๐Ÿงถ scope Scope means where variables are and what you can access. . When we define convertToPercentage we also define a local scope - the region of code enclosed inside convertToPercentage’s function body. This region is convertToPercentage’s local scope. This means any variables we declare inside convertToPercentage’s local scope can only be accessed within this region. If we attempt to reference a variable outside the scope where it was declared, then get a ReferenceError.

๐Ÿ•› 12 vs 24 hour clock

Learning Objectives

We usually write the time in one of two ways: the analogue 12 hour clock or the digital 24 hour clock. The 12 hour clock counts up to 12: it resets at midday. The 24 hour clock counts up to 24: it resets at midnight.

Conversion from 24 hour to 12 hour ๐Ÿงถ ๐Ÿงถ Conversion from 24 hour to 12 hour
2๏ธโƒฃ4๏ธโƒฃ hour time๐Ÿ•› 12 hour time
09:0009:00 am
10:0010:00 am
11:3011:30 am
12:0012:00 pm
13:431:43 pm
14:002:00 pm

We use the notation “HH:MM”. HH is our stand-in for the hours value. MM is our stand-in for the minutes value.

๐Ÿงฉ Stating the problem

Let’s pose a problem: given any time in 24 hour clock, we want to format it as a 12 hour clock time. To achieve this goal, we’re going to implement a function formatAs12HourClock.

Given a time in 24 hour clock When we call formatAs12HourClock Then we get back a string representing the same time in 12 hour clock

๐Ÿงช Our tests:

I expect formatAs12HourClock("09:00") to be "09:00 am"
I expect formatAs12HourClock("14:19") to be "2:19 pm"

Interacting with computers

Learning Objectives

Modern computers are complicated: it would be too difficult and time-consuming to list all the components that make up a modern computer. So to build our mental model, we will use this simple definition of a computer:

A computer is a device used to store and perform operations on data.

๐ŸŽ›๏ธ Application Programming Interface

Learning Objectives

With Jest installed, we need to figure out how to use the Jest framework to write tests. This means we need to look at APIs ๐Ÿงถ ๐Ÿงถ APIs An API is a boundary between a programmer and an application, enabling a programmer to use an applicationโ€™s functionality without being concerned with how the application was built. again.

API stands for

  • Application
  • Programming
  • Interface.

We’ve encountered the word interface already.

But we can break down each word in this acronym to understand it altogether.

  • An application is a program or piece of software designed to serve some purpose.

  • Programming refers to the process of writing code or software.

  • An ๐Ÿ•น๏ธinterface is a shared boundary between two or more systems.

Weโ€™ve encountered several functions like console.log, Math.round already. console.log and Math.round are APIs.

console.log is actually implemented in a different language (C++), but that doesn’t matter - its functionality is exposed to us when we write JavaScript, and we don’t need to care how it’s actually implemented or how it works.

Jest provides an API so we can write tests. So we have to find out about the Jest API to start writing tests with Jest.

Try and list other examples of APIs you’ve used since the start of the course. Start a thread in Slack to discuss with your class.

๐Ÿ—๏ธ Assembling the parts

Learning Objectives

Earlier we defined a sub-goal to find a value for the hours from the time input. We’ve found that Number(time.slice(0,2)) is an expression that evaluates to the hours from time. So we can write an if statement using this expression:

if (Number(time.slice(0, 2)) > 12) {
}

If the time is "23:00" then the expression Number(time.slice(0, 2)) > 12 will evaluate to true and the body of the if statement will be executed.

This if statement is implementing the following part of the diagram from earlier:

flowchart TD A{Check: Are the hours greater than 12?}

Now we can format the string using our approach from earlier: weโ€™ll need to append "pm" to the string expression and subtract 12 from the hours. So we get the following:

if (Number(time.slice(0, 2)) > 12) {
  return `${time.slice(0, 2) - 12}:00 pm`;
}

The return statement above implements the following steps we set out earlier:

flowchart LR D[Step 4: subtract 12 from the hours] D --> E[Step 5: add 'pm' to the rest of the time] E --> F[Step 6: return the new time]

Now we can re-run our assertions from earlier to check our function behaves as target.

๐Ÿท๏ธ Parameterising a function

Learning Objectives

At the moment, decimalNumber is a variable in the global scope of our program:

const decimalNumber = 0.5; // defined in the global scope of our program

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

const output1 = convertToPercentage(0.5);
const output2 = convertToPercentage(0.231);

So long as decimalNumber is always in the global scope, convertToPercentage will always go to the global scope to get the value of decimalNumber.

However, we want convertToPercentage to work for any input we pass to it.

To make a function work for any number, we need to handle inputs. We do this using a parameter ๐Ÿงถ ๐Ÿงถ parameter A parameter is a special kind of variable: its value is defined by the caller. .

decimalNumber is still a variable - but as a parameter we don’t assign decimalNumber a value inside the function’s body. It is a placeholder. When we call the function, we pass an input to the function, and the value of that input is assigned to the decimalNumber parameter when the function is called. This happens automatically.

We can add a parameter decimalNumber to our function:

1
2
3
4
5
6
7
8
function convertToPercentage(decimalNumber) {
  // now decimalNumber is a parameter of convertToPercentage
  const percentage = `${decimalNumber * 100}%`;
  return percentage;
}

const output1 = convertToPercentage(0.5);
const output2 = convertToPercentage(0.231);

In the example above, we’re calling convertToPercentage twice: first with an input of 0.5 and second with an input of 0.231. In JavaScript instead of input we use the word argument ๐Ÿงถ ๐Ÿงถ argument Arguments are inputs given to a function inside (). An argument means an input. .

We’re calling convertToPercentage twice: first with an argument of 0.5 and next with an argument of 0.231.

Think of a function as a box. We put data in and then act on it using the rules in the box; at the end, the box gives us new data back. In programming we say that we pass arguments into a function, the function’s code is executed and we get a return value after the function has finished executing. Here’s a diagram:

flowchart LR A[argument] --> B{function} B --> C[return]

Here’s a diagram of what happens when convertToPercentage is passed a specific argument:

flowchart LR A[0.231] --> B{convertToPercentage} B --> C[23.1%]

In this interactive widget we have defined a parameter decimalNumber in the function declaration inside parentheses after the function name convertToPercentage. In our mental model, a function call means going to convertToPercentage and running the code inside the function.

Use the interactive widget to see what happens when the code above is executed. Pay close attention to what happens inside the convertToPercentage frame.

๐Ÿท๏ธ Saving expressions

Learning Objectives

In programming we often want to reuse our work. Consider the string: "Hello there";

Suppose we want to create different greetings for different people, like: "Hello there, Alicia" "Hello there, Barny";

We can use a variable to store this string and reuse it. How can we create a variable ๐Ÿท๏ธ ๐Ÿท๏ธ variable A variable is a label for a piece of data. We assign a piece of data to a label and then refer back to this label, in place of the data.

We can create a variable in our program by writing a **variable ** declaration ๐Ÿงถ ๐Ÿงถ declaration A declaration is an instruction that binds an identifier to a value. , like this:

const greeting = "Hello there";

Break down the different syntactic elements of this variable declaration:

  • const is a keyword used to indicate we’re creating a variable.
  • greeting is the identifier - it is used to refer to a variable.
  • = this is the assignment operator. It means assign to the label greeting the value of the expression on the right hand side.
  • "Hello there" - this is the expression whose value we’re assigning to the label greeting.

Type this variable declaration into the REPL:

const greeting = "Hello there";

Now refer to the label greeting in the REPL:

`${greeting}, Alicia`

Our greeting variable is stored in memory. We can reuse it to build more expressions:

`${greeting}, Barny`
greeting
Store your string in a variable and reuse it

We just used backticks to create a template literal.

`A template literal places ${expressions} inside strings;

With template literals, we can insert expressions into strings to produce new strings. Any time we want to reference a variable inside a template literal we use a dollar sign $ and a set of curly braces (). We can put any expression (e.g. a variable name) inside the curly braces. The value that expression evaluates to is then placed inside the string.

When an operation uses an expression, that expression is immediately evaluated, and how it was written is forgotten about. That means that the greetAlicia variable is the same in all three of these cases:

const greetAlicia = "Hello there, Alicia";

๐Ÿ“ note

In this example, we don’t use a variable or an expression to create a string. Instead we write a string "Hello there, Alicia".

A sequence of characters enclosed in quotation marks is called a string literal. "Hello there, Alicia" is a string literal.

const name = "Alicia";
const greetAlicia = `Hello there, ${name}`;
const greeting = "Hello there";
const name = "Alicia";
const greetAlicia = `${greeting}, ${name}`;

The greetAlicia variable doesn’t remember whether you used variables to make it or not - in all three cases, greetAlicia contains the string "Hello there, Alicia". Once a value is made, it doesn’t matter how it was made.

๐Ÿ•น๏ธ Using an interface

Learning Objectives

We want to use computers without understanding exactly how they are built. Every day we ask machines to do things, and usually we have no idea how these machines work. We could not use modern technology if we had to understand it completely before we could use it; it would take too long! Instead we use interfaces ๐Ÿงถ ๐Ÿงถ interfaces Think of an interface as a gate that allows communication between a user and a machine. The user asks the machine to do things via the interface.

Think about a cash machine (ATM). We go to a hole in the wall with a screen and a keypad. The screen and the keypad are the user interface. We press the buttons and ask the machine to do things - like giving our balance, or withdrawing some money from an account.

exercise

Define the user interface for these devices:

  • a calculator
  • a microwave
  • a desktop lamp
  • Facebook
  • Alexa
  • ChatGPT

๐Ÿ–ฅ๏ธ Terminal interface

Learning Objectives

Programmers need interfaces to ask computers to do things. A computer terminal is an interface where programmers can issue commands to a computer. Because users enter text instructions and receive text output, we say that the terminal is a text-based interface.

Interface via the terminal

We can input a command into the prompt and hit enter. The terminal then passes this command to the computer to execute. Find your own terminal and input the ls command:

ls
terminal
The terminal is a window on the computer, prompting users for instructions.

๐Ÿ–Š๏ธ Writing computer instructions

We can issue commands to the computer using the terminal. These commands are instructions that the computer knows how to interpret.

The computer knows ls means “list the files and directories in the current directory”.

During the execution of a computer program, a computer will store and modify data ๐Ÿงถ ๐Ÿงถ data Data is information. Text, images, numbers are all forms of data. The data in a executing program is sometimes called the state. A computer program will modify data with operations ๐Ÿงถ ๐Ÿงถ operations Operations modify or create data, from the current data in the program. Adding numbers, joining words, changing text to ALLCAPS, are all operations.

ls is a shell command. Shell is a programming language we use to interact with the files and folders on our computer. You already know at least two more programming languages. Can you name them?

definition

A programming language is a limited set of rules for writing computer instructions.

๐Ÿ–จ๏ธ Logging

Learning Objectives

Printing to the terminal

To look at values when our program runs, we can use a function called console.log.

๐Ÿ’ก tip

console usually means a text interface like a terminal. A log is a written record of something that happened.

So console.log will record something that happens in our program and print it to a text based interface.

console.log logs the result of expressions while our program is executing. This is very useful for complex programs when we need to check what values expressions evaluate to at specific moments of our program execution.

Let’s see how to use console.log . In a file called example.js, write the name of the function console.log.

console.log;

If we run this file with Node, we won’t be able to see anything in the terminal. As with Math.round we need to use the syntax for calling a function. Add brackets after the function name:

console.log("hello there!");

We should see the string "hello there!" logged out in the terminal.

๐Ÿ–ผ๏ธ Testing frameworks

Learning Objectives

To help us think about the requirements of getOrdinalNumber, let’s consider one case:

๐Ÿ’ผ Case 1

const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";

Case 1 states that when getOrdinalNumber is called with an input of 1, it has a target output of “1st”. Our first step is to check that getOrdinalNumber works as we have stated.

We have used console.assert to write assertions to check our code before, but console.assert is limited. Now we will write tests to check our code is behaving in a particular way.

๐Ÿ”‘ A test is any piece of code that runs an assertion on the code we’re testing

We want our tests to:

  • be easy to write
  • be easy to read
  • to give clear feedback on what the current output is
  • to give clear feedback on what the target output is
  • easily write multiple test cases

To help us build test cases like this, we will use a test framework ๐Ÿงถ ๐Ÿงถ test framework A test framework is a set of tools we can use to build tests efficiently.

discussion

We can use a short dialogue to think about why we want to use a testing framework:

๐Ÿง‘๐Ÿฝ BรผลŸra
Ali, looks like I need to implement a function.
๐Ÿง‘๐Ÿฟ Ali
Cool. How are you going to check it works?
๐Ÿง‘๐Ÿฝ BรผลŸra
I’m going to use tests to check that the function gives the target output as described in the specification
๐Ÿง‘๐Ÿฟ Ali
Seems wise. How are you going to write a lot of tests efficiently?
๐Ÿง‘๐Ÿฝ BรผลŸra
I’m going to use a testing framework to write test cases quickly. The framework will make sure that the tests give fast, reliable feedback.

๐Ÿ—„๏ธ Classifying data

Learning Objectives

Weโ€™re going to focus on the JavaScript programming language.

A programming language organises data with rules so we understand what we can and cannot do with it. Languages split data up into different categories called data types ๐Ÿงถ ๐Ÿงถ data types A data type is a grouping of data with some particular properties . In JavaScript, we have five data types. We will look first at numbers and strings.

Number data type

10 is an example of the number data type. 3.14 is also part of the number data type; non-integers are a type of number.

String data type

A string is a sequence of characters demarcated by quotes.

"Code Your Future";

๐Ÿงฎ Creating expressions

Think of the numbers 10 and 32. We could ask questions about these numbers, like: What is the sum of 10 and 32?

Another way to say this is what do 10 and 32 add up to?. In English we can say this in many ways, but in JavaScript we can say this using numbers and an operator. Just like in mathematics, โ€œthe sum of 10 and 32โ€ can be written as 10 + 32:

10 + 32

In JavaScript, + is an operator ๐Ÿงถ ๐Ÿงถ operator An operator represents an operation, or act. It’s a symbol. In this example, + represents the operation โ€œmake the sum of the numbersโ€. It symbolises addition.

The combination of symbols 10 + 32 is an expression ๐Ÿงถ ๐Ÿงถ expression An expression is a value or any valid combination of values and symbols that results in a single value. We say that expressions evaluate to a single value. So we say that 10 + 32 evaluates to the value 42.

10 is also an expression. It evaluates to the value 10.

๐Ÿ—„๏ธ Generalising further

Learning Objectives

In English, ordinal numbers mostly follow the same pattern.

Numbers ending in 1 will generally have an ordinal number ending in “st”.

Here are some examples of this pattern,

1st, 11th, 21st, 31st, 41st,…

All the numbers ending in 1 will continue to end in "st", with the exception of 11. 11 is slightly different and ends with a "th".

We can now update our test case to check that getOrdinalNumber works for lots of different numbers ending in 1.

get-ordinal-number.test.js

1
2
3
4
5
6
7
8
9
function getOrdinalNumber() {
  return "1st";
}

test("works for any number ending in 1", function () {
  expect(getOrdinalNumber(1)).toBe("1st");
  expect(getOrdinalNumber(11)).toBe("11th");
  expect(getOrdinalNumber(21)).toBe("21st");
});

We’ve also updated the test description because we’re adding more assertions and checking slightly different functionality.

exercise

Try implementing getOrdinalNumber so it passes the test case above.

๐Ÿ—„๏ธ Generalising further

Learning Objectives

๐Ÿงฐ Handling outliers

We can now implement functionality for getOrdinalNumber.

Our strategy might be something like this:

flowchart LR A{Check is num 11} -- true --> B[return 11th] A -- false --> C[return num + st]

Most of the time we just need to return the number with “st” on the end.

However, 11 is an outlier: it doesn’t conform to this pattern.

So our current strategy for this test case will be to check if the number is 11 first and do something differently ( return "11th" ): otherwise we return the default value of num with "st" on the end.

Here’s the implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function getOrdinalNumber(num) {
  if (num === 11) {
    return "11th";
  }
  return `${num}st`;
}

test("works for any number ending in 1", function () {
  expect(getOrdinalNumber(1)).toBe("1st");
  expect(getOrdinalNumber(11)).toBe("11th");
  expect(getOrdinalNumber(21)).toBe("21st");
});

ย ๐Ÿงญ Future strategies

Now, we’ve handled any numerical inputs ending in 1. We can try to build up functionality for any number ending in 2.

We can start by adding a test case that only asserts something about the input of 2.

We cannot add this assertion to the first test case. The first test case checks for inputs that end in a 1. To check the case when the input ends in 2, we need to introduce a new test case.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function getOrdinalNumber(num) {
  if (num === 11) {
    return "11th";
  }
  return `${num}st`;
}

test("works for any number ending in 1", function () {
  expect(getOrdinalNumber(1)).toBe("1st");
  expect(getOrdinalNumber(11)).toBe("11th");
  expect(getOrdinalNumber(21)).toBe("21st");
});

test("converts 2 to an ordinal number", function () {
  expect(getOrdinalNumber(2)).toBe("2nd");
});

Check the test output

Here’s the test feedback for the test above:

second-case-fail

Play computer with getOrdinalNumber when it is called with an input of 2 Double check you agree with the test feedback in this case.

Before coding, outline a strategy for handling the second test case.

  exercise</span>

Try updating the second test case to check getOrdinalNumber works for other numerical inputs ending in 2.

We know that this doesn’t solve all cases (e.g. it will give the wrong answer for getOrdinalNumber(2)), but it’s a start, and we have a test-case showing that it works in one case.

This points out a limitation of tests. They only test the cases we wrote tests for. Right now, all our tests are passing, but we know our solution doesn’t work for all inputs!

In order to generalise our solution (to make it work “in general” rather than just for one specific case), It’s important to think about what different groups of inputs we may expect.

๐Ÿง  Forming sub-goals

Learning Objectives

For formatAs12HourClock our strategy for inputs like "23:00" involves checking if the hours value is less than 12. For this purpose, we can use the greater than comparison operator >.

> will check if the value on the operator’s left side is less than the value on the operator’s right side.

So 3 > 12 evaluates to false, as 3 is not greater than 12.

So provided we have an expression for hours, we can write an if statement as follows:

if (/* here goes an expression here that evaluates to the hours */ < 12) {
  // do code to format the 12 hours
}

To complete the logic, we can form a sub-goal ๐Ÿงถ ๐Ÿงถ sub-goal A sub-goal is a goal for a smaller problem that makes up some bigger problem .

Any time we’re solving a problem, we can define a goal - a thing we need to achieve to consider the problem solved. We can break a problem into smaller problems, each with its own sub-goal. The problem-solving process involves continually breaking down problems into smaller manageable problems, each with its own sub-goal.

For the implementation of formatAs12HourClock, we can form a sub-goal as follows:

๐ŸŽฏ Sub-goal: Find the hours value from the time input

๐Ÿงฉ Percentages

Learning Objectives

Let’s begin with this problem:

Given a decimal number I want to convert it into a percentage format.

For example, given the decimal number 0.5 we return the string "50%". Given the decimal number 0.231 we return the string "23.1%".

Restating the problem

Our function must convert any decimal to a percentage. We have used functions already. Here are some functions we’ve used:

1
2
console.log("hello world"); // logs "hello world" to the console
Math.round(3.141); // evaluates to 3

All these expressions are function calls: weโ€™re passing input ("hello world" or 3.141) to the functions (console.log or Math.round) to use their functionality. Math.round and console.log are functions that the JavaScript language designers have written and stored inside the language, because everyone needs them all the time.

No such pre-built function converts any number to a percentage, so we must write our own. We’re going to create a function called convertToPercentage with the following requirements:

Given a number input When we call convertToPercentage with the number input Then we get back a string representing the percentage for that

Here are some examples:

1
convertToPercentage(0.5); // should return "50%"
1
convertToPercentage(0.231); // should return "23.1%"

Useful expressions

It is often helpful to solve a problem in one specific instance before doing it for all cases.

In programming, we always try the simplest thing first. Letโ€™s consider how to convert just one number to a percentage. Look at this variable declaration:

1
const decimalNumber = 0.5;

We want to create an expression for the percentage using the value of decimalNumber. To convert to a percentage, we will multiply the number by 100 and then add a "%" sign on the end.

1
2
const decimalNumber = 0.5;
const percentage = `${decimalNumber * 100}%`;

Recalling template literals, the expression in the curly braces will be evaluated first and then inserted into the string, giving us the percentage string.

Now that we’ve solved the problem of converting a single decimal number to a percentage, let’s practice solving other similar problems using expressions.

Create a new JavaScript file so that you can try running the code for yourself.

Calculating the area and perimeter of a rectangle

In one of these new files, let’s make two variables that describe the dimensions of a rectangle:

const height = 10; // 10 is just an example of a value here - your code should still work if you change this to another value.
const width = 30; // Also just an example - your code should still work if this changes.

Using these variables, let’s calculate the area and perimeter of the rectangle.

We can calculate the area and perimeter by creating expressions that use the height and width variables we just created. Hint: read the links above if you don’t remember how to calculate area and perimeter of a rectangle.

Finally, we’ll create two more variables: area and perimeter to store the result of the calculations.

const area = FILL_ME_IN;
const perimeter = FILL_ME_IN;

Now, if we change the numbers assigned to height and width, are the area and perimeter values still correct? Try using console.log to print out the value of the variables and then run the script using Node to view the output.

Remember to create a new JavaScript file to run the code for yourself.

Converting pence to pounds

Like the rectangle example, we’ll start by creating a variable to store a price in pence:

const price = 130; // Just an example value. Try changing this value to 0, 10, or 1521, and make sure you still get the right answer from your code.

Now, you should write an expression that calculates the price in pounds. The price in pounds should be shown with 2 decimal places and start with “ยฃ”.

Try using console.log to print out the value of price in pounds and then run the script using Node to view the output.

๐Ÿงฎ Ordinal numbers

Learning Objectives

๐Ÿข Let’s imagine you’re working in a 10 storey office building. There are 10 different levels. We need a way to describe each level of the building. We start on the ground floor of the building - level with the ground. We use an ordinal number to describe the other levels in the building.

To form the ordinal number we take a number and add the correct suffix ๐Ÿงถ ๐Ÿงถ suffix The suffix comes from the word used to describe each number, like first, second, third etc.

โ˜๐Ÿฟ Up from the ground floor, we are then on the 1st floor (first floor) โ˜๐Ÿฝ Up from the 1st floor, we are on the 2nd floor (second floor)

number+ suffix= ordinal number
1st1st
2nd2nd

What will the ordinal number be for:

a) 21? b) 40? c) 49?
d) 13?

Use ordinal numbers to write the days of the month for the following events:

a) Tomorrow b) A week from now c) Easter Sunday 2024 d) When is Eid expected to occur in 2024

  1. 1st
  2. 2nd
  3. 3rd
  4. 4th
  5. 5th
  6. 6th
  7. 7th
  8. 8th
  9. 9th
  10. 10th

๐Ÿ“‹ Specification

Let’s consider a function called getOrdinalNumber that needs to work like this:

  • it takes one argument - a whole number, like 1, 2, 3, etc
  • it returns a string that represents the ordinal number
getOrdinalNumber(1); // returns "1st";
getOrdinalNumber(2); // returns "2nd";
getOrdinalNumber(6); // returns "6th";

The requirements above form a specification ๐Ÿงถ ๐Ÿงถ specification A specification is a set of requirements for how a piece of software should behave. . Now we have a specification for how the function should work we can create many cases showing how we expect the function getOrdinalNumber to behave when it is called with different inputs.

๐Ÿงฐ Install a UNIX based operating system

Learning Objectives

If you get stuck on any of the below or above instructions, please post in your class channel on Slack.

You probably already have this if you have done previous modules.

โš ๏ธ warning

Code Your Future does not support Windows

If you have a Mac or Linux machine already, you already have a UNIX based operating system. All CYF-supplied laptops run Mac OS or Linux. If you have your own machine and it runs Windows, you should already have set up a Linux partition.

If you have still not done this, you must do it now. We cannot support learners using Windows. It takes too much time from everybody else. If you need help doing this, post in Slack, or bring your laptop to a CYF co-working space to get support. It’s normal to need help with this process.

Dual Boot on Windows

๐Ÿงฐ Install Node with nvm

Learning Objectives

If you get stuck on any of the below or above instructions, please post in your class channel on Slack.

๐Ÿ’ก tip

Check if you already have NodeJS installed by running node -v in a terminal. The command should return a version number. If it does, you can skip the next steps.

๐Ÿง On Ubuntu

  1. Install nvm by running the following commands in your terminal:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
  1. After the installation is complete, you’ll need to source the nvm script by running:
source ~/.bashrc
  1. Install the latest LTS version of Node.js by running:
nvm install --lts
  1. Check that you have successfully installed Node.js by running:
node -v

You should see a version number like v20.16.0.

  1. Check that you have successfully installed npm by running:
npm -v

You should see a version number like 10.5.0.

๏ฃฟ On Mac

  1. Install nvm by running the following command in your terminal:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
  1. After the installation is complete, you’ll need to source the nvm script by running:
source ~/.zshrc
  1. Install the latest LTS version of Node.js by running:
nvm install --lts
  1. Check that you have successfully installed Node.js by running:
node -v

You should see a version number like v20.16.0.

  1. Check that you have successfully installed npm by running:
npm -v

You should see a version number like 10.5.0.

๐Ÿ’ก tip

Using nvm allows you to easily install and manage multiple versions of Node.js on your system. This will help you access projects that use older versions of Node.js.

๐Ÿงถ Accessing strings

Learning Objectives

Given a time string we need to access the first 2 characters of the string which represent the hours.

Strings are zero-indexed. Index means position, so zero-indexed means we start counting character positions from 0 onwards.

Here are the positions/indexes for "23:00"

index01234
character"2""3"":""0""0"

In JavaScript, we can use square bracket notation to access specific characters in the string using the index.

time[0]; // evaluates to "2"
time[1]; // evaluates to "3"
time[2]; // evaluates to ":"
// etc

Square bracket access will only give a single character. We must use another method to extract multiple characters from the given string.

โœ‚๏ธ Extracting a slice

To extract 1 or more characters from a string, we can use a function called slice ๐Ÿงถ ๐Ÿงถ slice slice is a function that can take 2 arguments: a start index and an end index. slice will return a section of the string from the start index up to but not including the end index.

time; // holds the value "23:00"
time.slice(0, 2); // will access the characters below
index01
character"2""3"

So time.slice(0,2) will evaluate to "23" when the time is "23:00". Finally we must convert "23" to the number 23, otherwise we can’t compare this value properly. We can use the Number function to convert the string into a number.

Number(time.slice(0, 2)); // evaluates to 23

๐Ÿงน Improving the code

Learning Objectives

Refactoring

Now the assertions pass: in other words, our functionโ€™s current output matches with the target output described in the assertions.

In addition to implementing functionality, we also need to continually improve the code quality. Other developers will continue to read our code so it’s vital our code is readable by other humans.

๐Ÿ’ก tip

The process of updating our code quality (without changing the implementation) is called refactoring.

Let’s consider our working implementation so far: Currently, weโ€™re using the same expression twice: Number(time.slice(0, 2)). This means weโ€™re calling the functions Number and slice twice.

Additionally, expressions embedded inside curly braces and parentheses can often be difficult to read. In this situation it makes sense to label the recurring expression so we can reuse it wherever we need to in our code.

Letโ€™s create a variable called hours and assign to it our expression’s result.

function formatAs12HourClock(time) {
  const hours = Number(time.slice(0, 2));

  if (hours > 12) {
    return `${hours - 12}:00 pm`;
  }
  return `${time} am`;
}

Note that the function’s behavior hasn’t changed: it still returns the same outputs from the given inputs. We’ve just improved the implementation without changing the underlying behaviour.

๐Ÿ› Fixing bugs

Here is our current implementation of formatAs12HourClock:

function formatAs12HourClock(time) {
  const hours = Number(time.slice(0, 2));

  if (hours > 12) {
    return `${hours - 12}:00 pm`;
  }
  return `${time} am`;
}

However, formatAs12HourClock currently has a bug ๐Ÿงถ ๐Ÿงถ bug Any unintended behaviour or effect from our software is called a bug.

function formatAs12HourClock(time) {
  const hours = Number(time.slice(0, 2));

  if (hours > 12) {
    return `${hours - 12}:00 pm`;
  }
  return `${time} am`;
}

a) Write an assertion to check the output of formatAs12HourClock when it is called with an input "17:42" b) Check the assertion output and try to explain what the bug is

Once you’ve established the bug in the code, try removing the bug by updating the implementation of formatAs12HourClock.
Once you’ve changed the implementation to formatAs12HourClock, re-run all the assertions to check you’ve not broken any previous functionality.

๐Ÿงพ Evaluating expressions

Learning Objectives

๐Ÿ’ก tip

Computers work by storing and performing operations on data.

Computer programs are built from many expressions. We must understand how expressions are evaluated to understand how computer programs are executed.

We can take an expression like 36 * 45 and ask what it evaluates to. If we know what the * operator represents (multiplication) and if we understand the arithmetic rules represented by the operation we can evaluate this expression ourselves.

Happily, computers can evaluate expressions for us.

NodeJS is an application that runs JavaScript programs. In other words, NodeJS can understand and execute programs written in JavaScript. One feature of Node is the REPL.

info

REPL is a special type of program that stands for:

Read - Users enter some code that Node will read Evaluate - Node will then evaluate this code Print - Node will print the result to the terminal Loop - Node will loop back to the beginning and prompt users to input some more code

With a REPL we can run pieces of code and look at what happens.

We input JavaScript instructions that are then executed by NodeJS. The REPL replies with, or prints out, the result of this execution.

Type each of the following expressions into the REPL one at a time and then press enter to check the result.

10 + 32
32 / 10

  activity</span>

In this activity, you’ll check you’re ready to use the Node REPL on your machine.

  1. Open the terminal on your computer
  2. Check you’ve got Node installed on your computer
  3. Start the Node REPL in your terminal
  4. Enter the expressions and evaluate them using the Node REPL

Note: If you don’t know how to do any of the steps above, then try searching for an appropriate command online; searching for things when you’re stuck is super important part of being a developer!

  activity</span>

Create your own expressions and enter them into the Node REPL.

Before hitting enter, predict what the REPL output will be. Write your prediction down and compare it to the outcome.

๐Ÿช„ Declaring functions

Learning Objectives

๐Ÿ’ก tip

Functions are reusable blocks of code.

To create a function, we can use a function declaration. A function declaration looks like this:

1
function convertToPercentage() {}

The function declaration consists of the following syntactic elements:

  • function keyword, begins the function declaration
  • convertToPercentage - names the function
  • () - any input to the function will go between these round braces (our function above doesn’t take any input (yet), but it still needs the ()s)
  • {} - the body of the function is written inside the curly braces (our function above doesn’t do anything yet, but it still needs the {}s)

We can create a function declaration by wrapping up the percentage variable and the expression for the percentage inside the function.

1
2
3
4
5
const decimalNumber = 0.5;

function convertToPercentage() {
  const percentage = `${decimalNumber * 100}%`;
}

At the moment decimalNumber is not wrapped up inside the body of the function. In the following sections, we will explore what happens when this is the case.

๐Ÿช„ Functions

Learning Objectives

Now, instead of adding or multiplying numbers, weโ€™ll consider 10.3.

๐Ÿค” “What is the nearest whole number to 10.3?”

The process of finding the nearest whole number to a decimal number is called rounding. So we could rephrase our question as:

๐Ÿค” “What does the number 10.3 round to?โ€

โ™ป๏ธ Reusing instructions

There is no operator for rounding the number 10.3 in JavaScript. But we will want to round numbers again and again. We should use a function ๐Ÿงถ ๐Ÿงถ function A function is a reusable set of instructions. .

Math.round is a function. Because a function is a reusable set of instructions, Math.round rounds any number.

Functions usually take inputs and then apply their set of instructions to the inputs to produce an output.

  1. Write Math.round in the Node REPL
  2. Hit enter to evaluate our expression

The REPL output [Function: round] is telling us Math.round is a function.

๐Ÿ–ผ๏ธ

๐Ÿ“ฒ Calling a function

For our function to work, we need Node to read the instructions and execute ๐Ÿงถ ๐Ÿงถ execute Execution means the computer reads and follows instructions. them. Write the following in the REPL:

Math.round(10.3);

Notice the ( and ) brackets after the name of the function and a number inside the brackets. These brackets mean we are calling the function. The number inside the brackets is the input we’re passing to the function.

๐Ÿ“ note

Calling a function means telling the computer to read the function’s instructions and carry out its instructions. When calling a function we can also pass inputs to the function.

Math.round(10.3) is a call expression; read this as:

“apply the set of instructions for Math.round to the number 10.3.”

If we type Math.round(10.3) then we get the result 10. So we say that Math.round(10.3) returns 10.

A call expression is an expression which evaluates to the value returned by the function when it is called. So the expression Math.round(10.3) evaluates to the value 10.

If we assign that expression to a variable, or use it in a string, we’ll get the value 10. So we can write:

const roundedValue = Math.round(10.3);

or we can write:

const roundedValueInAString = `10.3 rounds to ${Math.round(10.3)}`;

Both of these instructions evaluate the call expression Math.round(10.3) to the returned value 10 as soon as the call expression appears. The variable roundedValue will have a numeric value 10 (just like if we’d written const roundedValue = 10;), and the variable roundedValueInAString will have a string value "10.3 rounds to 10".