blog bg

November 07, 2023

JavaScript: Working with Arrays

Share what you learn in this blog to prepare for your interview, create your forever-free profile now, and explore how to monetize your valuable knowledge.

Arrays (learn more)

In JavaScript,  arrays are probably the most used data structure because we use them all the time and therefore, JavaScript has countless modern array methods. In this article, we will learn all about these tools and how to use them.


 Simple Array Methods

Why arrays do actually have methods?

Well, remember that methods are simply functions that we called on objects. So, basically, they are functions attached to objects. So, if we have array methods, that means that arrays themselves are also objects and they get access to special built in methods that can essentially see as tools for arrays. Let's learn some very simple methods that we can use them on arrays.

 

#Slice

It's very similar to the Slice() method available on Strings. With the Slice method, we can extract part of any array but without changing the original array. So essentially, we can take  slice of an array.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.slice())//[ 'a', 'b', 'c', 'd', 'e' ]
console.log(arr.slice(2))//[ 'c', 'd', 'e' ]
console.log(arr.slice(2,4))//[ 'c', 'd' ]
console.log(arr.slice(-2))//[ 'd', 'e' ]
console.log(arr.slice(1, -2))//[ 'b', 'c' ]

Key points,

  • it accepts two parameters and they are actually optional
    • startIndex(optional): index at which to start extraction and negative index starts extraction from the end of the array
    • endIndex(optional): index at which to end extraction but not including the endIndex and negative index counts back from the end of the array 
  • if we don't specify any arguments in slice() method, then it will returns a shallow copy of the original array

 

#Splice

The splice() method works in almost the same the way as slice() method, but the fundamental difference is that, it does actually change the original array so, it mutates that array. Using splice() method, we can removing or replacing existing elements or adding new elements.

let's see some examples,

let arr = ['a',"b","c","d","e"]
console.log(arr.splice(2))//[ 'c', 'd', 'e' ]
console.log(arr)//[ 'a', 'b' ]

As you can see that, all the remaining elements in our original array is the first two elements and now extracted elements are actually removed from the original array so, Splice deleted them

let arr = ['a',"b","c","d","e"]
console.log(arr.splice(-1))//[ 'e' ]
console.log(arr)//[ 'a', 'b', 'c', 'd' ]

Splice removed the last element from the original array

Syntax

splice(start)
splice(start, deleteCount)
splice(start, deleteCount, item1)
splice(start, deleteCount, item1, item2)
splice(start, deleteCount, item1, item2,..., itemN)

Parameters

  • start: index at which to start changing the array
  • deleteCount(optional): An Integer indicating the number of elements in the array to be removed from the start. for example, remove 2 elements from start index 1
let arr = ['a',"b","c","d","e"]
console.log(arr.splice(1,2))//[ 'b', 'c' ]
console.log(arr)//[ 'a', 'd', 'e' ]
  • item1,...itemN(optional): The elements to be added to the array, beginning from start, for example, add  and  before 'c' without deleting any elements
let arr = ['a',"b","c","d","e"]
arr.splice(2,0,"j","k")
console.log(arr)//[ 'a', 'b', 'j','k', 'c', 'd','e' ]
     

NOTE: To splice the elements in an array without mutating the original array, use toSpliced() method 

 

#reverse

The reverse() method allows us to reverse the array, but it does actually mutate the original array

For example,

let arr = ['a',"b","c","d","e"]
console.log(arr.reverse())//[ 'e', 'd', 'c', 'b', 'a' ]
console.log(arr);//[ 'e', 'd', 'c', 'b', 'a' ]

NOTE: To reverse the elements in an array without mutating the original array, use toReversed() method 

 

#concat

The concat() method allows us to merge two or more arrays. This method does not mutate/change the existing arrays, but instead returns a new array.

For example,

let arrOne = ['a',"b","c","d","e"]
let arrTwo = ['f',"g","h","i","j"]
console.log(arrOne.concat(arrTwo))//[ 'a', 'b', 'c', 'd','e', 'f', 'g', 'h','i', 'j' ]

