# Arrays and methods

### Array

Two ways of creating empty array

```javascript
let arr = new Array();
let arr = [];
```

Create an array with elements in them

```javascript
let arr = ["Apple", "Orange", "Plum"];
```

Array in JavaScript is heterogeneous, you can store element of any type in them.

<p class="callout info">`arr.length` property to get the number of element in array if you used it properly. Otherwise, if you insert item into an array with a huge gap like below</p>

```javascript
let arr = [];
arr[999] = 59;

console.log(arr.length); // 1000, it is the index of the last element + 1
```

##### arr.at()

Normally, if you index an array you can only use positive indexing, i.e. `[0..length - 1]`. You can use negative index with the `arr.at` method.

##### pop/push

Use `pop` to remove element from the end of the array

Use `push` to insert element to the end of the array. You can insert multiple items by providing them in the parameter.

Treat array as a stack, first in last out data structure.

##### shift/unshift

Use `shift` to dequeue the first element from the head of the array

Use `unshift` to add element to the head of the array.

These two operations are slow takes `O(n)` because it requires shifting the array after removing or adding the element.

##### Queue

To use an array as a queue, use the `push` and `shift` function to dequeue and enqueue element into the queue.

### Looping over array

You can use a traditional index loop

```javascript
let arr = ["Apple", "Orange", "Pear"];

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}
```

But if you only need to loop over the element without the indices then there is a shorter syntax

```javascript
let fruits = ["Apple", "Orange", "Plum"];

// iterates over array elements
for (let fruit of fruits) {
  console.log(fruit);
}
```

<p class="callout warning">Remember although you can use `for ... in` loop for array, it is not optimized for looping over array, so it will be slower. In addition, you will be looping over extra properties that is in array. So it is not recommended to use `for ... in` for array iterations, only for object property iterations.</p>

### toString()

Array object implements the `toString()` method to return a comma separated value of String

```javascript
let arr = [1, 2, 3];

arr.toString() // returns "1,2,3"
```

Array object doesn't have `Symbol.toPrimitive` nor a valid `valueOf`, so they only implement `toString` as a catch all object to primitive conversion.

`[]` will become `""`

`[1]` will become `"1"`

`[1, 2]` will become `"1,2"`

### Comparison of array using ==

Remember that `==` comparison does type conversion if the type are different, and with objects it will be converted to a primitive, and in this case because array only implemented `toString()` it will be converted to a String. Which results in some really interesting comparison.

With `==` if you are comparing two object, it will only be `true` if they are referencing the same object.

The only exception is `null == undefined` is `true` and nothing else.

```javascript
[] == [] // false, because they are different object when you created an empty array
[0] == [0] // false, again different object
```

```javascript
0 == [] // true, because the empty array gets converted to '', and '' is converted to 0 which is true
'0' == [] // false, since empty array converts to '', those two strings are different
```

<p class="callout success">So how do we actually compare array? Use a loop to compare item-by-item</p>

### Array methods

##### arr.splice  


```javascript
arr.splice(start[, deleteCount, ele1, ..., elemN])
```

The splice method will modify `arr` starting from index `start`, it removes `deleteCount` elements and then inserts `elem1, ..., elemN` at their place. Returns the array of removed elements.

```javascript
let arr = ["Dusk blade", "Eclipse", "Radiant virtue", "Moonstone"];

console.log(arr.splice(1, 2)); // Prints out ["Eclipse", "Radiant virtue"] they are removed
console.log(arr); // Prints out ["Dusk blade", "Moonstone"]
```

##### arr.slice

```javascript
arr.slice([start], [end])
```

The slice method will return a new array copying all the item from index `start` to `end` not including `end`.

##### arr.concat

```
arr.concat(arg1, arg2...)
```

Create a new array that includes values from other arrays and additional items.

`args` can be either an array, in which it will include every items, or it can be elements themselves which will also be appended into the array

##### indexOf, includes

- `arr.indexOf(item, from)`: Looks for `item` starting from index `from`, and return the index where it is found, if it cannot find it return `-1`
- `arr.includes(item, from)`: Looks for `item` starting from index `from`, and return `true` if found

The comparison for `indexOf` and `includes` uses `===` strict equality checking. So if you are looking for let's say `false`, you won't be accidentally finding `0`, since `==` equality checking does type conversion.

##### arr.find  


