# 7. Arrays

{% hint style="info" %}
Follow along with code examples [here](https://github.com/The-Marcy-Lab-School/1-1-1-arrays)!
{% endhint %}

**Table of Contents**

* [Array Basics](#array-basics)
  * [Arrays Are Mutable](#arrays-are-mutable)
  * [Array Methods for Adding and/or Removing Values](#array-methods-for-adding-andor-removing-values)
* [Reference vs. Primitive Values](#reference-vs-primitive-values)
  * [How Reference Types (Arrays and Objects) are Stored in Memory](#how-reference-types-arrays-and-objects-are-stored-in-memory)
  * [Mutability vs. Reassignment](#mutability-vs-reassignment)
  * [Impure and Pure Functions](#impure-and-pure-functions)
  * [Making Copies of Arrays to Make Pure Functions with the Spread Syntax](#making-copies-of-arrays-to-make-pure-functions-with-the-spread-syntax)
  * [Copying Array Challenge](#copying-array-challenge)
* [Advanced Array Syntax](#advanced-array-syntax)
  * [2D Arrays](#2d-arrays)
  * [Destructuring Assignment and Rest Operator](#destructuring-assignment-and-rest-operator)

## Array Basics

* Arrays are lists of data (order matters). Values in an array are called **elements of the array**.
* Arrays use square brackets `[]` to encapsulate the data
* Arrays are a type of object
* Like strings arrays also...
  * have indexes starting at `0`
  * use bracket notation to access individual elements: `array[index]`
  * have a `.length` property that returns the number of elements in the array
  * have many of the same read-only methods like `slice`, `indexOf` and `includes`
* Unlike strings, arrays are mutable.

```js
const friends = ['bert', 'ernie', 'big bird', 'kermit', 'miss piggy', 'elmo'];
console.log(`typeof on an Array returns ${typeof friends} because Arrays are Objects!`);

console.log(`I have ${friends.length} friends`);
console.log(`My best friend is ${friends[2]}`);
console.log(`My first three friends are ${friends.slice(0, 3)}`)
console.log('But here are all of my friends:');

for (let i = 0; i < friends.length; i += 1) {
  // friends[i] holds the "current" friend that we're looking at in this iteration of the loop
  console.log(friends[i]);
}
```

**Challenge: This function can verify whether or not a given array `arr` contains a given value `value`. For each numbered comment below, add a comment explaining the logic of the function!**

```js
const hasValue = (arr, value) => {
  for (let i = 0; i < arr.length; i++) { // 1.
    if (arr[i] === value) { // 2.   
      return true; // 3.
    }
  }
  return false; // 4.
}

const letters = ['a', 'b', 'c', 'd'];
console.log(hasValue(letters, 'c')); // Prints true
console.log(hasValue(letters, 'e')); // Prints false
```

<details>

<summary><strong>Check out this example</strong></summary>

```js
const hasValue = (arr, value) => {
  for (let i = 0; i < arr.length; i++) { // Loop through every index in the array
    if (arr[i] === value) { // Check each element of arr to see if it matches the value   
      return true; // If it does, we can immediately return true
    }
    // if it doesn't, keep going!
  }
  return false; // If we make it to the end of the loop, we must not have found it. Return false.
}

const letters = ['a', 'b', 'c', 'd'];
console.log(hasValue(letters, 'c')); // Prints true
console.log(hasValue(letters, 'e')); // Prints false
```

</details>

### Arrays Are Mutable

Strings have read-only methods like `toUpperCase()` and `slice()` that make a copy of the string but don't change the original string.

```js
const myName = 'ben';
myName.toUpperCase(); // doesn't mutate the string, it makes a copy
myName[0] = "J"; // doesn't even do anything
console.log(myName);  // still 'ben'
```

Unlike strings, arrays let you **mutate** them directly (change their contents without reassignment).

```js
const endLetters = ["x", "y", "z"];
endLetters[1] = "foo";
console.log(endLetters); // ["x", "foo", "z"]
```

We can even modify the `.length` of an Array to empty them:

```js
endLetters.length = 0; // endLetters now has no values
console.log(endLetters); // []
```

<details>

<summary><strong>Question: How are we allowed to modify the array if it is stored in a <code>const</code> variable?</strong></summary>

`const` prevents us from reassigning the variable. Notice that we never reassign `endLetters`! The variable still references the same array, we're just changing the contents of the array.

</details>

### Array Methods for Adding and/or Removing Values

Since arrays are mutable, they not only have read-only methods like `slice`, `indexOf`, and `includes`, they also have methods for adding to and removing from the array.

These methods let you add values to an array

* `arr.push(value)` — adds a given value to the end of an array, increasing the length by 1
* `arr.unshift(value)` — adds a given value to the front of an array, increasing the length by 1

These methods let you remove values from an array

* `arr.pop()` — removes a given value from the end of an array, reducing the length by 1
* `arr.shift()` — removes a given value from the front of an array, reducing the length by 1

Splice lets you add and remove values to and from an array all at once!

* `arr.splice(index, qtyToRemove, valuesToAdd)` — removes or replaces elements in an array and/or adds new elements to an array

```js
const letters = ['a', 'b', 'c', 'd', 'e'];

letters.push('f'); // adds 'f' to the end of letters
letters.unshift('z'); // adds 'z' to the beginning of letters
console.log(letters); // Prints ['z', 'a', 'b', 'c', 'd', 'e', 'f']

letters.pop(); // removes the last element ('f')
letters.shift(); // removes the first element ('z')
console.log(letters); // Prints ['a', 'b', 'c', 'd', 'e']

letters.splice(2, 0, 'Hi!'); // At index 2, removes no elements and inserts 'Hi!' 
console.log(letters); // Prints ['a', 'b', 'Hi!', 'c', 'd', 'e']

letters.splice(2, 1, 'Hey ;)'); // At index 2, replaces 1 element with 'Hey ;)'
console.log(letters); // Prints ['a', 'b', 'Hey ;)', 'c', 'd', 'e']

letters.splice(2, 2, 'Nope', 'Bye!'); // At index 2, replaces 2 elements with 'Nope' and 'Bye!'
console.log(letters); // Prints ['a', 'b', 'Nope', 'Bye!', 'd', 'e']
```

## Reference vs. Primitive Values

Arrays and Objects are considered **reference types**. Let's see why.

### How Reference Types (Arrays and Objects) are Stored in Memory

When a variable is created, a chunk of your computer's memory is assigned to hold some data. Primitive values are small enough to be stored in memory as-is. Arrays and objects (a.k.a "reference types") however can grow to be any size and therefore cannot be contained within a single memory slot.

Instead, the data in arrays and objects are stored in an area called the **heap** and a **reference to their heap address** is stored in the variable instead.

{% embed url="<https://docs.google.com/presentation/d/13d9TXTogmt4hV9DLuHzARExbcu3A7Nr9dnY69Zz5fKw/embed?start=false&loop=false&delayms=3000>" %}

As a result, we can mutate the contents of an array without reassigning the variable because the variable doesn't hold the array, it holds a reference to the array!

### Mutability vs. Reassignment

Consider the code below, what will the value of `nums` and `clone` be after the program runs?

```js
const nums = [1,2,3];
const clone = nums;
clone[1] = 20;

console.log(nums);  // [1,20,3]
console.log(clone); // [1,20,3]
```

Both `nums` and `clone` will hold the values `[1,20,3]`. Why?

Again, this is because arrays are reference types because:

* When we assign `clone = nums`, the "value" of `nums` is the **reference** to the array `[1,2,3]`, not the array itself. So, `clone` also holds a reference to the exact same array
* When we mutate `clone[1]`, we are mutating the array referenced by `clone` which is the same array referenced by `nums`. So, both `nums` and `clone` return the mutated array.

The same thing happens when we invoke a function and provide a reference type as an argument:

```js
const emptyTheArray = (arr) => {
  // 3. Upon receiving the reference, this function modifies array
  arr.length = 0;
}

// 1. The array below is created in the heap and the "reference" to its location is stored in letters
const letters = ['a', 'b', 'c'];

// 2. When we invoke emptyTheArray, we assign the "reference" to the function parameter "arr"
emptyTheArray(letters);

// 4. The array referenced by letters has been modified by the function!
console.log(letters); // Prints []
```

It is impossible for this behavior to occur when dealing with primitive values like strings, numbers, and booleans because they are immutable.

```js
let x = 10;
let y = x;
y++; // shorthand for y = y + 1
```

In this example, even though it *looks* like we're mutating the value `y`, we are NOT. We're reassigning `y` to store a completely different value (`11`).

### Impure and Pure Functions

Functions are considered **impure functions** if they:

1. Produce different outputs when given the same inputs
2. Produce side effects (like mutating incoming values)

The functions below are impure functions:

```js

// This function can't return the same output each time it is invoked.
const rollDie = () => {
  return Math.ceil(Math.random() * 6);
}
console.log(rollDie()); // ???
console.log(rollDie()); // ???


let count = 0;
// This function has the side effect of modifying the count variable which is outside of its scope
const incrementBy = (x) => {
  count += x;
  return count;
};
console.log(incrementBy(2)); // 2
console.log(incrementBy(2)); // 4
console.log(incrementBy(2)); // 6
console.log(count); // 6


// This function mutates the incoming arr value
const emptyTheArray = (arr) => {
  arr.length = 0;
}

const letters = ['a', 'b', 'c'];
emptyTheArray(letters):

console.log(letters); // Prints []
```

### Making Copies of Arrays to Make Pure Functions with the Spread Syntax

Functions that accept Arrays and modify them are impure. To make a function that modifies an Array pure, we need to make a copy of it first. The most effective way to do this is using the spread syntax: `[...arr]`

```js
const extend = (arr, value) => {
  const newArr = [...arr, value];
  return newArr;
}

const letters = ['a', 'b', 'c'];
const moreLetters = extend(letters, 'd');

console.log(letters); // Prints ['a', 'b', 'c']
console.log(moreLetters); // Prints ['a', 'b', 'c', 'd'];
```

### Copying Array Challenge

Make this impure array function pure!

```js
const shorten = (arr) => {
  arr.pop();
  return arr;
}
```

## Advanced Array Syntax

### 2D Arrays

An Array can contain other Arrays! When the inner arrays contain values, we call this a "2-Dimensional (2D)" Array or a **"matrix"**.

The matrix below has 2 columns and 3 rows:

```js
const coordinates = [
  [30, 90],
  [40, 74],
  [34, 118],
  [42, 88],
  [39, 77]
];

const newOrleans = coordinates[0];
const newOrleansLat = coordinates[0][0];
const newOrleanLong = coordinates[0][1];

console.log(newOrleans);
console.log(newOrleansLat);
console.log(newOrleanLong);
```

When accessing a 2D array, the first index references the "row" and the second index references the "column"

### Destructuring Assignment and Rest Operator

The **destructuring assignment** syntax is a JavaScript expression that makes it possible to "unpack" values from arrays (or properties from objects) into distinct variables.

The syntax uses `[]` on the left side of an assignment operator `=` to define which values to unpack from the Array on the right side (the "source variable");

```js
const coordinates = [
  [30, 90],
  [40, 74],
  [34, 118],
  [42, 88],
  [39, 77]
];

// Unpack the first three arrays from coordinates (we are choosing to ignore the rest)
const [newOrleans, newYork, losAngeles] = coordinates;
console.log(newYork);

// Unpack the two values from the newYork array.
const [newYorkLat, newYorkLong] = newYork;

console.log(newYorkLat);
console.log(newYorkLong);
```

In the example above, we only unpacked the first 3 values of `coordinates`.

If we want to, we can use the "rest" operator (`...rest`) to store the remaining unpacked values in a variable:

```js
const [, , ...lesserCities] = coordinates;

console.log(lesserCities);
/* 
[ 
  [34, 118],
  [42, 88],
  [39, 77]
];
*/
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://marcylabschool.gitbook.io/marcy-lab-school-docs/mod-1-javascript-fundamentals/6-arrays.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
