📜 ⬆️ ⬇️

What you need to know about JavaScript arrays

We present you a translation of the article by the author Thomas Lombart, which was published on the website medium.freecodecamp.org. The translation is published with the permission of the author.


An example of using the reduce method to shorten an array

Let me make a bold statement: loops are often useless and difficult to read the code. For iterations in arrays, searching, sorting elements and other similar actions, you can use one of the methods below.
')
Despite their 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 ones. Consider this article your guide to JavaScript array methods.

Note : Before we begin, 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 give up on changing the array in general, but it is worth considering that some methods lead to this. As a result, there are side effects, undesirable changes and, as a result, bugs.

Initially, this article was published on thomlom.dev - there you can find more materials on web development.

The basics


There are four methods that you should know about if you work with arrays. These are map , filter , reduce and spread operator. They are effective and helpful.

map
You will often use the map method. 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 a map .

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

Like map , filter takes as its only parameter a function that is called on each element of the array. This function should return a boolean value:


As a result, you will have the right new array with the items you wanted 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 an array.

 const participants = [ { id: 'a3f47', username: 'john' }, { id: 'fek28', username: 'mary' }, { id: 'n3j44', username: 'sam' }, ] function removeParticipant(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 lot of opportunities.

Usually the reduce method takes an array of values ​​and combines them into one value. It takes two parameters, a callback function (which is a gearbox ) and an optional initial value (which is the first element of the array by default). The gearbox itself takes four parameters:


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

But let's not go deep into the theory and consider the most common example of the use of 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 of 37. The returned value is 37 + n, where n = 12. We get 49.

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

The reduce method is so functional that you can use it to build a variety of array methods like map or 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 initial value [] to the reduce method, the battery. For map we run a function, the result of which is added to the end of the battery using the spread operator (we’ll talk about it below, do not worry). For filter do almost the same thing, only we 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: strongly reduce the array [1, 2, 3, [4, [[[5, [6, 7]]]], 8]] to [1, 2, 3, 4, 5, 6, 7, 8] .

 function flatDeep(arr) { return arr.reduce((flattenArray, element) => { return Array.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 it goes beyond our theme, but if you want to learn more, go to this great resource .

Operator spread (ES2015)
I agree, this is not a method. However, the spread operator helps to 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] 

Attention : the spread operator 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 indeed duplicated. However, with objects and arrays, the situation is different: only the reference to the original value is copied. Therefore, if you make a shallow copy of the array that includes the object, and change the object in the copied array, it will also be changed in the original, because they have the same link .

 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 we should not consider ourselves obliged 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 and which can be useful for solving problems such as finding an element in an array, removing part of an array, and more.

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

Fortunately, the includes method does the checking for us. Specify the parameter for includes, and it will search the item by 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 want function concatAll(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 forEach method. It takes a function as a parameter, which, in turn, also takes three parameters: the current value, the index, and the 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 an element can be found in an array. Also using indexOf often check 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 find method is similar to filter . You need to provide it with a function that tests each element of the array. However, find stops testing elements as soon as it finds one that passed the test. This is not a filter that iterates over the entire array, regardless of the 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 filter method when you want to filter the entire array, and the find method when 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 

You might think that findIndex and indexOf are the same thing. Not really. The first parameter of indexOf is a primitive value (boolean, number, string, undefined value, or character), while the first parameter findIndex is a callback function.

Therefore, when you need to find an element index 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 call the slice method. But be careful: as the spread operator, 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 cycles 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 need to display only five of them. Below are two approaches: one with cycles, the other with the slice method.

 // The "traditional way" to do it: // Determine the number of messages to take and use a for loop const nbMessages = messages.length < 5 ? messages.length : 5 let 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 array const messagesToShow = messages.slice(0, 5) 

some
If you want to check whether the test passes at least one element of the array, you can use some . Like map , filter or find , the some method takes a callback function as the only parameter, and then returns true if at least one element passes the test, and false if 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 the 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 by its one name.

First, it starts the mapping function for each element, and then shrinks the array in one go. 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 a lot of sentences in the array and you want to get all the words. Instead of using the map method and dividing all sentences into words, and then shortening the array, you can immediately use flatMap .

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

 const wordsCount = allWords.reduce((count, word) => { count[word] = count[word] ? count[word] + 1 : 1 return 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 flatMap method flatMap also often used in reactive programming. An example you can see here .

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

For example, using join you can visually display all 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 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 be useful when you work with the object model of the document.

 const nodes = document.querySelectorAll('.todo-item') // this is an instance of NodeList const 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, register event listeners for each of them using the forEach method.

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

Methods that change the array, which is worth knowing


The following are other standard methods. Their difference is that they change the original array. There is nothing wrong with changing, but it is worth considering this when working.

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

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

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

 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, the sort method 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 sort method 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 the return of a digit:


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: new Date(2018, 11, 26), }, { title: 'How to get better at writing CSS', date: new Date(2018, 06, 17) }, { title: 'JavaScript arrays', date: new Date() }, ] posts.sort((a, b) => a.date - b.date) // Substracting two dates returns the difference in millisecond between them console.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 fill method changes or fills all elements of an array from the initial index to the final one with the specified value. An example of excellent use of fill is to fill 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. function fakeUser() { 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 make it easy to find an alternative. I'm 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 an 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 itemsIncremented method, there are suitable for this and already familiar to us map , filter or reduce . For example, we can take a map to do this.

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

And if you want to use push when you need to add a new element, then the spread operator comes in handy.

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

splice
splice often referenced to remove an element on a specific index. You can do the same with the filter method.

 const months = ['January', 'February', 'March', 'April', ' May'] // With splice months.splice(2, 1) // remove one element at index 2 console.log(months) // ['January', 'February', 'April', 'May'] // Without splice const 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 1 console.log(months) // ['January', 'February', 'April', 'May'] // Without splice const monthsFiltered = [...months.slice(0, 1), ...months.slice(4)] console.log(monthsFiltered) // ['January', 'February', 'April', 'May'] 

shift
The shift method 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 operator.

 const numbers = [1, 2, 3, 4, 5] // With shift const firstNumber = numbers.shift() console.log(firstNumber) // 1 console.log(numbers) // [2, 3, 4, 5] // Without shift const [firstNumber, ...numbersWithoutOne] = numbers console.log(firstNumber) // 1 console.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 with the spread operator.

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

TL; DR



, JavaScript. , «» ( 50 , :-)) . !

Source: https://habr.com/ru/post/446902/


All Articles