If we are looking for an object with specific condition, we can use `arr.find`. This is very similar to `find` in Ruby as well. We will pass in an anonymous function into `find`, and it will find the first element that satisfy the condition, i.e. that returns `true`.

When you are writing the anonymous function, if you don't need all of the arguments, it is okay to write your anonymous function without it. Since if the `find` will pass all three of those parameter into the anonymous function, but if your function doesn't take it it is okay.

```javascript
function foo(a, b) {
    console.log(a, b);
}

foo(1, 2, 3); // Calling it with more argument won't matter. Extra arg are ignored
foo(1); // Calling it with less arg is fine too, rest will just be undefined
```

Example of using `arr.find`:

```javascript
let result = arr.find(function(item, index, array) {
	// if true is returned, item is returned and iteration is stopped
    // if no true is return, undefined is returned
});
```

There are other variant to find `arr.findIndex` if you are interested in finding the index of the element you are looking for instead of the element.

`arr.findLastIndex` to search right to left.

##### arr.filter

Very similar syntax to `arr.find`, but instead of searching for only one element, this method will look for all element that satisfies the condition, i.e. that returns `true` and return the result in an array.

##### arr.map

Again very similar syntax as well to `find` and `map`, this time you are transforming each element of the array into something else. The function will be responsible for collecting all of the element. You just need to return the element after it is transformed

```javascript
let nums = [1, 2, 3, 4, 5];// Using multi-line arrow function, to practice returning.console.log(nums.map((ele) => {    let result = ele * 2;    return result;}));
```

##### arr.sort

Sorts the array in place. However, by default if you don't provide a comparer function it will be sorting it lexicographically, even if the elements are other type. It will attempt to convert the type into String and then sort it accordingly.

```javascript
let numArr = [1, 2, 15];

numArr.sort(); // This will modify numArr to be [1, 15, 2]! Because lexicographically 2 comes after 15!
```

In order to fix this we will have to write our own comparer function.

```javascript
function compareNumeric(a, b) {
	if (a > b) return 1; // a comes after b
    if (a == b) return 0; // they are equal return 0
    return -1; // a comes before b
}

let numArr = [1, 2, 15];
numArr.sort(compareNumeric); // now this will modify numArr to be [1, 2, 15];
```

Actually, you can even simplify this even further by just writing

```javascript
arr.sort((a, b) => a - b); // Ascending
arr.sort((a, b) => -(a - b)); // Descending
```

##### arr.reverse

Just reverse the order of elements in place.

```javascript
let arr = [1, 10, 4, 2];
arr.reverse(); // arr is now [2, 4, 10, 1];
```

##### split and join

Works just like how it work in Python, but with a minor differences.

```javascript
let names = "Ricky Xin Rek'sai"

let arr = names.split(' '); // arr is ["Ricky", "Xin", "Rek'sai"]
```

`split()` need you to specify a space `' '` if your delimiter separating the different words by a space. In addition, if you provide in an empty String `''` as delimiter, it will split the String into array of single letters.

`join()` requires you to call it on the array that you are joining together, and you provide the String to join all the element together by as a parameter

```javascript
let strs = ["Ricky", "Xin", "Rek'sai"];

let out = strs.join("-"); // Becomes "Ricky-Xin-Rek'sai"
```

##### reduce/reduceRight

The basic syntax for using `reduce/reduceRight` is as follows:

```javascript
let value = arr.reduce(function(accumulator, item, index, array), initial);
```

- `accumulator`: The result of previous function call, on the first element it is equal to `initial` if it is provided
- `item`: The current array item
- `index`: The current array item's index
- `array:` The array that you call `reduce` on
- `initial`: if the initial is provided then `accumulator` will be assigned that value and start the iteration at the first element. **Otherwise, if `initial` is not provided, then `accumulator` will take the value of the first element of the array and start the iteration at the 2nd element**

How `reduce` work is that the function will be applied to every element, the function will return a result, and that result is passed to the next element as `accumulator`. The first parameter basically becomes the storage area for storing the overall results.

The easiest example is to use this to sum up all the elements in a number array:

```javascript
let nums = [1, 2, 3, 4, 5];

console.log(nums.reduce((sum, ele) => sum + ele), 0); // Will return 15
```

You can do more complicated logic with multi-line arrow function. You just need to return the result as the accumulator for the next function call.

##### Array.isArray

You can check if an object is an array by using this function to check whether the parameter passed is an array or not. This is needed because `typeof` doesn't distinguish between `object` and `array`.