umma.dev

Algorithms + Data Structures: Arrays

I’m going to attempt to teach you (and myself) how Algorithms and Data Structures are weaved into web applications!

More often than not, Algorithms and Data Structures are taught, described and learned in a highly theoretical way. It’s also common for Algorithms and Data Structures to be taught using languages such as Java, Python of C++. I understand why these languages are used; syntax, built-in functionality, helper functions etc. In these posts I would like to be able to show how these algorithms and data structures are used within web applications.

So to kick off the series, I’m starting with simplest one, Arrays!

Array Structure

An array is a structure that can store different types of values, from strings to characters to integers. Arrays start with an index of zero and each index has a value assigned to it.

An example of an array:

Let’s say you have a group of student’s test scores in random order of 23, 49, 20, 10, 10, 2. Here the size of the array is 6. All arrays start with index 0, which means the size of the index is 5.

So what does this mean in terms of code?

There are a few ways to implement an array, below is the simplest way in JavaScript.

let myArray = [] // this is an empty array
let testScores = [23, 49, 20, 10, 10, 2] // here are the student test scores

console.log(testScores[0]) // 23
console.log(testScores[5]) // 2

Within an array, you can different types of values, and you can also have nested arrays known as double matrix arrays.

let randomArray = ['Bob', 29, 0.123, 'A']
let doubleMatrixArray = [[1,2], 3, [4,5], 6, [7,8], 9, 10]

console.log(doubleMatrixArray[0]) // [1,2]
console.log(doubleMatrixArray[6]) // 10
console.log(doubleMatrix[0][1]) // 2

Note: when obtaining values in a double matrix array, the first is for the outer array and the second for the inner array.

Array Methods

array.at()

The at() method takes in a value and returns the item in the array at the given index. You can use both positive and negative values.

let arr = [1, 2, 3, 4, 5]

console.log(arr.at(1)) // 2
console.log(arr.at(-1)) // 5

arr.at(n) is equivalent to arr[n] (when n is positive)

array.concat()

This is one of a few ways of merging two or more arrays together. The existing arrays do not change; a new array is created.

let arrOne = [1, 2, 3, 4, 5]
let arrTwo = [6, 7, 8, 9, 10]
let arrThree = arrOne.concat(arrTwo)

