What You Need to Know About JavaScript Arrays

Original author: Thomas Lombart
  • Transfer
We present to you a translation of an article by Thomas Lombart, which was published on medium.freecodecamp.org. The translation is published with permission of the author.


An example of using the reduce method to reduce an array

Let me make a bold statement: loops are often useless and make code difficult to read. For iterations in arrays, searching, sorting elements, and other similar actions, you can use one of the methods below.

Despite the effectiveness, most of these methods are still little known and not very popular. I will do the hard work for you and tell you about the most useful. Read this article as your guide to JavaScript array methods.

Note : Before we get started, you need to know one thing: I am biased towards functional programming. To avoid side effects, I strive to apply methods that do not directly modify the original array. I am not telling you to refuse to change the array at all, but it is worth considering that some methods lead to this. As a result, side effects, unwanted changes and, as a result, bugs appear.

This article was originally published on thomlom.dev , where you can find more web development material.

The basics


There are four methods worth knowing if you are working with arrays. This mapis filter,, reduceand the operator spread. They are effective and useful.

map
You will often use the method map. In general, every time you need to change the elements of an array, consider this option.

It takes one parameter - a function that is called on each element of the array, and then returns a new array so that there can be no side effects.

const numbers = [1, 2, 3, 4] 
const numbersPlusOne = numbers.map(n => n + 1) console.log(numbersPlusOne) // [2, 3, 4, 5]

You can also create a new array that stores only one specific property of the object.

const allActivities = [
  { title: 'My activity', coordinates: [50.123, 3.291] }, 
  { title: 'Another activity', coordinates: [1.238, 4.292] }
] 
const allCoordinates = allActivities.map(activity => activity.coordinates) 
console.log(allCoordinates) // [[50.123, 3.291], [1.238, 4.292]]

So, remember: when you need to change an array, think about using map .

filter
The name of this method speaks for itself: use it when you want to filter an array.

As well as map, it filtertakes as a single parameter a function that is called on each element of the array. This function should return a boolean value:

  • true- if you want to save an element in an array;
  • false - if you do not want to save it.

As a result, you will have the correct new array with the elements you want to leave.

For example, only odd numbers can be stored in an array.

const numbers = [1, 2, 3, 4, 5, 6] 
const oddNumbers = numbers.filter(n => n % 2 !== 0) console.log(oddNumbers) // [1, 3, 5]

You can also use filter to remove a specific element in the array.

const participants = [
  { id: 'a3f47', username: 'john' }, 
  { id: 'fek28', username: 'mary' }, 
  { id: 'n3j44', username: 'sam' }, 
]
functionremoveParticipant(participants, id) { 
  return participants.filter(participant => participant.id !== id) 
} 
console.log(removeParticipant(participants, 'a3f47')) //  [{ id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }];

reduce
In my opinion, this method is the most difficult to understand. But as soon as you master it, you will have a bunch of opportunities.

Typically, a method reducetakes an array of values ​​and concatenates them into a single value. It takes two parameters, a callback function (which is the reducer ) and an optional initial value (which is the first element of the array by default). The gearbox itself takes four parameters:

  • a battery that collects the returned values ​​in the gearbox ;
  • current value of the array;
  • current index;
  • the array for which the method was called reduce.

Basically, you will use only the first two parameters - the battery and the current value.

But let's not go deep into theory and consider the most common application example reduce.

const numbers = [37, 12, 28, 4, 9] 
const total = numbers.reduce((total, n) => total + n) console.log(total) // 90

In the first iteration, the accumulator, which is the sum, takes the initial value 37. The returned value is 37 + n, where n = 12. We get 49.

During the second iteration, the accumulator is 49, the returned value is 49 + 28 = 77. And so on.

The method is reduceso functional that you can use it to build many array methods like mapor filter.

const map = (arr, fn) => { 
  return arr.reduce((mappedArr, element) => { 
    return [...mappedArr, fn(element)] 
  }, []) 
} 
console.log(map([1, 2, 3, 4], n => n + 1)) // [2, 3, 4, 5]const filter = (arr, fn) => { 
  return arr.reduce((filteredArr, element) => { 
    return fn(element) ? [...filteredArr] : [...filteredArr, element] 
  }, []) 
} 
console.log(filter([1, 2, 3, 4, 5, 6], n => n % 2 === 0)) // [1, 3, 5]