NOTE: we can also contact two or more arrays using spread operator, for example [...arrOne,...arrTwo]. It also does not mutate any of the existing arrays

 

#join

The join() method allows us to join all of the elements in an array using the separator and returns a new string.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.join("*"))//a*b*c*d*e

 

#push

The push() method allows us to add the specified elements to the end of an array and returns the new length of the array.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.push("f"))//6
console.log(arr)//[ 'a', 'b', 'c', 'd', 'e', 'f' ]

console.log(arr.push("g","h","i"))//9
console.log(arr)//[ 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i' ]

 

#pop

The pop() method allows us to remove the last element from the array and returns the removed element.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.pop())//e
console.log(arr)//[ 'a', 'b', 'c', 'd' ]

console.log(arr.pop())//d
console.log(arr)//[ 'a', 'b', 'c' ]

 

#shift

The shift() method allows us to remove the first element from the array and returns the removed element.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.shift())//a
console.log(arr)//[ 'b', 'c', 'd', 'e' ]

console.log(arr.shift())//b
console.log(arr)//[ 'c', 'd', 'e' ]

 

#unshift

The unshift() method allows us to add the specified elements to the beginning of an array and returns the new length of the array.

For example,

let arr = ['a',"b","c","d","e"]

console.log(arr.unshift('n'))//6
console.log(arr)//[ 'n', 'a', 'b', 'c', 'd', 'e' ]

console.log(arr.unshift('m'))//7
console.log(arr)//[ 'm, 'n', 'a', 'b', 'c', 'd', 'e' ]

 

#At

The At() method takes an integer value and returns the item at that index and this method allows us to use both positive and negative integer. if we use positive integer then it starts from the beginning of the array to find the element and if we use negative integer then it starts from the end of the array to find the element. for example,

 

let arr = ['a',"b","c","d","e"]

console.log(arr[0]);//a
console.log(arr.at(0));//a

console.log(arr[-1]);//undefined
console.log(arr.at(-1));//e
console.log(arr[arr.length - 1]);//e

as you can see that, At() method works exactly our traditional bracket notation([]). However, there is one particularly of the At() method, which make it quite useful to use instead of the bracket notation, which is to get the last element of the array, with our traditional bracket notation we need to find the array length and deduct 1 to get the last element, but this new At() method makes this process even easier by writing negative number in our case simply use -1 to get the last element

 

Looping Arrays

#forEach 

We already know how to loop over an array using the for...of loop, but the forEach is really fundamentally different. Let's start with an example,

let numbers = [200, 450, -400]

//using for..of loop
for (const number of numbers){
  if(number > 0){
    console.log(`${number} is positive`);
  }else{
    console.log(`${number} is negative`);
  }
}

//using forEach loop
numbers.forEach((number,index) => {
  if(number > 0){
    console.log(`${number} at index ${index} is positive`);
  }else{
    console.log(`${number} at index ${index} is negative`);
  }
});

Output:
200 at index 0 is positive
450 at index 1 is positive
-400 at index 2 is negative

forEach method will loop over the array and in each iteration, it will execute the callback function. In each iteration, it will pass in three arguments, which are the current element , current Index of that element and the entire array that we are looping as an arguments to the callback function. However, we just don't need to pass these three arguments all the time. if want we can pass only current element or current element and current index or all three together.

To access the current index of the current element in the for..of loop, we use arr.entries() method, which returns an array of arrays, in each array element, first position contains the current index and second position contains the the value itself. But accessing current index of the element is much easier when we use forEach loop than for...of loop.

Another difference between these two loops is that we can not break out of a for loop which means that continue or break statements do not work in a forEach loop at all. So if we want to break out of the loop, then we will have to use for...of loop.

 

ForEach with Maps and Sets

ForEach is also available on maps and sets.

#Maps

consider the following example,