console.log(arrOne.concat(arrTwo)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(arrThree) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

More Than Two Arrays

let arrOne = [1, 2, 3]
let arrTwo = [4, 5, 6]
let arrThree = [7, 8, 9]

let concatNos = arrOne.concat(arrTwo, arrThree)

console.log(concatNos) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Concat Values Not in Array

let arr = [1, 2, 3]
let concatVals = arr.concat(4, 5, 6)

console.log(concatVals) // [1, 2, 3, 4, 5, 6]

Nested Arrays

let arrOne = [1, 2, 3]
let arrTwo = [5, [6,7], 8, [9, 10]]

let concatNest = arrOne.concat(arrTwo)

console.log(concatNest) // [1, 2, 3, 5, [6, 7], 8, [9, 10]]
array.copyWithin()

Shallow copies part of an array into another location in the same array and returns the existing array, length remains unchanged.

copyWithin(target)
copyWithin(target, start)
copyWithin(target, start, end)
let arr = [1, 2, 3, 4, 5]

// copy to index 1 all elements from index 2 to the end
console.log(arr.copyWithin(1,2)) // [1, 3, 4, 5, 5]

// using negative numbers
console.log(arr.copyWithin(-1)) // [1, 2, 3, 4, 1]

// empty slots
let newArr = [1, , 3, 4, 5]
console.log(newArr.copyWithin(1,2)) // [1, 3, 4, 5, 5]
array.entries()

Returns a new Array Iterator object, this contains the key/value pairs for each index in the array.

let arr = [1, 2, 3, 4, 5]
let getArrEntry = arr.entries()

for(const entry of getArrEntry) {
  console.log (entry)
}

// [0, 1]
// [1, 2]
// [2, 3]
// [3, 4]
// [4, 5]
array.every()

Tests whether all element in the array pass the conditions within a function. Returns a Boolean.

// Arrow function
every((element) => { /* … */ } )
every((element, index) => { /* … */ } )
every((element, index, array) => { /* … */ } )

// Callback function
every(callbackFn)
every(callbackFn, thisArg)

// Inline callback function
every(function(element) { /* … */ })
every(function(element, index) { /* … */ })
every(function(element, index, array){ /* … */ })
every(function(element, index, array) { /* … */ }, thisArg)
const moreThanTen = (currVal) => currVal < 10

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

console.log(arr.every(moreThanTen)) // false
array.fill()

Changes all values in the array to a static value and returns the modified array.

fill(value)
fill(value, start)
fill(value, start, end)
  • value: value to fill the array with
  • start: start index (default is 0)
  • end: end index (default is arr.length)
const arr = [1, 2, 3, 4]

console.log(arr.fill(6)) // [6, 6, 6, 6, 6]
console.log(arr.fill(0, 1)) // [1, 0 , 0, 0]
console.log(arr.fill(0, 1, 2)) // [1, 0 , 3, 4]
array.filter()

Creates a shallow copy of a protion of an array, which is filtered to the elements that pass the conditions in a function.

The current array is not altered, a new array is returned.

// Arrow function
filter((element) => { /* … */ } )
filter((element, index) => { /* … */ } )
filter((element, index, array) => { /* … */ } )

// Callback function
filter(callbackFn)
filter(callbackFn, thisArg)

// Inline callback function
filter(function(element) { /* … */ })
filter(function(element, index) { /* … */ })
filter(function(element, index, array){ /* … */ })
filter(function(element, index, array) { /* … */ }, thisArg)
const arr = [5, 10, 15, 20, 25]
const result = arr.filter(a => a > 10)

console.log(result) // [15, 20, 25]

Example: Finding all prime numbers in an array

const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const isPrime = (num) => {
  for(let i = 2; num > i; i++) {
    if(num % i === 0) return false
  }
  return num > 1
}

console.log(arr.filter(isPrime)) // [2, 3, 5, 7]

Example: Searching in an array

const words = ["hello", "world", "test", "search"]

const filterWords = (arr, search) => {
  return arr.filter((word) => word.toLowerCase().includes(search.toLowerCase()))
}

console.log(filterWords(words, 'te')) // ['test']
array.find()

Returns the first element in the array that passes the conditions set in the function. If no values pass the conditions set, undefined is returned.

// Arrow function
find((element) => { /* … */ } )
find((element, index) => { /* … */ } )
find((element, index, array) => { /* … */ } )

// Callback function
find(callbackFn)
find(callbackFn, thisArg)

// Inline callback function
find(function(element) { /* … */ })
find(function(element, index) { /* … */ })
find(function(element, index, array){ /* … */ })
find(function(element, index, array) { /* … */ }, thisArg)
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const find = arr.find(element => element > 5)

console.log(find) // 6

Example: Find an object in array by a property within the object

const data = [
  {
    name: "test one",
    age: 50
  },
  {
    name: "test two",
    age: 100
  }
]

const theTest = data.find(({name}) => name === "test one")

console.log(theTest) // { name: 'test one', age: 50 }
array.findIndex()

Returns the index of the first element in the array which passes the provided conditions within a function. if no element is able to pass the conditions, -1 is returned.

// Arrow function
findIndex((element) => { /* … */ } )
findIndex((element, index) => { /* … */ } )
findIndex((element, index, array) => { /* … */ } )

// Callback function
findIndex(callbackFn)
findIndex(callbackFn, thisArg)

// Inline callback function
findIndex(function(element) { /* … */ })
findIndex(function(element, index) { /* … */ })
findIndex(function(element, index, array){ /* … */ })
findIndex(function(element, index, array) { /* … */ }, thisArg)
const arr = [2, 4, 6, 7, 8, 10]
const bigNo = (element) => element > 6

console.log(arr.findIndex(bigNo)) // 3
array.findLast()

Iterates the array in reverse order and returns the value of the first element that passes the conditions within a function. If no elements pass the conditions, undefined is returned.

// Arrow function
findLast((element) => { /* … */ } )
findLast((element, index) => { /* … */ } )
findLast((element, index, array) => { /* … */ } )

// Callback function
findLast(callbackFn)
findLast(callbackFn, thisArg)

// Inline callback function
findLast(function(element) { /* … */ })
findLast(function(element, index) { /* … */ })
findLast(function(element, index, array){ /* … */ })
findLast(function(element, index, array) { /* … */ }, thisArg)
const arr = [5, 10, 15, 20, 25]
const found = arr.findLast((element) => element > 13)

console.log(found) // 25
array.findLastIndex()

Iterates the array in reverse order and returns the index of the first element that passes the conditions within a function. If no elements pass the conditions, -1 is returned.

// Arrow function
findLastIndex((element) => { /* … */ } )
findLastIndex((element, index) => { /* … */ } )
findLastIndex((element, index, array) => { /* … */ } )

// Callback function
findLastIndex(callbackFn)
findLastIndex(callbackFn, thisArg)

// Inline callback function
findLastIndex(function(element) { /* … */ })
findLastIndex(function(element, index) { /* … */ })
findLastIndex(function(element, index, array){ /* … */ })
findLastIndex(function(element, index, array) { /* … */ }, thisArg)
const arr = [5, 10, 15, 20, 25]
const found = arr.findLastIndex((element) => element > 13)

console.log(found) // 4
array.flat()

Creates a new array with all sub-array elements placed into one array.

flat()
flat(depth)
  • depth: the level of how deep a nested array should be flattened (default is 1)
const arr = [0, [1, 2], 3 [4, 5]]
console.log(arr.flat()) // [0, 1, 2, 3, 4, 5]

const arrTwo = [0, [1, 2], [3, [4, 5]]]
console.log(arrTwo.flat(2)) // [0, 1, 2, 3, 4, 5]
array.flatMap()

Essentially map() + flat() combined but more efficient. Only flattens result by one level.

// Arrow function
flatMap((currentValue) => { /* … */ } )
flatMap((currentValue, index) => { /* … */ } )
flatMap((currentValue, index, array) => { /* … */ } )

// Callback function
flatMap(callbackFn)
flatMap(callbackFn, thisArg)

// Inline callback function
flatMap(function(currentValue) { /* … */ })
flatMap(function(currentValue, index) { /* … */ })
flatMap(function(currentValue, index, array){ /* … */ })
flatMap(function(currentValue, index, array) { /* … */ }, thisArg)
const arr = [1, 2, [3, 4], [5, 6]]
const flatten = arr.flatMap(a => a)

console.log(flatten) // [1, 2, 3, 4, 5, 6]

Example: Difference between map() and flatMap()

const arr = [1, 2, 3, 4]

arr.map(x => [x*2]) // [[2], [4], [6], [8]]
arr.flatMap((x) => [x*2]) // [2, 4, 6, 8]

arr.flatMap((x) => [[x*2]]) // [[2], [4], [6], [8]]
array.forEach()

Executes a provided function on each element of the array.

// Arrow function
forEach((element) => { /* … */ })
forEach((element, index) => { /* … */ })
forEach((element, index, array) => { /* … */ })

// Callback function
forEach(callbackFn)
forEach(callbackFn, thisArg)

// Inline callback function
forEach(function(element) { /* … */ })
forEach(function(element, index) { /* … */ })
forEach(function(element, index, array){ /* … */ })
forEach(function(element, index, array) { /* … */ }, thisArg)
const arr = [1, 2, 3, 4, 5]

arr.forEach(element => console.log(element * 2))

// 2
// 4
// 6
// 8
// 10
array.from()

Static method which creates a new shallow copied array instance from an iterable or array-like object.

// Arrow function
Array.from(arrayLike, (element) => { /* … */ } )
Array.from(arrayLike, (element, index) => { /* … */ } )

// Mapping function
Array.from(arrayLike, mapFn)
Array.from(arrayLike, mapFn, thisArg)

// Inline mapping function
Array.from(arrayLike, function mapFn(element) { /* … */ })
Array.from(arrayLike, function mapFn(element, index) { /* … */ })
Array.from(arrayLike, function mapFn(element) { /* … */ }, thisArg)
Array.from(arrayLike, function mapFn(element, index) { /* … */ }, thisArg)
Copy to Clipboard
console.log(Array.from([1, 2, 3], x => x + x)) // Array [2, 4, 6]

Example: Array from a Map

const map = new Map([[1,2], [2, 4], [4,8]])
Array.from(map) // [[1,2], [2,4], [4,8]]

const mapper = new Map([['1', a], ['2', 'b']])
Array.from(mapper.values()) // ['a', 'b']

Array.from(mapper.keys()) // ['1', '2']

Example: Arrow functions

Array.from([1, 2, 3], (x) => x + x) // [2, 4, 6]

Array.from({length: 5}, (v, i) => i) // [0, 1, 2, 3, 4]
array.includes()

Determines whether an array includes an element, and returns true or false.

includes(searchElement)
includes(searchElement, fromIndex)
const arr = [1, 2, 3]
console.log(arr.includes(1)) // true
console.log(arr.includes(10)) // false
array.indexOf()

Returns the first index of a given element can be found in the array. Returns -1 if not found.

const arr = [1, 2, 3, 4, 5]

console.log(arr.indexOf(1)) // 0
console.log(arr.indexOf(5)) // 4
console.log(arr.indexOf(2, -1)) // -1
console.log(arr.indexOf(1, -5)) // 0
indexOf(searchElement)
indexOf(searchElement, fromIndex)
Array.isArray()

Static method which determines whether a vlaye passed is an Array.

// true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array("a", "b", "c", "d"));
Array.isArray(new Array(3));

// false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray("Array");
Array.isArray(true);
Array.isArray(false);
array.join()

Creates and returns a new string by concatenating all the elements in an array, separating each one via commas or a specified string separator.

join()
join(separator)
const arr = ['a', 'b', 'c']

console.log(arr.join()) // "a, b, c"
console.log(arr.join('')) // abc
console.log(arr.join('-')) // a-b-c
array.keys()

Returns a new Array Iterator object that contains the keys for each index in the array.

keys()
const arr = ['a', 'b', 'c']
const iterator = arr.keys()

for(const key of iterator) {
  console.log(key)
}

// 0
// 1
// 2
array.lastIndexOf()

Returns the last index at which the given element can be found in the array. Otherwise returns -1. The array is searched backwards (fromIndex).

lastIndexOf(searchElement)
lastIndexOf(searchElement, fromIndex)
const arr = [1, 2, 5, 4, 5]
console.log(arr.lastIndexOf(5)) // 4
console.log(arr.lastIndexOf(2)) // 1
array.map()

Creates a new array populated with ther esult of calling a function on every element in the array.

// Arrow function
map((element) => { /* … */ })
map((element, index) => { /* … */ })
map((element, index, array) => { /* … */ })

// Callback function
map(callbackFn)
map(callbackFn, thisArg)

// Inline callback function
map(function(element) { /* … */ })
map(function(element, index) { /* … */ })
map(function(element, index, array){ /* … */ })
map(function(element, index, array) { /* … */ }, thisArg)
const arr = [1, 2, 3, 4, 5]
const mapArr = arr.map((a) => a * 2)

console.log(mapArr) // [2, 4, 6, 8, 10]

Example: Square Roots

const arr = [1, 4, 9]
const root = arr.map((n) => Math.sqrt(n))

console.log(arr) // [1, 4, 9]
console.log(root) // [1, 2, 3]
Array.of()

Creates a n ew Array instance from a variable number of arguments.

Array.of(element0)
Array.of(element0, element1)
Array.of(element0, element1, /* … ,*/ elementN)
Array.of(1, 2, 3); // [1, 2, 3]
Array(1, 2, 3);    // [1, 2, 3]
array.pop()

Removes the last element from an array and returns that element, thus changing the length of the array.

pop()
const arr = [1, 2, 3, 4, 5]
console.log(arr.pop()) // 5
console.log(arr) // [1, 2, 3, 4]
array.push()

Adds one or more elements to the end of the array, thus changing the length of the array.

push(element0)
push(element0, element1)
push(element0, element1, /* … ,*/ elementN)
const arr = [1, 2, 3, 4, 5]
const add = arr.push(6)

console.log(arr) [1, 2, 3, 4, 5, 6]

Example: Merging two arrays

const arrOne = [1, 2, 3]
const arrTwo = [4, 5, 6]

arrOne.push(...arrTwo)

console.log(arrOne) // [1, 2, 3, 4, 5, 6]
array.reduce()

A “reducer” callback function on each element of the array in order. The final result of running the reduce across all the elements is one value.

// Arrow function
reduce((previousValue, currentValue) => { /* … */ } )
reduce((previousValue, currentValue, currentIndex) => { /* … */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* … */ } )

reduce((previousValue, currentValue) => { /* … */ } , initialValue)
reduce((previousValue, currentValue, currentIndex) => { /* … */ } , initialValue)
reduce((previousValue, currentValue, currentIndex, array) => { /* … */ }, initialValue)

// Callback function
reduce(callbackFn)
reduce(callbackFn, initialValue)

// Inline callback function
reduce(function(previousValue, currentValue) { /* … */ })
reduce(function(previousValue, currentValue, currentIndex) { /* … */ })
reduce(function(previousValue, currentValue, currentIndex, array) { /* … */ })

reduce(function(previousValue, currentValue) { /* … */ }, initialValue)
reduce(function(previousValue, currentValue, currentIndex) { /* … */ }, initialValue)
reduce(function(previousValue, currentValue, currentIndex, array) { /* … */ }, initialValue)
const arr = [1, 2, 3, 5]
const count = 0;
const sum = arr.reduce((prev, curr) => prev + curr, count)

console.log(sum) // 10

Example: Flatten an array of arrays

const flatten = [
  [0, 1],
  [2, 3],
  [4, 5]
].reduce((prev, curr) => prev.concat(curr), [])

console.log(flatten) // [0, 1, 2, 3, 4, 5]

Example: Remove duplicates

const arr = [1, 1, 2, 3, 2, 2, 4, 3, 6]

const noDups = arr.reduce((prev, curr) => {
  if(!prev.includes(curr) {
    return [...prev, curr]
  }
  return prev
  },
  [],
)

Example: Replace .filter().map() with .reduce()

const arr = [1, 2, 3, 4, 5]

const double = arr.reduce((prev, curr) => {
  if(curr > 0) {
    const douled = curr * 2
    return [...prev, doubled]
  }
  return prev
}, [])
array.reduceRight()

Applies a function against an accumlator and each value of the array to reduce to a single value.

// Arrow function
reduceRight((accumulator, currentValue) => { /* … */ } )
reduceRight((accumulator, currentValue, index) => { /* … */ } )
reduceRight((accumulator, currentValue, index, array) => { /* … */ } )
reduceRight((accumulator, currentValue, index, array) => { /* … */ }, initialValue)

// Callback function
reduceRight(callbackFn)
reduceRight(callbackFn, initialValue)

// Callback reducer function
reduceRight(function(accumulator, currentValue) { /* … */ })
reduceRight(function(accumulator, currentValue, index) { /* … */ })
reduceRight(function(accumulator, currentValue, index, array){ /* … */ })
reduceRight(function(accumulator, currentValue, index, array) { /* … */ }, initialValue)
const arr = [[0,1], [2, 3], [4, 5]]
const result = arr.reduceRight((acc, curr) => acc.concat(curr))

console.log(result) // [4, 5, 2, 3, 0, 1]

Example: Flatten an arrays of arrays

const arr = [
  [0, 1],
  [2, 3],
  [4, 5]
]
const flatten = arr.reduceRight((a, b) => a.concat(b), [])

console.log(flatten) // [4, 5, 2, 3, 0, 1]

Example: Difference between reduce and reduce right

const a = ["1", "2", "3", "4", "5"];
const left = a.reduce((prev, cur) => prev + cur);
const right = a.reduceRight((prev, cur) => prev + cur);

console.log(left); // "12345"
console.log(right); // "54321"
array.reverse()

Reverses an array in place and returns the reference to the same array. The first array element becomes the last element and the last element becomes the first element.

reverse()
const arr = [1, 2, 3]
const arrReversed = arr.reverse()

console.log(arr) // [1, 2, 3]
console.log(arrReversed) // [3, 2, 1]
array.shift()

Removes the first element from an array and returns that removed element, thus changing the length of the array.

shift()
const arr = [1, 2, 3]
const shiftArr = arr.shift()

console.log(arr) // [1, 2, 3]
console.log(shiftArr) // [2, 3]
array.slice()

Returns a shallow copy of a portion of an array into a new array selected from start to end, where start and end represent the index of items in that array. Orginial array is not modified.

slice()
slice(start)
slice(start, end)
const arr = [1, 2, 3, 4]

console.log(arr.slice(1)) // [2, 3, 4]
console.log(arr.slice(1, 2)) // [2]
array.some()

Tests whether at least one element in the array passes the tests in the function. Returns a Boolean.

// Arrow function
some((element) => { /* … */ } )
some((element, index) => { /* … */ } )
some((element, index, array) => { /* … */ } )

// Callback function
some(callbackFn)
some(callbackFn, thisArg)

// Inline callback function
some(function(element) { /* … */ })
some(function(element, index) { /* … */ })
some(function(element, index, array){ /* … */ })
some(function(element, index, array) { /* … */ }, thisArg)
const arr = [1, 2, 3, 4, 5]
const even = (elem) => elem % 2 === 0

console.log(arr.some(even)) // true
array.sort()

Sorting elements within an array in ascending order.

// Functionless
sort()

// Arrow function
sort((a, b) => { /* … */ } )

// Compare function
sort(compareFn)

// Inline compare function
sort(function compareFn(a, b) { /* … */ })
const arr = ['k', 'o', 's', 'p']
console.log(arr.sort()) // ['k', 'o', 'p', 's']

Example: Creating, displaying and sorting an array

const stringArray = ['Blue', 'Humpback', 'Beluga'];
const numberArray = [40, 1, 5, 200];
const numericStringArray = ['80', '9', '700'];
const mixedNumericArray = ['80', '9', '700', 40, 1, 5, 200];

function compareNumbers(a, b) {
  return a - b;
}

stringArray.join(); // 'Blue,Humpback,Beluga'
stringArray.sort(); // ['Beluga', 'Blue', 'Humpback']

numberArray.join(); // '40,1,5,200'
numberArray.sort(); // [1, 200, 40, 5]
numberArray.sort(compareNumbers); // [1, 5, 40, 200]

numericStringArray.join(); // '80,9,700'
numericStringArray.sort(); // ['700', '80', '9']
numericStringArray.sort(compareNumbers); // ['9', '80', '700']

mixedNumericArray.join(); // '80,9,700,40,1,5,200'
mixedNumericArray.sort(); // [1, 200, 40, 5, '700', '80', '9']
mixedNumericArray.sort(compareNumbers); // [1, 5, '9', 40, '80', 200, '700']
array.splice()

Changes the contents of an array by removing or replacing elements.

splice(startIndex)
splice(startIndex, deleteCount)
splice(startIndex, deleteCount, item1)
splice(startIndex, deleteCount, item1, item2, itemN)
const months = ['Jan', 'March', 'April', 'June']
months.splice(1, 0, 'Feb')

console.log(months) // ["Jan", "Feb", "March", "April", "June"]
array.toLocaleString()

Returns a string representing the elements of the array. Elements are converted to Strings using their toLocaleString methods.

toLocaleString()
toLocaleString(locales)
toLocaleString(locales, options)
const array1 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')]
const localeString = array1.toLocaleString('en', { timeZone: 'UTC' })

console.log(localeString) // "1,a,12/21/1997, 2:12:00 PM"
array.toString()

Returns a string representing the specified array and its elements.

toString()
const arr = [1, 2, 3]

console.log(arr.toString()) // "1, 2, 3"
array.unshift()

Adds one or more elements to the beginning of an array, thus changing the length of the array.

unshift(element0)
unshift(element0, element1)
unshift(element0, element1, /* … ,*/ elementN)
const arr = [1, 2, 3]

console.log(arr.shift(1, 1)) // [1, 1, 1, 2, 3]

Example: Using negatives

const arr = [1, 2];

arr.unshift(0);
// result of the call is 3, which is the new array length
// arr is [0, 1, 2]

arr.unshift(-2, -1);
 // the new array length is 5
// arr is [-2, -1, 0, 1, 2]

arr.unshift([-4, -3]);
// the new array length is 6
// arr is [[-4, -3], -2, -1, 0, 1, 2]

arr.unshift([-7, -6], [-5]);
// the new array length is 8
// arr is [ [-7, -6], [-5], [-4, -3], -2, -1, 0, 1, 2 ]
array.values()

Returns a new Array iterator object, which iterates the value of each index in the array.

values()
const arr = ['a', 'b', 'c']
const iterator = arr.values()

for(const val of iterator) {
  console.log(value)
}

// a
// b
// c

Experimental

These methods are currently experimental and thus may not be compatible with all browsers. Check here to see which browsers are currently compatible.

array.group()

The group() method groups the elements of the calling array according to the string values returned by a provided testing function. The returned object has separate properties for each group, containing arrays with the elements in the group.

// Arrow function
group((element) => { /* … */ } )
group((element, index) => { /* … */ } )
group((element, index, array) => { /* … */ } )

// Callback function
group(callbackFn)
group(callbackFn, thisArg)

// Inline callback function
group(function(element) { /* … */ })
group(function(element, index) { /* … */ })
group(function(element, index, array){ /* … */ })
group(function(element, index, array) { /* … */ }, thisArg)
array.groupByMap()

The groupToMap() method groups the elements of the calling array using the values returned by a provided testing function. The final returned Map uses the unique values from the test function as keys, which can be used to get the array of elements in each group.

// Arrow function
groupToMap((element) => { /* … */ } )
groupToMap((element, index) => { /* … */ } )
groupToMap((element, index, array) => { /* … */ } )

// Callback function
groupToMap(callbackFn)
groupToMap(callbackFn, thisArg)

// Inline callback function
groupToMap(function(element) { /* … */ })
groupToMap(function(element, index) { /* … */ })
groupToMap(function(element, index, array){ /* … */ })
groupToMap(function(element, index, array) { /* … */ }, thisArg)
console.log([1, , 3].groupToMap((x) => x));
// Map { 1 => [1], undefined => [undefined], 3 => [3] }

Theory Example

Easy

Sorted Square Array

Write a function that takes in a non-empty array of integers that are sorted in ascending order and returns a new array of the same length with the squares of the original integers also sorted in ascending order.

Sample input: array = [1, 2, 3, 5, 6, 7, 8]

Sample output: [1, 4, 9, 25, 36, 84, 81]

Answer

function sortedSquaredArray(array) {
  return array.map(a => a*a).sort((a,b)=> a-b)
}

Medium

Array of Products

Write a function that takes in a non-empty array of integers and returns an array of the same length, where each element in the output array is equal to the product of every other number in the input array.

The value at output[i] is equal to the product of every number in the input array other than input[i].

Sample input: array = [5, 1, 4, 2] Sample output: [8, 40, 10, 20]

Answer

function arrayOfProducts(array) {
  const total = []
  for(let i=0; i<array.length; i++) {
    let product = 1;
    for(let j=0; j<array.length; j++) {
      if(i !== j) {
        product *= array[j]
      }
      total[i] = product
    }
  }
  return total;
}

Hard

Four Number Sum

Write a function that takes in a non-empty array of distinct integers and an integer representing a target sum. The function should find all quadruplets in the array that sum up to the target sum and return a two-dimensional array of all these quadruplets in no particular order.

If no four numbers sum up to the target sum, the function should return an empty array.

Answer

function fourNumberSum(array, targetSum) {
  // array[a] + array[b] + array[c] + array[d] = [[a, b, c, d]]

  let hashmap = {}
  let output = []
  let sum = 0

  array.sort((a,b) => a-b)

  for(let i = 0; i < array.length; i++) {
    for(let j = i+1; j < array.length; j++) {
      let a = j + 1
      let b = array.length - 1

      while(a < b) {
        sum = array[i] + array[a] + array[b] + array[j]
        array[b]

        if(sum === targetSum) {
          const key = `${array[i]}${array[j]}${array[a]}${array[b]}`
          hashmap[key] = [array[i], array[j], array[a], array[b]]
          a++
          b--
        } else if(sum < targetSum) {
          a++
        } else if (sum > targetSum) {
          b--
        }
      }
    }
  }
  return Object.values(hashmap)
}

Web Application Example

Purpose of Application

The Code Sandbox below is a create-react-app with TypeScript. In the example below, you can see how to use both TypeScript and generic Array methods to draw out data, and use it within an application

TypeScript Generics

Generics provide a way to make components work with any data type and not restrict to one data type. Components can be called or used with a variety of data types. Generics in TypeScript is similar to C# generics.

Let’s take the following example:

function getArray(myArr : any[] ) : any[] {
    return new Array().concat(myArr);
}

let numArr = getArray([1, 2, 3]);
let strArr = getArray(["Hello", "World", "TypeScript"]);

numArr.push(400);
strArr.push("New");

numArr.push("One");
strArr.push(1);

console.log(numArr); // [1, 2, 3, "One"]
console.log(strArr); // ["Hello", "World", "TypeScript", 1]

What’s wrong with the above?

In the above example, the getArray() function accepts an array of type any. It creates a new array of type any, concats items to it and returns the new array. Since we have used type any for our arguments, we can pass any type of array to the function. However, this may not be the desired behavior. We may want to add the numbers to number array or the strings to the string array, but not numbers to the string array or vice-versa.

The getArray() function accepts an array of type any, adding elements to the arrays of any type too. Sometimes this can be heplful but what if you only wanted to add numbers to a numbers array or strings to a strings array? This is where generics come in.

Using Generics

Generics use the type variable <T>. The type variable only works with the type specified.

If we were to use generics, we would get the following:

function getArray<T>(myGenArr : T[] ) : T[] {
    return new Array<T>().concat(myGenArr);
}

let numArr = getArray<number>([1, 2, 3]);
let strArr = getArray<string>(["Hello", "World", "TypeScript"]);

myNumArr.push(4);
myStrArr.push("New String");

myNumArr.push("Hi"); // error - trying to put a type string element into a integer array
myStrArr.push("This will work");

Code Sandbox

Big-O

push() O(1)

pop() O(1)

shift() O(n)

unshift() O(n)

concat() O(n)

slice() O(n)

splice() O(n)