As a rule, we assign the reduceinitial value to the method []- the battery. For mapwe run a function, the result of which is added to the end of the battery using the spread operator (we will talk about it below, do not worry). For we are filter doing almost the same thing, we only run the filter function on the element. If it is true, we return the previous array. Otherwise, add the element to the end of the array.

Let's look at a more complex example: greatly reduce the array [1, 2, 3, [4, [[[5, [6, 7]]]], 8]]to [1, 2, 3, 4, 5, 6, 7, 8].

functionflatDeep(arr) { 
  return arr.reduce((flattenArray, element) => { 
    returnArray.isArray(element) ? [...flattenArray, ...flatDeep(element)] : [...flattenArray, element] 
  }, []) 
} 
console.log(flatDeep([1, 2, 3, [4, [[[5, [6, 7]]]], 8]])) // [1, 2, 3, 4, 5, 6, 7, 8]

This example is very similar to map, except that we use recursion here. I will not dwell on recursion in detail, because this is beyond the scope of our topic, but if you want to know more, go to this excellent resource .

Spread statement (ES2015)
I agree, this is not a method. However, the spread operator helps achieve different goals when working with arrays. You can apply it to expand the values ​​of one array in another, and then make a copy or link several arrays together.

const numbers = [1, 2, 3] 
const numbersCopy = [...numbers]
console.log(numbersCopy) // [1, 2, 3]const otherNumbers = [4, 5, 6]
const numbersConcatenated = [...numbers, ...otherNumbers]
console.log(numbersConcatenated) // [1, 2, 3, 4, 5, 6]

Note : the spread statement makes a shallow copy of the original array. But what does “superficial” mean?

Such a copy will duplicate the original elements as little as possible. If you have an array with numbers, strings or Boolean values ​​( primitive types ), there are no problems and the values ​​are really duplicated. However, things are different with objects and arrays : only a reference to the original value is copied . Therefore, if you make a shallow copy of the array including the object and change the object in the copied array, it will also be changed in the original, because they have the same reference .

const arr = ['foo', 42, { name: 'Thomas' }]
let copy = [...arr]
copy[0] = 'bar'console.log(arr) // No mutations: ["foo", 42, { name: "Thomas" }]console.log(copy) // ["bar", 42, { name: "Thomas" }]
copy[2].name = 'Hello'console.log(arr) // /!\ MUTATION ["foo", 42, { name: "Hello" }]console.log(copy) // ["bar", 42, { name: "Hello" }]

So, if you want to create a real copy of an array that contains an object or arrays, you can use a lodash function like cloneDeep . But do not consider yourself obligated to do this. Your goal is to find out how everything works under the hood .

Useful methods


Below you will find other methods that are also useful to know about and which can be useful for solving problems such as finding an element in an array, removing part of an array, and much more.

includes (ES2015)
Have you ever used indexOfto find out if an element is in an array or not? A terrible way to check, right?

Fortunately, the method includes does the test for us. Set the parameter for includes, and it will search for the element in the array.

const sports = ['football', 'archery', 'judo'] 
const hasFootball = sports.includes('football')
console.log(hasFootball) // true

concat
The concat method can be used to merge two or more arrays.

const numbers = [1, 2, 3]
const otherNumbers = [4, 5, 6]
const numbersConcatenated = numbers.concat(otherNumbers)
console.log(numbersConcatenated) // [1, 2, 3, 4, 5, 6]// You can merge as many arrays as you wantfunctionconcatAll(arr, ...arrays) {
  return arr.concat(...arrays)
}
console.log(concatAll([1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12])) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

forEach
If you want to perform an action for each element of an array, you can use the method forEach. It takes a function as a parameter, which in turn also takes three parameters: current value, index, and array.

const numbers = [1, 2, 3, 4, 5] 
numbers.forEach(console.log)
// 1 0 [ 1, 2, 3 ]// 2 1 [ 1, 2, 3 ]// 3 2 [ 1, 2, 3 ]

indexOf
This method is used to return the first index at which the element can be found in the array. Also, with the help of indexOfoften check for the presence of an element in the array. Honestly, now I use it infrequently.

const sports = ['football', 'archery', 'judo'] 
const judoIndex = sports.indexOf('judo') 
console.log(judoIndex) // 2

find
The method find is similar to filter. You need to provide it with a function that tests each element of the array. However, it find stops testing the elements as soon as it finds one that has passed the test. This is notfilter one that iterates over the entire array regardless of circumstances.