const currencies = new Map([
  ['USD','United States dollar'],
  ['EUR','Euro'],
  ['GBP','Pound Sterling'],
])
in this array of arrays, each of these array elements, will be one entry of the map(inner array), where the first element is the key and the second element is the value. When we call forEach on map, it will call with three arguments which are the current value, the key and the entire map that is being looped over. For example,
currencies.forEach((value, key, map) => {
  console.log(`${key}:${value}`);
})

Output:

USD:United States dollar
EUR:Euro
GBP:Pound Sterling
 

#Sets

consider the following example,

const currenciesUnique = new Set(['USD','GBP','USD','EUR','EUR'])

console.log(currenciesUnique);
Output:

Set(3) { 'USD', 'GBP', 'EUR' }
With sets, need to pass an iterable, in this example, we used an array. As you can see that, it only has unique values. let's call forEach on this set,
currenciesUnique.forEach((value, key, sets) => {
  console.log(`${key}:${value}`);
})

Output:

USD:USD
GBP:GBP
EUR:EUR
As you can see that, the key is exactly the same as the value. This is because a set doesn't have keys and also it doesn't have indexes as well.


 

Data Transformations: Map, Filter and Reduce

There are three big and important array methods that we use all the time to perform data transformation. Basically, these three methods that we use to create new arrays based on transforming data from other arrays and these are map, filter and reduce.

 

#Map

The map() method returns a new array containing the results of applying an operation on all the original array elements. For example, we have an array of numbers and  we say that each element shall be multiplied by two. With this condition in place, the map method multiplies every single element by two and puts it into a new array. 

Consider the following array, where we have an array of names, now we need to compute a username and the username is simply the initials of the users. 

const accounts = [
    {
      name: 'Jonas Schmedtmann',
    },
    {
      name: 'Jessica Davis',
    },
    {
      name: 'Steven Thomas Williams',
    },
    {
      name: 'Sarah Smith',
    },
];

const calculateUserName = (name) => {
  let userNameArr = name.toLowerCase().split(" ");
  const userNameFirstLetterArr = userNameArr.map((name) => name[0]);
  return userNameFirstLetterArr.join("");
}
accounts.map((account) => {
  account.userName = calculateUserName(account.name)
});

console.log(accounts);

Output:

[
  { name: 'Jonas Schmedtmann', userName: 'js' },
  { name: 'Jessica Davis', userName: 'jd' },
  { name: 'Steven Thomas Williams', userName: 'stw' },
  { name: 'Sarah Smith', userName: 'ss' }
]

 

#Filter

The filter() method returns a new array containing the array elements that passed a specified test condition. For example, we have an array, which contains numbers and  we are only looking for elements greater than 50. So, all the elements that pass the condition, will make it into a new filtered array. In other words, elements which satisfy the condition will be included in a new array that the filter() method returns. All other elements will get filtered out so, they will not be included in the new array.

Consider following array, where we have an array of number with both positive and negative numbers, lets filter the array with only positive numbers.

const numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

const postiveNumbers = numbers.filter((number) =>  number > 0);

console.log(postiveNumbers);//[ 200, 450, 3000, 70, 1300 ]

 

#Reduce

The reduce() method boils(reduces) down all the array elements into one single value. For example, add all the elements in an array which has only numbers. We need to specify an operation where we have an accumulator variable as the reduce method loops over the array, it keeps adding the current element into the accumulator until loop finishes. At the end, we will have total sum of all the elements

For example,

  • we have an array of number with both positive and negative numbers, lets calculate the total of the array.
const numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

const total = numbers.reduce((accumulator, number) => accumulator + number,0);

console.log(total);//3840
  • we have an array of number with both positive and negative numbers, lets find the maximum number from that array.
const numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

const maxNumber = numbers.reduce((accumulator, number) => {
   if(accumulator > number){
     return accumulator
   }else{
     return number
   }
},0);

console.log(maxNumber);//3000


 Chaining Map, Filter and Reduce

Let's have an example by chaining all these methods one after another. For example, we have an array of an account's deposits(positive number) and withdrawals(negative numbers), from that array we need to take all the deposits and then convert them from euros to dollars and finally add them all up so that we know how much was deposited.

let movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

let eurToUsd = 1.2;
const totalDepositsUSD = movements.filter((movement) => movement > 0)
          .map((movement) => movement * eurToUsd)
          .reduce((acc, movement) => acc + movement,0);

console.log(totalDepositsUSD);//6024


 #Find

The find() method allows us to retrieve one element from array based on a condition. Basically, it returns the first element that satisfies the condition. If no element in the array satisfies the condition then undefined is returned. The find() method also accept a callback function, which will then be called as the method loops over the array.

For example, we have an array of positive and negative number and let's find a number which is less than 0.

let numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

let negativeNumber = numbers.find(number => number < 0);
console.log(negativeNumber);//-400

let postiveNumber = numbers.find(number => number > 10000);
console.log(postiveNumber);//undefined

As you can see, -400 is the very first value that appears in the array that is negative and also it returns undefined if the condition did not satisfy any element in the array


 #FindIndex

The findIndex() method works almost the same as the find() method, but findIndex() method returns the index of the found element from the array based on a condition. Basically, it returns the first element that satisfies the condition. If no element in the array satisfies the condition then -1 is returned. The findIndex() method also accept a callback function, which will then be called as the method loops over the array.

For example, we have an array of positive and negative number and let's find the index of the number which is less than 0.

let numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

let negativeNumber = numbers.findIndex(number => number < 0);
console.log(negativeNumber);//2

let postiveNumber = numbers.findIndex(number => number > 10000);
console.log(postiveNumber);//-1

 

Some and Every

#Some

As we learned earlier, we use includes() method to test if an array includes a certain value, but if we want to test for a condition instead? that's where this some method comes into play. So, we use some() method  to test whether at least one element in the array passes the condition. It returns true, if it finds an element for which the provided function returns true, otherwise it returns false. 

For example, we have an array of positive and negative number and find if there is any positive number in the array.

let numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

let anyPostiveNumber = numbers.some(number => number > 0);
console.log(anyPostiveNumber);//true

let anyPostiveNumberTwo = numbers.some(number => number > 10000);
console.log(anyPostiveNumberTwo);//false

 

#Every

The every() method similar to the some() method, but it will return true if all of the elements in the array satisfy  the condition, that we passed in. In other words, if every elements passes the test in our callback function, the every() method returns true otherwise it returns false.

For example

let numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];

let postiveNumber = numbers.every(number => number < 10000);
console.log(postiveNumber);//true

let postiveNumberTwo = numbers.every(number => number > 0);
console.log(postiveNumberTwo);//false

 
Flat and Flatmap

 

#Flat

Let's say we have an nested array and if we wanted to take all these elements in the separate and put all of these together in just one big array. This is pretty simple using the flat method. So, the flat() method creates a new array with all sub-array elements into one array.

For example

let numbers = [[200, 450],[ -400, 3000, -650, -130], 70, 1300];

console.log(numbers.flat());//[200, 450, -400, 3000, -650, -130, 70, 1300];

However, let's say that we have an array which is even deeper nested like shown below,

let numbers = [[200, 450], -400, [3000, -650,[ -130, 70, 1300]]];

we apply flat() method on this array, we will get following result,

console.log(numbers.flat());//[ 200, 450, -400, 3000, -650, [ -130, 70, 1300 ] ]

By default, the flat() method goes one level deep, when flatting the array. However, we can fix  this by using the depth argument. if don't specify the depth argument in flat() method, by default it takes value 1. As you can see,  as the above the array, which has 2 levels deep,  we need to pass 2 as depth argument into flat() method.

console.log(numbers.flat(2));//[ 200, 450, -400, 3000, -650, -130, 70, 1300 ]

However, what if we don't know the depth of an array. In that case, we just need to pass "Infinity" as an argument in falt() method. for example,

let numbers = [[200, 450], -400, [3000, -650,[ -130, [70, 1300]]]];

