๐Ÿง‘๐Ÿพโ€๐Ÿ’ป prep

๐Ÿ’พ Grouping data

Learning Objectives

In programming, we often have related pieces of data.

Let’s consider a list of prices in a bill:

4.6, 5.03, 7.99, 8.01

limitations of many variables

We can store this list of prices in a JavaScript program by declaring multiple variables:

const price0 = 4.6;
const price1 = 5.03;
const price2 = 7.99;
const price3 = 8.01;

Each identifier is the word price with a numerical suffix to indicate its position in the list. However, this is undoubtedly the wrong approach.

  • If the number of items in the bill is huge, we must keep declaring new variables.
  • If the number of items changes, we must reassign the values of variables so they’re in the correct order, and change any place we’re using the variables to know about the new one.
  • If we do mutliple things to all of the values (say we have one loop adding them, and one loop printing them), we will need to change all of the places any time we add new values.

Instead we have to group the data together using a data structure ๐Ÿงถ ๐Ÿงถ data structure A data structure is a collection of data. It may have functions that can be applied to access or manipulate the data.

๐Ÿ“œ Arrays

Learning Objectives

In JavaScript, we can store data inside an array ๐Ÿงถ ๐Ÿงถ array An array is an ordered list of data

Instead of writing:

const item0 = 4.6;
const item1 = 5.03;
const item2 = 7.99;
const item3 = 8.01;

We can declare an array literal as follows:

const items = [4.6, 5.03, 7.99, 8.01];

Notice the identifier for the array is items. We chose to use the plural word items instead of the singular item, because arrays can store multiple pieces of information.

ordered data

๐Ÿ’ก Recall

Zero-indexing means we start counting from 0
We’ve already encountered ordered data before. A string is an ordered collection of characters. Let’s recall an example of a string:

const volunteer = "Moussab";

The character "M" is at index 0, "o" is at index 1, and so on.

As with strings, arrays are also zero-indexed in a similar way:

const items = [4.6, 5.03, 7.99, 8.01];

So we can refer to the elements ๐Ÿงถ ๐Ÿงถ elements An element is another name for an item inside an array. of the array by an index.

index0123
element4.65.037.998.01

In JavaScript, we can use square bracket notation to access specific elements in the array using an index.

items[0]; // evaluates to 4.6
items[1]; // evaluates to 5.03
items[2]; // evaluates to 7.99
// etc

๐Ÿ“Š Calculating the mean

Learning Objectives

Let’s consider a problem where we calculate the mean of a list of numbers.

Given an array of numbers
When we call calculateMean with the array of numbers
Then we get the mean.

Let’s create a test to check its functionality. In your prep dir, touch mean.js && touch mean.test.js. Write the following test in the mean.test.js file.

test("calculates the mean of a list of numbers", () => {
  const list = [3, 50, 7];
  const currentOutput = calculateMean(list);
  const targetOutput = 20;

  expect(currentOutput).toBe(targetOutput); // 20 is (3 + 50 + 7) / 3
});

In this test, we’re checking we get a value of 20 by adding together 3 + 50 + 7 and then dividing by the number of items (3). We calculate the mean of a list of numbers by:

  1. summing all the numbers in the array
  2. dividing the sum by the length of the array

We can define a ๐ŸŽฏ sub-goal of calculating the sum of all numbers in the list.

โž• Summation

Learning Objectives

๐ŸŽฏ Sub-goal: compute the sum of an array of numbers.

To sum a list we can start by creating a variable total with an initial value of 0.

We then need to repeatedly add each value in the list to our total.

function sumValues(list) {
  let total = 0;
  total += list[0]; // access a list element and add to total
  total += list[1];
  total += list[2];
  total += list[3];
  total += list[4];
  return total;
}

sumValues([1, 2, 3, 4, 5]);

However, this approach is flawed.

Explain why the approach above is flawed when it comes to summing the numbers for an array of any length.

๐Ÿ” Iteration

Learning Objectives

To solve the sub-goal, we have to repeatedly add each number in the array to the total, one at a time. In programming, the process of repeating something is called iteration.

In programming, we can iterate by using a loop ๐Ÿงถ ๐Ÿงถ loop A loop is a sequence of instructions that is continually repeated until some condition is reached. .

In particular, we can use a for...of statement to sum the elements of the array.

function calculateMean(list) {
  let total = 0;
  for (const item of list) {
    total += item;
  }
}

๐Ÿ“Š Calculating the median

Learning Objectives

