# Arrays

Arrays, as mentioned [before](https://github.com/nushackers/wiki/blob/main/orbital/javascript/datatypes.md) are any ordered set of values separated by commas and enclosed in square brackets (`[]`).

```js
let arr = [1, 2, 3, 4];
let emptyArr = [];
let nestedArrs = [[1, 2, 3], [4, 5], [6]];
let jumbledArr = [2, true, false, "2.43", [4, ["hello"], []]];
```

{% hint style="info" %}
When creating an array, it is possible to split it into multiple lines. Type the opening square bracket and hit `enter`; your cursor will go to the next line. Then, enter a value, put a comma, and hit `ctrl` + `enter` (or `cmd` + `return` on mac). The console will go to the next line. Keep doing this until you have entered all the values needed, then navigate to the closing square bracket and hit `enter` to execute the line.
{% endhint %}

As with strings, you can access a particular element in an array using square bracket indexes:

```js
let arr = [1, 2, 3, 4];
arr[0]; // 1
arr[2]; // 3
```

These indexes can be chained to access particular elements in nested arrays:

```js
let nestedArr = [
    [1, 2, 3, 4],
    [5, 6, 7],
    [8, 9]
]
nestedArr[0]; // [1, 2, 3, 4]
nestedArr[0][1]; // 2
nestedArr[2][1]; // 9
```

You can also reassign values in an array in this way:

```js
let nums = [1, 2, 3, 4];
nums[2] = 10;
nums; // [1, 2, 10, 4]
```

The length of an array is accessible through its `length` property:

```js
arr.length; // 4
nestedArr.length; // 3
nestedArr[2].length; // 2
```

## Adding values to an array

To add values to the end of an array, there are 2 ways. The first way is to assign values using indexing.

```js
let arr = []; // empty array
arr[0] = 1
arr; // [1]
arr[1] = 2;
arr; // [1, 2]
```

In general, `arr[arr.length] = x` will add `x` to the end of the array.

It is possible to use the wrong index an accidentally reassign a value. It is also possible to pass in an index beyond the length of an array. In this case, JavaScript will create "gaps" in the array filled with the value `undefined`:

```js
let arr = [1, 2, 3];
arr[4] = 4;
arr; // [1, 2, 3, undefined, 4]
```

The second way to add arrays prevents any chance of using the wrong index, and involves the `push` method.

```js
let arr = [1, 2, 3];
let l = arr.push(4);
arr; // [1, 2, 3, 4]
l; // 4
```

The method takes in one argument and pushes it to the back of the array. It also returns the new length of the array after insertion. `push` is a [variadic function](https://wiki.nushackers.org/orbital/readme-1/functions), so you can push multiple values at once:

```js
let arr = [];
let l = arr.push(1, 2, 3, 4, 5, 6);
arr; // [1, 2, 3, 4, 5, 6]
l; // 6
```

{% hint style="warning" %}
There is no easy way to insert elements into a specific position in the array. One way is to copy over the elements into a new array, making sure to insert the new value you want to at the right index, and then use the new array. Another way is to use the [`splice` method](https://www.w3schools.com/jsref/jsref_splice.asp), as detailed in [this StackOverflow post](https://stackoverflow.com/questions/586182/how-to-insert-an-item-into-an-array-at-a-specific-index).
{% endhint %}

## Removing values from an array

The best way to remove elements from an array is to use the `pop` method. It is a nullary function that removes the last element in the array and returns the element it removed.

```js
let nums = [1, 2, 3, 4];
let x = nums.pop();
nums; // [1, 2, 3]
x; // 4
```

{% hint style="warning" %}
Again, it is not easy to remove a specific element; the `splice` and `indexOf` methods will be needed.
{% endhint %}

## Searching for an element

To check if an element exists in an array, the `indexOf` method is useful. It returns -1 if the element is not present, otherwise it returns the index of the element.

```js
let arr = [1, 2, 3, 4];
arr.indexOf(3); // 2
arr.indexOf(23); // -1
```

{% hint style="warning" %}
`indexOf` will not work when passing in another array as an argument:

```js
let arr = [[1, 2, 3], [4, 5, 6]]; // 2D nested array
arr.indexOf([1, 2, 3]); // returns -1 even though [1, 2, 3] is an element
```

This is because `indexOf` uses the `===` operator, which returns `false` for two arrays even if they have the same elements unless they refer to the same object:

```js
let arr1 = [1, 2, 3];
let arr2 = [arr1, [4, 5, 6]];
arr2.indexOf(arr1); // returns 0 now
```

{% endhint %}

## Array sliceing

To get a particular portion of an array, you can use the `slice` method. It takes in a `start` and `end` and returns all the elements from the `start` index to just before the `end` index:

```js
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.slice(2, 5); // [3, 4, 5]
```

Omitting the `end` value will slice till the end of the array:

```js
arr.slice(4); // [5, 6, 7, 8, 9]
```

If the `end` value is greater than or equal to the `start` value then it returns an empty array:

```js
arr.slice(6, 2); // []
```

If only one argument `k` is provided and the argument is negative, then the *last* `|k|` elements are returned:

```js
arr.slice(-4); // [6, 7, 8, 9]
```

## Array sorting

There are two ways to sort an array. The first is the `sort` method. It takes no arguments, has no return value and sorts the array it is called on:

```js
let nums = [3, 4, 6, 1, 2, 5];
nums[3]; // 1
nums.sort();
nums; // sorted to [1, 2, 3, 4, 5, 6]
nums[3]; // 4
```

The second way is to use the `toSorted` method. This does not change the original array and instead returns a new sorted array:

```js
let nums = [3, 4, 6, 1, 2, 5];
let sortedNums = nums.toSorted();
nums; // still [3, 4, 6, 1, 2, 5]
sortedNums; // [1, 2, 3, 4, 5, 6]
```

Both these methods sort the elements of the array according to their natural ascending order as defined by JavaScript. To sort an array in descending order, you can call one of the `sort` or `toSorted` methods and then reverse the array.

## Reversing an array

Like with sorting, there are two ways to reverse the ordering of elements in an array. The first is the `reverse` method, which works like the `sort` method (i.e. it changes the original array):

```js
let nums = [1, 2, 3, 4, 5, 6];
nums[3]; // 4
nums.reverse();
nums; // [6, 5, 4, 3, 2, 1]
nums[3]; // 3
```

The second way is to use the `toReversed` method which is similar to the `toSorted` method (i.e. it does not modify the original array and returns a new one instead):

```js
let nums = [1, 2, 3, 4, 5, 6]
let reversedNums = nums.toReversed();
nums; // still [1, 2, 3, 4, 5, 6]
reversedNums; // [6, 5, 4, 3, 2, 1]
```

To sort an array in descending order, combine the sorting and reversing methods:

```js
let nums = [3, 5, 1, 2, 6, 4];
nums.sort()
nums.reverse();
nums; // now [6, 5, 4, 3, 2, 1]
```

## Iterating through an array

A `for` or `while` loop can be used to iterate through the elements of an array and perform an action. For this next bit let's try to go through the elements of an array `nums` and add the squares of each element to a new array. Below is the code for this:

```js
let nums = [1, 2, 3, 4, 5];
let squaredNums = []
for (let i = 0; i < nums.length; i++) {
    squaredNums.push(nums[i] ** 2);
}
squareNums; // [1, 4, 9, 16, 25]
```

But there is a better way of doing this. Arrays have a `forEach` method that takes in a unary function and applies it to every element in the array. It is common to use lambda expressions and anonymous functions for this. The above code can be shortened to:

```js
let nums = [1, 2, 3, 4, 5];
let squaredNums = [];
nums.forEach(x => squaredNums.push(x ** 2));
squaredNums; // [1, 4, 9, 16, 25]
```

{% hint style="warning" %}
If the unary function passed in to `forEach` returns any value, the value is ignored and "lost" i.e. cannot be used by any code you write
{% endhint %}

## More methods

There is a full list of array methods [here](https://www.w3schools.com/jsref/jsref_obj_array.asp); this section would be far too long if I tried covering them here.

## Next steps

Now we're done covering the basics of JavaScript. The next section will move on to HTML, followed by CSS, and then back to JavaScript where we'll combine the 3 to make a functional frontend.