const users = [
  { id: 'af35', name: 'john' },
  { id: '6gbe', name: 'mary' },
  { id: '932j', name: 'gary' },
]
const user = users.find(user => user.id === '6gbe')
console.log(user) // { id: '6gbe', name: 'mary' }

So, use the method filterwhen you want to filter the entire array, and use the method findwhen you are sure that you are looking for a unique element in the array.
findIndex
This method is almost the same as find, but it returns the index of the first element found instead of the element itself.

const users = [
  { id: 'af35', name: 'john' },
  { id: '6gbe', name: 'mary' },
  { id: '932j', name: 'gary' },
]
const user = users.findIndex(user => user.id === '6gbe')
console.log(user) // 1

It may seem that findIndex , and indexOf - this is the same thing. Not really. The first parameter indexOf is a primitive value (Boolean value, number, string, undefined value, or character), while the first parameter findIndex is a callback function.

Therefore, when you need to find the index of an element in an array of primitive values, you can work with indexOf. If you have more complex elements, such as objects, use findIndex.

slice
When you need to take part of an array or copy an array, you can refer to the method slice. But be careful: like the spread operator, it slice returns a shallow copy .

const numbers = [1, 2, 3, 4, 5] 
const copy = numbers.slice()

At the beginning of the article, I mentioned that loops are often useless. Let me show you how to get rid of them.

Suppose you want to return a certain number of chat messages from the API and you only need to see five of them. Below are two approaches: one with loops, the other with a method slice.

// The "traditional way" to do it:// Determine the number of messages to take and use a for loopconst nbMessages = messages.length < 5 ? messages.length : 5let messagesToShow = []
for (let i = 0; i < nbMessages; i++) {
  messagesToShow.push(posts[i])
}
// Even if "arr" has less than 5 elements,// slice will return an entire shallow copy of the original arrayconst messagesToShow = messages.slice(0, 5)

some
If you want to check whether at least one element of the array passes the test , you can use it some. Like map, filter or find, the method some takes a callback function as the only parameter, and then returns a value trueif at least one element passes the check, and a value falseif not.

Also some suitable for working with permissions.

const users = [
  {
    id: 'fe34',
    permissions: ['read', 'write'],
  },
  {
    id: 'a198',
    permissions: [],
  },
  {
    id: '18aa',
    permissions: ['delete', 'read', 'write'],
  }
]
const hasDeletePermission = users.some(user => 
  user.permissions.includes('delete')
)
console.log(hasDeletePermission) // true

every
This method is similar to some, except that it checks that each element (and not one ) matches the condition .

const users = [
  {
    id: 'fe34',
    permissions: ['read', 'write'],
  },
  {
    id: 'a198',
    permissions: [],
  },
  {
    id: '18aa',
    permissions: ['delete', 'read', 'write'],
  }
]
const hasAllReadPermission = users.every(user =>
  user.permissions.includes('read')
)
console.log(hasAllReadPermission) // false

flat (ES2019)
These are completely new methods in the JavaScript world. Usually flat creates a new array, connecting all the elements of a nested array. It takes one parameter - a number that indicates how much you want to reduce the dimension of the array.

const numbers = [1, 2, [3, 4, [5, [6, 7]], [[[[8]]]]]]
const numbersflattenOnce = numbers.flat()
console.log(numbersflattenOnce) // [1, 2, 3, 4, Array[2], Array[1]]const numbersflattenTwice = numbers.flat(2)
console.log(numbersflattenTwice) // [1, 2, 3, 4, 5, Array[2], Array[1]]const numbersFlattenInfinity = numbers.flat(Infinity)
console.log(numbersFlattenInfinity) // [1, 2, 3, 4, 5, 6, 7, 8]

flatMap (ES2019)
Guess what this method does? I bet you will understand one name.

First, it runs the mapping function for each element, and then shrinks the array at a time. Easy peasy!

const sentences = [
  'This is a sentence',
  'This is another sentence',
  "I can't find any original phrases",
]
const allWords = sentences.flatMap(sentence => sentence.split(' '))
console.log(allWords) // ["This", "is", "a", "sentence", "This", "is", "another", "sentence", "I", "can't", "find", "any", "original", "phrases"]

In this example, you have many sentences in the array and you want to get all the words. Instead of using the method map and dividing all sentences into words, and then shortening the array, you can use immediately flatMap.