Let’s define another problem.

We want to calculate the median value from an array of numbers.

Given an array of numbers in ascending order
When we call calculateMedian with this array
Then we get the median value.

We calculate the median of a list of numbers by finding the middle value in the list.

Let’s start with a test to check the return value of calculateMedian given an ordered list of numbers. In your prep dir, touch median.js && touch median.test.js. Write the following test in the median.test.js file.

test("calculates the median of a list of odd length", () => {
  const list = [10, 20, 30, 50, 60];
  const currentOutput = calculateMedian(list);
  const targetOutput = 30;

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

๐Ÿ”จ Implementing calculateMedian

So we can implement calculateMedian.

We can summarise our approach as follows.

flowchart LR A[Step 1: Find the middle index of the array] --> B[Step 2: Get the middle item] B --> C[Step 3: Return the middle item]

In code we can we can use splice to to get the middle item.

function calculateMedian(list) {
  const middleIndex = Math.floor(list.length / 2);
  const median = list.splice(middleIndex, 1)[0];

  return median;
}

๐Ÿงฑ Assembling the parts

Learning Objectives

Now suppose we have a program where we use the functions we implemented earlier:

const salaries = [10, 20, 30, 40, 60, 80, 80];
const median = calculateMedian(salaries);
const mean = calculateMean(salaries);

console.log(`The median salary is ${median}`);
console.log(`The mean salary is ${mean}`);

Predict and explain what will get printed to the console when the code above runs.

Then run the code above on your local machine to check your prediction. Does your initial explanation now make sense?

(Note: you’ll have to declare the functions somewhere too)

๐Ÿ› Finding the bug

In the code above, the median value is correct: however, the mean is incorrect.

We can add a log to the program to identify the origin of the bug.

1
2
3
4
5
6
7
8
const salaries = [10, 20, 30, 40, 60, 80, 80];
const median = calculateMedian(salaries);

console.log(salaries, "<--- salaries input before we call calculateMean");
const mean = calculateMean(salaries);

console.log(`The median salary is ${median}`);
console.log(`The mean salary is ${mean}`);

Run it

Try re-running the code above with the additional log. What does this tell you?

To understand why this bug occurs, we need to explore more concepts.

๐Ÿค References

Learning Objectives

Arrays are stored by reference ๐Ÿงถ ๐Ÿงถ reference A reference points to a location in memory.

Consider the following example,

const list = [10, 20, 30];
const copy = list;
copy.push(60, 70);

console.log(list);
console.log(copy);

Let’s break down what is happening in this program.

Play computer with the code above to step through the code and find out what happens when the code is executed.

point-to-array

  • We make an array [10, 20, 30] and store it somewhere in memory.
  • list is assigned a reference to the location in memory containing [10, 20, 30]
  • copy is assigned a reference pointing at the same location in memory as list

At this stage in the program, list and copy point to the same location in memory.

  • push function mutates (changes) the array that copy points to.
  • prints out list: [10, 20, 30, 60, 70]
  • prints out copy: [10, 20, 30, 60, 70]

So as copy and list point to the same array. If we mutate list then we’re mutating the same list that copy points to.

So the console output is the same.

1
2
3
4
5
6
7
8
const salaries = [10, 20, 30, 40, 60, 80, 80];
const median = calculateMedian(salaries);

console.log(salaries, "<--- salaries input before we call calculateMean");
const mean = calculateMean(salaries);

console.log(`The median salary is ${median}`);
console.log(`The mean salary is ${mean}`);

In the example above, salaries is assigned a reference on the first line. Explain why calculateMedian and calculateMean both get access to the same array.

Shared reference

We can also check these variables share the same reference.

const list = [10, 20, 30];
const copy = list;

console.log(list === copy); // logs true

If we’re comparing 2 array variables with ===, then it will evaluate to true only if they have the same reference. === is comparing the references to the arrays, not the contents of arrays.

If we made two different arrays with the same contents, they would not be === equal:

const list = [10, 20, 30];
const copy = [10, 20, 30];

console.log(list === copy); // logs false

Value vs reference

In JavaScript, arrays and objects are reference types: everything else is a value type.

๐Ÿ“ Passing by value

As strings are value types, they are passed by value. Passed by value means that every time you assign a value to a variable then a copy of that value is made.

Use the tabs below to compare the effects of passing by reference and passing by value. There are two different but similar implementations of pluralise - a function that appends an s to the end of its input.

Here pluralise is passed an array by reference.

lettersInAnArray is passed by reference. pluralise’s modification is visible here, because the same underlying storage was modified.

Step through the code to observe this behaviour:

Here pluralise is passed a string by value.

This means a copy of string’s value is passed to pluralise in the second tab. pluralise’s reassignment is not visible here, because a copy was made just for the function before the value was modified.

Step through the code to observe this behaviour:

๐Ÿ”€ Mutation

Learning Objectives

Let’s take another look at our earlier implementation of calculateMedian:

function calculateMedian(list) {
  const middleIndex = Math.floor(list.length / 2);
  const median = list.splice(middleIndex, 1)[0];

  return median;
}
const salaries = [10, 20, 30, 40, 60, 80, 80];

const median = calculateMedian(salaries);
// At this point, the array referenced by salaries has been mutated after calculateMedian(salaries), and a reference to the same array is given to calculateMean
const mean = calculateMean(salaries);

console.log(`The median salary is ${median}`);
console.log(`The mean salary is ${mean}`);

calculateMedian gets the middle value by calling splice. However, splice is a mutating ๐Ÿงถ ๐Ÿงถ mutating For arrays, mutation means changing the contents of an array. This could mean changing a value at some index or removing an item altogether. array method.

When we call splice it does 2 things:

  • removes the specified item from the list
  • returns the removed item

splice modifies the array: however, calculateMean is also passed a reference to the same array too.

In other words, calculateMedian modifies the same array that is passed to calculateMean.

Play computer with the example above. Pay careful attention to what happens when salaries is passed to calculateMedian

โš ๏ธ Side effects

Learning Objectives

Currently calculateMedian mutates its input - the array of numbers. This mutation is called a side effect ๐Ÿงถ ๐Ÿงถ side effect A function has a side effect if it does something which can be observed from outside of the function (aside from returning a value). Removing an element from an array is a side effect. Logging something to the console is also a side effect. .

In this case, the side effect has unintended consequences. We have introduced a bug ๐Ÿงถ ๐Ÿงถ bug Any unintended behaviour or effect from our software is called a bug. which makes calculateMean return the wrong value. Both calculateMean and calculateMedian need access to the original salaries array. Therefore, we should take make sure we don’t mutate the array unless we really mean to.

Testing no mutation

We can add an additional assertion to the tests for calculateMedian to check it isn’t modifying the original input:

test("doesn't modify the input", () => {
  const list = [1, 2, 3];
  calculateMedian(list);

  expect(list).toEqual([1, 2, 3]); // Note that the toEqual matcher checks the values inside arrays when comparing them - it doesn't use `===` on the arrays, we know that would always evaluate to false.
});

In this test, we don’t check the return value of calculateMedian. We assert that the input has the same contents as the original input. We can use the toEqual matcher to check the contents of the array referenced by the variable list.

Recall the current buggy implementation of calculateMedian:

function calculateMedian(list) {
  const middleIndex = Math.floor(list.length / 2);
  const median = list.splice(middleIndex, 1)[0];

  return median;
}

We’ve established that we shouldn’t use splice to retrieve the median from the input array. Fix the implementation of calculateMedian above so it no longer calls splice (which mutates the input), and instead gives the right answer without mutating the input.

๐Ÿ“ˆ Implementing all the cases

Learning Objectives

Try writing a test case to check calculateMedian works in the case when it is passed an array of even length.

Use documentation to check how the median is computed in this case.

Once you’ve written your test case for calculateMedian, hopefully you see this implementation isn’t doing the right thing. Try implementing the functionality for this case.

Fail Fast Prep

Learning Objectives

Introduction

Failing fast is crucial for identifying flaws and giving you a quick opportunity to change anything that is needed.

Embracing failure as a learning opportunity accelerates growth and adaptation and is a skill that makes you a more efficient professional.

Failing fast

๐ŸŽฏ Goal: Learn about failing fast (20 minutes)

  • Read this piece that will introduce you to the concept of failing fast and how this can be put into practice by using the Agile framework.
  • Watch this video about failure and how you learn and innovate.

Reflect on your experience of Failing Fast

๐ŸŽฏ Goal: Reflection on failing fast concept in your life (20 minutes)

Considering what you have learned regarding Failing Fast, think of an instance when you failed to achieve something you aimed for.

Write down about this event and reflect on it.

You can use the following questions to guide you.

  • What did you learn from it?
  • Did you try a different approach to achieve the same goal?
  • Did you change the objective you were after?
  • What would you have done differently?