console.log(numbers.flat(Infinity));//[ 200, 450, -400, 3000, -650, -130, 70, 1300 ]


 #Flatmap

The flatmap() method essentially combines, a map and a flat method, into just one method. So, flatmap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level. it is identical to a map() followed by a flat() of depth 1, but slightly more efficient than calling those two methods separately.

For example, we have 4 different accounts and each has it's own movements. Our target is to combine all the movements into one long array.

const account1 = {
  movements: [200, 450, -400]
};

const account2 = {
  movements: [5000, 3400, -150]
};

const account3 = {
  movements: [200, -200, 340]
};

const account4 = {
  movements: [430, 1000, 700]
};

const accounts = [account1, account2, account3, account4];

const accountMovements = accounts.map(acc => acc.movements);
console.log(accountMovements);

const allMovements = accountMovements.flat();
console.log(allMovements);//[200,  450, -400, 5000,3400, -150,  200, -200,340,  430, 1000,  700]

//with flatMap
const onlyMovements = accounts.flatMap(acc => acc.movements);
console.log(onlyMovements);////[200,  450, -400, 5000,3400, -150,  200, -200,340,  430, 1000,  700]

 

Sorting Arrays

 

#Sort

The sort() method allows us to sorts the elements of an array. But, it does mutate the origin array.

Consider the array of strings,

const fruits = ["Apple","Mango","Kiwi","Watermelon","Strawberry"];
fruits.sort();
console.log(fruits);//[ 'Apple', 'Kiwi', 'Mango', 'Strawberry', 'Watermelon' ]

As you can see that, array sorted alphabetically from A to Z and also original array is now mutated

Consider the array of numbers,

const numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];
numbers.sort();
console.log(numbers);//[ -130, -400, -650,1300,  200, 3000,450,70 ]

If we take a look at the result, it is not really what we are expecting and these numbers are not at all ordered in the any way. The reason for this that the sort() method does the sorting based on strings. Basically, what it does is to convert everything to strings and then it does the sorting itself. If we look at the result as if they were strings, the minus always comes first. So, it alphabetically order them(1(30),4(00),6(50)) and then order the positive numbers like (1(300),2(00),3(000),4(50),7(0)). 

However,  we can fix this by passing a compare callback function. For example,

const numbers = [200, 450, -400, 3000, -650, -130, 70, 1300];
//if return < 0 a,b
//if return > 0 b,a
numbers.sort((a,b) => {
  if(a > b){
    return 1;
  }
  if(b > a){
    return -1;
  }
});
console.log(numbers);//[ -650,-400,-130,70,200,450,1300, 3000 ]

This callback function accepts two parameters which are the current value and the next value. when compare these two numbers, if it returns a negative value, then a will be put before b and if returns a positive value, then b will be put before a in the sorted output array.

NOTE: To sort the elements in an array without mutating the original array, use toSorted() method 


 More Ways of Creating and Filling Arrays

Generally, we create an array as shown below,

console.log([1,2,3,4,5,6,7,8])//[1,2,3,4,5,6,7,8]
console.log(new Array(1,2,3,4,5,6,7,8));////[1,2,3,4,5,6,7,8]

However, in these cases we already have data, therefore we could then manually create these arrays. However, we can actually also generate arrays programmatically, without having to define all the items manually. There are multiple ways of doing it,

use Array() constructor

const arr = new Array(7);
console.log(arr);//[ <7 empty items> ]

we might think that this is going to create an array with only one element 7, instead it creates a new array with 7 empty elements. Whenever, we pass only one argument into Array(), it creates a new empty arguments with that length. So, if we don't know  about this behaviour of the Array() constructor then this can lead to weird errors.

There is only way to fill this array is to call fill() method. For example,

const arr = new Array(7);
console.log(arr);//[ <7 empty items> ]

console.log(arr.map(() => 3))//[ <7 empty items> ] -- no diff even if we use map

arr.fill(3);
console.log(arr);//[3, 3,3,3,3,3,3]

We need to pass a value and then it will fill up the entire array with this specific value and it does actually mutate the undelaying array.

Beside this value we can also specify where we want to begin the the array with. So, we can specify a begin parameter and then it only starts at index that we specify and it will then fill it up until the end unless we specify an end parameter but the final index is not gonna be included in the array

For example,

  • We need to start filling from index 3
const arr = new Array(7);

arr.fill(1,3);
console.log(arr);//[ <3 empty items>, 1, 1, 1, 1 ]
  • We need to start filling from index 3 until 6
const arr = new Array(7);

arr.fill(1,3,6);
console.log(arr);//[ <3 empty items>, 1, 1, 1, <1 empty item> ]

We can also fuse the fill() method on other arrays as well and it does not have to be an empty array. for example,

const arr = [1,2,3,4,5,6,7];
arr.fill(23,2,5);
console.log(arr);//[1,2,23,23,23,6,7];

With this, we created an array programmatically, without actually having to write it down manually

 

#Array.from()

This method allow us to create a new array from an iterable or array-like object. It actually accept 3 parameters which are

  • arrayLike(Required): An iterable or array-like object, which we need to convert to an array
  • mapFunction(optional): A function to call on every element of the array. if we provide this function, then the return value is added to the array. This function accept two parameters which are current element and index of the current element
  • thisArgument(optional): value to use as this when executing mapFunction.

For example,

const arr = Array.from({length: 7}, () => 1);
console.log(arr);//[1, 1, 1, 1, 1, 1, 1]

const arr1 = Array.from({length: 7}, (element,i) => i+1);
console.log(arr1);//[1, 2, 3, 4, 5, 6, 7]

//In this case, we are not using the current element in map function so we can use underscore
const arr2 = Array.from({length: 7}, (_,i) => i+1);
console.log(arr2);//[1, 2, 3, 4, 5, 6, 7]

const arr3 = Array.from([1,2,3,4,5,6,7], (curElement,i) => curElement+i);
console.log(arr3);//[1,  3,  5, 7, 9, 11, 13]

const arr4 = Array.from("javascript");
console.log(arr4);//['j', 'a', 'v', 'a','s', 'c', 'r', 'i','p', 't']


  

Which Array Method to Use?

Methods to mutate the original array

  • Add elements to the original array
    • .push() -> at the end
    • .unshift() -> at the beginning
  • Remove elements from the original array
    • .pop() -> at the end
    • .shift() -> at the beginning
    • .splice() -> at any position
  • Others methods
    • .reverse()
    • .sort()
    • .fill()

 

Methods to use/create a new array

  • Calculate/Compute one array from the original array
    • .map() -> it loops over the entire array
  • Filtering an array using a condition
    • .filter()
  • taking a portion/slice of the original array
    • .slice()
  • Adding Original to other array Or Concatenating two or more arrays
    • .concat()
  • Flattening the original
    • .flat()
    • .flatMap()

 

Methods to find An array index

  • Based on Value
    • .indexOf()
  • Based on test condition
    • .findIndex() -> search for an element in the array based on the condition that we provide in the callback function and return index

 

Methods to find An array Element

  • Based on test condition
    • .find() -> search for an element in the array based on the condition that we provide in the callback function and return element

 

Methods to know if array includes a certain element or not

  • Based on value
    • .includes() -> return true if an array contains a single value
  • Based on test condition
    • .some() -> return true if at least one of the elements in the array satisfies the condition
    • .every() -> return true if all of the elements in the array satisfies the condition

 

Methods to transform an array into a new string

  • Based on separator string
    • .join()

 

Methods to transform an array into a value

  • Based on accumulator
    • .reduce() -> Boil down to single value of any type: number, string,  Boolean, or even new array or object

 

To loop array

  • Based on callback
    • .forEach() -> does not create a new array, just loop over it

 

EXERCISES

I have attached the github link with examples with answers. I hope it will help you to understand the concept further.

Next Article: JavaScript: Numbers, Dates and Timers

365 views

Please Login to create a Question