Then you can count the number of words with the function reduce (this does not apply flatMap, I just want to show you another example of using the method reduce).

const wordsCount = allWords.reduce((count, word) => {
  count[word] = count[word] ? count[word] + 1 : 1return count
}, {})
console.log(wordsCount) // { This: 2, is: 2, a: 1, sentence: 2, another: 1, I: 1, "can't": 1, find: 1, any: 1, original: 1, phrases: 1, }

The method is flatMap also often used in reactive programming. You can see an example here .

join
If you need to create a string based on array elements, the method join is what you need. It allows you to create a new line by connecting all elements of the array, separated by the provided separator.

For example, with the help join you can visually display all the participants in the activity.

const participants = ['john', 'mary', 'gary']
const participantsFormatted = participants.join(', ')
console.log(participantsFormatted) // john, mary, gary

And this is a more realistic example where you can first filter the participants and get their names.

const potentialParticipants = [
  { id: 'k38i', name: 'john', age: 17 },
  { id: 'baf3', name: 'mary', age: 13 },
  { id: 'a111', name: 'gary', age: 24 },
  { id: 'fx34', name: 'emma', age: 34 },
]
const participantsFormatted = potentialParticipants
  .filter(user => user.age > 18)
  .map(user => user.name)
  .join(', ')
console.log(participantsFormatted) // gary, emma

from
This is a static method that creates a new array from an array-like or iterable object, such as a string. It can come in handy when you work with the document object model.

const nodes = document.querySelectorAll('.todo-item') // this is an instance of NodeListconst todoItems = Array.from(nodes) // now, you can use map, filter, etc. as you're workin with an array!

Did you see that we used an array type instead of an array instance? That is why this method is called static.

Then you can have fun with the nodes, for example registering event listeners for each of them using the method forEach.

todoItems.forEach(item => {
  item.addEventListener('click', function() {
    alert(`You clicked on ${item.innerHTML}`)
  })
})

Array Modification Worth Knowing About


The following are other standard methods. Their difference is that they modify the original array. There is nothing wrong with the change, but you should consider this when working.

If you do not want to modify the original array while working with these methods, make a surface or full copy in advance.

const arr = [1, 2, 3, 4, 5] 
const copy = [...arr] // or arr.slice()

sort
Yes, sort modifies the original array. In fact, it sorts the elements of the array in place. The default sorting method transforms all elements into strings and sorts them in alphabetical order.

const names = ['john', 'mary', 'gary', 'anna']
names.sort()
console.log(names) // ['anna', 'gary', 'john', 'mary']

Be careful: if you, for example, switched from the Python language, then the method sort when working with an array of numbers will not give you the desired result.

const numbers = [23, 12, 17, 187, 3, 90]
numbers.sort()
console.log(numbers) // [12, 17, 187, 23, 3, 90] 

How then to sort an array? The method sort takes one function - the comparison function . It takes two parameters: the first element ( а) and the second element for comparison ( b). A comparison between these two elements requires a digit to be returned:

  • if the value is negative - asorted before b;
  • if the value is positive, it is bsorted before a;
  • if the value is 0, no change.

Then you can sort the numbers.

const numbers = [23, 12, 17, 187, 3, 90]
numbers.sort((a, b) => a - b)
console.log(numbers) // [3, 12, 17, 23, 90, 187]

Or you can sort the dates from the latest.

const posts = [
  {
    title: 'Create a Discord bot under 15 minutes',
    date: newDate(2018, 11, 26),
  },
  {
    title: 'How to get better at writing CSS', 
    date: newDate(2018, 06, 17) },
  { 
    title: 'JavaScript arrays', 
    date: newDate() 
  },
]
posts.sort((a, b) => a.date - b.date) // Substracting two dates returns the difference in millisecond between themconsole.log(posts)
// [ { title: 'How to get better at writing CSS',//     date: 2018-07-17T00:00:00.000Z },//   { title: 'Create a Discord bot under 15 minutes',//     date: 2018-12-26T00:00:00.000Z },//   { title: 'Learn Javascript arrays the functional way',//     date: 2019-03-16T10:31:00.208Z } ]

fill
The method fill changes or fills all elements of the array from the start index to the end with the specified value. An example of excellent use fill is populating a new array with initial data.

// Normally I would have called a function that generates ids and random names but let's not bother with that here.functionfakeUser() {
  return {
    id: 'fe38',
    name: 'thomas',
  }
}
const posts = Array(3).fill(fakeUser())
console.log(posts) // [{ id: "fe38", name: "thomas" }, { id: "fe38", name: "thomas" }, { id: "fe38", name: "thomas" }]

reverse
It seems to me that the name of the method fully explains its essence.

const numbers = [1, 2, 3, 4, 5]
numbers.reverse()
console.log(numbers) // [5, 4, 3, 2, 1]

pop
This method removes the last element from the array and returns it.

const messages = ['Hello', 'Hey', 'How are you?', "I'm fine"]
const lastMessage = messages.pop()
console.log(messages) // ['Hello', 'Hey', 'How are you?']console.log(lastMessage) // I'm fine

Methods that can be replaced


In the last section, you will find methods that modify the original array and which are easy to find an alternative. I am not saying that they need to be discounted, I just want to convey to you that some methods have side effects and can be replaced.

push
This method is used frequently. It allows you to add one or more elements to the array, as well as build a new array based on the previous one.

const todoItems = [1, 2, 3, 4, 5]
const itemsIncremented = []
for (let i = 0; i < items.length; i++) {
  itemsIncremented.push(items[i] + 1)
}
console.log(itemsIncremented) // [2, 3, 4, 5, 6]const todos = ['Write an article', 'Proofreading']
todos.push('Publish the article')
console.log(todos) // ['Write an article', 'Proofreading', 'Publish the article']

If you need to build an array based on another, as in the method itemsIncremented, there are suitable for this and already familiar to us map, filter or reduce. For example, we can take mapto do this.

const itemsIncremented = todoItems.map(x => x + 1)

And if you want to use pushit when you need to add a new element, then the spread operator is useful.

const todos = ['Write an article', 'Proofreading'] console.log([...todos, 'Publish the article'])

splice
K is splice often referred to to remove an element at a specific index. You can do the same with the method filter.

const months = ['January', 'February', 'March', 'April', ' May']
// With splice
months.splice(2, 1) // remove one element at index 2console.log(months) // ['January', 'February', 'April', 'May']// Without spliceconst monthsFiltered = months.filter((month, i) => i !== 3)
console.log(monthsFiltered) // ['January', 'February', 'April', 'May']

You ask: what if I need to remove a lot of elements? Then use slice.
const months = ['January', 'February', 'March', 'April', ' May']
// With splice
months.splice(1, 3) // remove thirds element starting at index 1console.log(months) // ['January', 'February', 'April', 'May']// Without spliceconst monthsFiltered = [...months.slice(0, 1), ...months.slice(4)]
console.log(monthsFiltered) // ['January', 'February', 'April', 'May']

shift
The method shift removes the first element of the array and returns it. To do this in the style of functional programming, you can use the spread or rest statement.

const numbers = [1, 2, 3, 4, 5]
// With shiftconst firstNumber = numbers.shift()
console.log(firstNumber) // 1console.log(numbers) // [2, 3, 4, 5]// Without shiftconst [firstNumber, ...numbersWithoutOne] = numbers
console.log(firstNumber) // 1console.log(numbersWithoutOne) // [2, 3, 4, 5]

unshift
The unshift method allows you to add one or more elements to the beginning of an array. As in shift, you can do this using the spread operator.

const numbers = [3, 4, 5]
// With unshift
numbers.unshift(1, 2)
console.log(numbers) // [1, 2, 3, 4, 5]// Without unshiftconst newNumbers = [1, 2, ...numbers]
console.log(newNumbers) // [1, 2, 3, 4, 5]

TL; DR


  • When you want to perform some operations with an array, do not use the for loop and do not reinvent the wheel, because most likely there is a method from the above that can do what you need.
  • Most often you will use the methods map, filter, reduce and operator of spread - an important tool for any developer.
  • There are also many methods for arrays that are good to know: slice, some, flatMap,, etc. Meet with them and use as needed...
  • Side effects can lead to unwanted changes. Keep in mind that some methods modify your original array.
  • The slice spread method and operator make shallow copies. As a result, objects and subarrays will have the same links - this is also worth keeping in mind.
  • Old methods that modify the array can be replaced with new ones. You decide what to do.

Now you know everything you should know about JavaScript arrays. If you liked this article , click on the "Pat" button (up to 50 times, if you want :-)) and share it. And feel free to share your impressions in the comments!

Also popular now: