Arrays in JavaScript
An array is a variable that contains a list of data, where each element has its own index and can be accessed through that index. Array indexing starts from zero. An array can contain different types of data: other arrays (multidimensional arrays), objects, numbers, strings.
Arrays allow storing and processing large lists of data, for example, a list of products in an online store or storing a user profile.
Example of an array with different data types:
const mixedArray = [ 42, // Number "Hello", // String true, // Boolean null, // null undefined, // undefined { name: "John", age: 30 }, // Object [1, 2, 3], // Array function() { return "Hi"; }, // Function new Date(), // Date /pattern/, // RegExp Symbol('id'), // Symbol 123n, // BigInt NaN, // NaN Infinity // Infinity ]; console.log(mixedArray);
// 1. Array of strings (names): const names = ["Olya", "Andriy", "Maria", "Taras", "Irina"]; // 2. Array of numbers: const ages = [18, 25, 30, 42, 57]; // 3. Mixed array (different data types): const mixed = [ true, "JS", 2023, null, { skill: "coding" } ]; // 4. Array of objects: const products = [ { id: 1, name: "Laptop", price: 20000 }, { id: 2, name: "Smartphone", price: 10000 }, { id: 3, name: "Headphones", price: 800 } ]; // 5. Two-dimensional (nested) array: const matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ];
Examples of arrays of strings, numbers, mixed arrays, arrays of objects, and nested (two-dimensional) arrays
Values in an array can be changed: deleted, replaced, or added. Changes can be made to the original array using special methods that exist in JS for working with arrays. These methods are divided into two groups: those that mutate the array they are applied to and those that process the array and return a new one, leaving the original array unchanged.
Methods that mutate the array
push()
— adds one or more elements to the end of the array. Returns the new length of the array.pop()
— removes the last element from the array and returns it.shift()
— removes the first element from the array and returns it.unshift(…elements)
— adds one or more elements to the beginning of the array.splice(
start, deleteCount ,…items, …items,…items)
— adds, removes, or replaces elements in the array at the specified index. Returns an array of removed elements.reverse()
— reverses the order of elements in the array. Returns a reference to the modified array.sort(compareFunction)
— sorts the elements of the array (as strings by default). Returns a reference to the sorted array.fill(value, start, end)
— fills all elements of the array from the start index to the end index with a single value.copyWithin(target, start, end)
– copies a sequence of array elements within the array from positions[start, end)
to thetarget
position. Returns a reference to the modified array.
Methods that do not mutate the array
concat()
— returns a new array that is the result of merging two or more arrays.slice()
— returns a new array that contains a copy of part of the original array.map()
— returns a new array that is the result of applying a function to each element of the original array.filter()
— returns a new array that contains elements that satisfy a condition.reduce()
— returns a single value that is the result of applying a function to each element of the array.reduceRight()
— similar toreduce()
, but processes the array from right to left.flat()
— returns a new array with all subarrays “flattened” to the specified depth.flatMap()
— combinesmap()
andflat()
, returns a new array.join()
— returns a string that is the result of joining all elements of the array.toString()
— returns a string representation of the array.indexOf()
— returns the index of the first found element or-1
if the element is not found.lastIndexOf()
— returns the index of the last found element or-1
if the element is not found.includes()
— returnstrue
orfalse
, depending on whether the element is in the array.find()
— returns the first element that satisfies the condition, orundefined
.findIndex()
— returns the index of the first element that satisfies the condition, or-1
.every()
— returnstrue
if all elements satisfy the condition.some()
— returnstrue
if at least one element satisfies the condition.
More about the main array methods
Main methods and code examples for adding and removing elements
push()
: Adds one or more elements to the end of the array and returns the new length of the array. Mutates the array.
pop()
: Removes the last element from the array and returns the removed element. Mutates the array.
shift()
: Removes the first element from the array and returns the removed element. Mutates the array.
unshift()
: Adds one or more elements to the beginning of the array and returns the new length of the array. Mutates the array.
const numbers = [1, 2, 3]; // Add to the end numbers.push(4, 5); // numbers = [1, 2, 3, 4, 5] // Remove the last const removed = numbers.pop(); // removed = 5, numbers = [1, 2, 3, 4] // Remove the first numbers.shift(); // numbers = [2, 3, 4] // Add to the beginning numbers.unshift(0); // numbers = [0, 2, 3, 4]
How to create a copy of an array
Arrays are reference types in JavaScript. For example, if you do const arr2 = arr1;
, this does not create a new array but only a reference to the existing one.
If you need to work with an array without changing it, you can use non-mutating methods or create a copy of the array using slice()
or the spread operator (...
):
const copy = [...arr]; // copy of the array
The slice()
method allows you to create a new array that is a copy of part or all of the original array. It takes two arguments:
start (optional) — the index at which to start copying (inclusive).
end (optional) — the index at which to end copying (exclusive).
If no arguments are passed, slice() returns a full copy of the array.
Examples:
Full copy of the array
const arr = [1, 2, 3, 4, 5]; const copy = arr.slice(); // [1, 2, 3, 4, 5] console.log(copy); // [1, 2, 3, 4, 5] console.log(arr === copy); // false (these are different arrays)
Copying part of the array
const arr = [1, 2, 3, 4, 5]; const slicedCopy = arr.slice(2, 4); console.log(slicedCopy) //[ 3, 4 ];
Negative indices with a single value
const arr = [1, 2, 3, 4, 5]; const part = arr.slice(-3); // [3, 4, 5] console.log(part); // [3, 4, 5]
Negative indices are counted from the end of the array.
Negative indices with two values
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant']; console.log(animals.slice(2, -1)); // Array ["camel", "duck"]
Three ways to create an array
Array literal
// 'fruits' array created using array literal notation const fruits = ["Apple", "Banana"]; console.log(fruits.length); // 2
Array() constructor
// 'fruits' array created using array literal notation const fruits = ["Apple", "Banana"]; console.log(fruits.length); // 2
Methods like Array.from()
, Array.of()
, Spread operator (...
), split()
for converting strings to arrays, map()
, fill()
and other non-mutating methods listed above.
Example using the from()
method:
//From a string: const arr = Array.from("Hello"); console.log(arr); // ['H', 'e', 'l', 'l', 'o'] //From a set: const set = new Set([1, 2, 3]); const arr = Array.from(set); console.log(arr); // [1, 2, 3] //From an array-like object: const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 }; const arr = Array.from(arrayLike); console.log(arr); // ['a', 'b', 'c'] //With a mapper function: const arr = Array.from([1, 2, 3], x => x * 2); console.log(arr); // [2, 4, 6]
The Set
object in the example above is a built-in JavaScript collection that represents a set of unique values. It is similar to an array but has several key differences. Let’s take a closer look at what a Set
is, how it works, and how it relates to objects and arrays.
Set
is a collection that stores only unique values. This means that it cannot contain two identical elements. For example, if you add the number 5
twice, it will only be stored once.
Key Features:
- Uniqueness: All values in a
Set
are unique. - Iterability:
Set
can be iterated using loops or methods likeforEach
. - No Indexes: Unlike arrays,
Set
does not have indexes, so elements cannot be accessed by index. - Methods for Element Manipulation:
Set
has methods for adding, deleting, and checking the presence of elements.
How to Create a Set
?
A Set
is created using the new Set()
constructor. You can pass an iterable object (e.g., an array) to the constructor, and the Set
will automatically add unique elements from that object.
For example, here the number 4
is added only once because Set
stores only unique values:
const mySet = new Set([1, 2, 3, 4, 4, 5]); console.log(mySet); // Set { 1, 2, 3, 4, 5 }
Key Methods and Properties of Set
add(value)
— adds a value to the Set
.
const mySet = new Set(); mySet.add(1); mySet.add(2); console.log(mySet); // Set { 1, 2 }
delete(value)
— removes a value from the Set
.
mySet.delete(1); console.log(mySet); // Set { 2 }
has(value)
— checks if a value is present in the Set
.
console.log(mySet.has(2)); // true console.log(mySet.has(3)); // false
clear()
— clears the Set
, removing all elements.
mySet.clear(); console.log(mySet); // Set {}
size
— returns the number of elements in the Set
.
const mySet = new Set([1, 2, 3]); console.log(mySet.size); // 3
How is Set
Related to Arrays and Objects?
1. Relation to Arrays: A Set
can be created from an array to automatically remove duplicates.
const arr = [1, 2, 2, 3, 4, 4, 5]; const uniqueSet = new Set(arr); console.log(uniqueSet); // Set { 1, 2, 3, 4, 5 }
A Set
can be converted back to an array using Array.from()
or the spread operator (...
):
const uniqueArr = Array.from(uniqueSet); // or const uniqueArr = [...uniqueSet]; console.log(uniqueArr); // [1, 2, 3, 4, 5]
2. Relation to Objects: Objects can be added to a Set
, and they will be stored as unique elements. However, objects are considered different even if they have the same content:
const obj1 = { name: "John" }; const obj2 = { name: "John" }; const mySet = new Set(); mySet.add(obj1); mySet.add(obj2); console.log(mySet.size); // 2 (objects are considered different)
Examples of Using Set
1. Removing Duplicates from an Array:
const arr = [1, 2, 2, 3, 4, 4, 5]; const uniqueArr = [...new Set(arr)]; console.log(uniqueArr); // [1, 2, 3, 4, 5]
2. Checking for the Presence of an Element:
const mySet = new Set([1, 2, 3]); console.log(mySet.has(2)); // true
3. Storing Unique Objects:
const obj1 = { name: "John" }; const obj2 = { name: "Jane" }; const mySet = new Set(); mySet.add(obj1); mySet.add(obj2); console.log(mySet.size); // 2
The Array.of()
method
creates an array from the provided arguments. It is useful when you need to create an array from a single number (unlike new Array()
, which in such a case would create an empty array with the specified length).
const arr = Array.of(5); console.log(arr); // [5]
Spread operator (...
)
allows you to create a new array based on an existing array or another iterable object.
const original = [1, 2, 3]; const copy = [...original]; console.log(copy); // [1, 2, 3]
Merging arrays:
const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = [...arr1, ...arr2]; console.log(combined); // [1, 2, 3, 4]
From an iterable object:
const str = "Hello"; const arr = [...str]; console.log(arr); // ['H', 'e', 'l', 'l', 'o']
The split()
method
If you have a string that needs to be converted into an array, you can use the split()
method.
const str = "apple,banana,orange"; const arr = str.split(','); console.log(arr); // ['apple', 'banana', 'orange']
Generating an array using Array()
and map()
const arr = [1, 2, 3, 4, 5]; const part = arr.slice(-3); // [3, 4, 5] console.log(part); // [3, 4, 5]
If you need to create an array with a specific number of elements that are dynamically generated, you can use a combination of Array()
, fill()
and map()
.
Array of numbers from 0 to 9:
const arr = Array(10).fill().map((_, index) => index); console.log(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Array of squared numbers:
const arr = Array(5).fill().map((_, index) => (index + 1) ** 2); console.log(arr); // [1, 4, 9, 16, 25]
Generating an array using Array.from()
and a mapper function
The Array.from()
method also allows you to create an array with a mapper function that generates elements.
Array of numbers from 0 to 9:
const arr = Array.from({ length: 10 }, (_, index) => index); console.log(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //creates an array with 10 elements, where each element equals its index. //_ — is a placeholder for an unused argument (current element). //index — is the index of the current element, which is returned as the array element value. //The function returns the value that will be written to the array at the current element's position.
Array of even numbers:
const arr = Array.from({ length: 5 }, (_, index) => index * 2); console.log(arr); // [0, 2, 4, 6, 8]
What does _
mean?
In JavaScript, _
is simply a variable name. In this context, it is used to denote an argument that is not used. This is a common practice to indicate that the value is ignored. For example: const func = (_, index) => index;
This function takes two arguments but only uses index
. The first argument (_
) is ignored. If you don’t want to use _
, you can simply omit the first argument: const arr = Array.from({ length: 10 }, (index) => index);
When to use array literal and when to use constructor?
When to use array literal ([]
)?
- Most cases: array literal is the standard and most common way to create arrays. It is shorter, clearer, and less error-prone.
- When you need to create an array with known elements: if you know the array elements in advance, array literal is the best choice.
- When you need to create an empty array: array literal is also suitable for creating an empty array.
- When code readability is important: array literal is more intuitive and understandable for other developers.
When to use the Array()
constructor?
When you need to create an array with a specified length:
- When you need to create an array with a specified length: the
Array()
constructor is useful when you need to create an array with a certain number of empty elements (e.g., for later filling). - When you need to dynamically create an array: if the array length or its elements are determined dynamically, the
Array()
constructor can be useful. - When you need to create an array from a single number: if you need to create an array from a single number, it’s better to use
Array.of()
, but theArray()
constructor can also be used. - When compatibility with old code is needed: in very old projects or for compatibility with older versions of JavaScript, using the constructor may be necessary.
Array iteration
Array iteration is used to sequentially process each element of an array. In particular, it allows you to:
- View or output array values (e.g.,
console.log()
). - Modify each element as needed (e.g., multiply all numbers by 2).
- Collect the necessary data into a new structure (e.g., create another array or calculate a total value).
- Filter the array, selecting only those elements that meet a certain condition.
- Transform data using various methods (e.g.,
map
,reduce
,filter
) and obtain a new result.
Iteration helps perform almost any operation on each element of an array — from simple checks to complex data processing.
Examples of array iteration
for
loop
This is the most basic method for iterating over an array. It allows you to create a counter variable, set conditions for continuing the iteration, and increment the counter.
Task find all positive numbers in the array, multiply each by 2, and save the result in a new array.
const arr = [2, -1, 5, 0, 7]; const positiveDoubled = []; for (let i = 0; i < arr.length; i++) { if (arr[i] > 0) {positiveDoubled.push(arr[i] * 2);}} console.log(positiveDoubled); // Result: [4, 10, 14]
Explanation:
- Initialize an empty array
positiveDoubled
. - Using the counter
i
, iterate over all indices ofarr
. - Check if the element (arr[i]) is positive (
> 0
). - If so, multiply it by 2 and add (
push
) it to the new array. - In the end, we have an array with only positive numbers doubled.
for...of
loop
A convenient syntax for iterating over array elements without the need to access indices.
Task build a new array of squares of each element (negative numbers will also be squared).
const arr = [2, -1, 5, 0, 7]; const squares = []; for (const item of arr) { squares.push(item * item);} console.log(squares); // Result: [4, 1, 25, 0, 49]
Explanation:
for...of
iterates directly over the values of the array elements, soitem
is already the element (without the index).- For each
item
, square ititem * item
. - Add the result to the
squares
array. - As a result, we get the array
[4, 1, 25, 0, 49]
.
while
loop
while
The while
loop executes only when the condition is true. The condition is checked before executing the loop body, which means that if the condition is not met initially, the loop may not execute at all.
Task find all positive numbers in the array, multiply each by 2, and save the result in a new array.
const arr = [1, 2, 3, 4]; // Array to iterate over. let i = 0; // Initialize the array index counter. Initially, it is 0. while (i < arr.length) { // Loop condition: as long as i is less than the array length, we will iterate. console.log(arr[i]); // Output the array element at index i. i++; // Increment the counter i by 1. }
Explanation:
- Array
arr = [1, 2, 3, 4]
contains four elements. We will iterate over this array. - We create a variable
i
and initialize it with the value0
, which corresponds to the index of the first element of the array. - The
while
loop checks the condition: is the value of the variablei
less than the length of the array (arr.length
). - Since the array has 4 elements, the condition will be true as long as
i
is not equal to 4. - Inside the loop, we output the array element pointed to by index
i
usingconsole.log(arr[i])
. - Each time the loop executes, the index
i
is incremented by 1 (i++
) to move to the next element of the array. - Once the value of
i
reaches 4 (the length of the array), the loop condition becomes false, and the loop ends.
do…while
loop
The do...while
loop, unlike while
, executes at least once, even if the condition was not met. The condition is checked after executing the loop body, so the loop body always executes at least once, regardless of the condition.
Task build a new array of squares of each element (negative numbers will also be squared).
const arr = [1, 2, 3, 4]; // Array to iterate over. let i = 0; // Initialize the array index counter. do { console.log(arr[i]); // Output the array element at index i. i++; // Increment the counter i by 1. } while (i < arr.length); // Condition: as long as i is less than the array length, iteration continues.
Explanation:
- Array
arr = [1, 2, 3, 4]
again contains four elements. We create a variablei
and initialize it with the value0
. Thedo...while
loop guarantees that the loop body will execute at least once, even if the condition was initially false. The loop body executes: - Output the array element pointed to by index
i
. - Increment
i
to move to the next element of the array. - The condition is checked after the loop body executes. If
i
is still less than the array length, the loop continues. Oncei
becomes equal to 4 (the length of the array), the condition becomes false, and the loop ends.
forEach()
loop
forEach()
An array method that calls the provided function for each element of the array. It does not return a value and does not allow changing indices.
const arr = [1, 2, 3, 4]; arr.forEach(function(num) { console.log(num); // Outputs 1, 2, 3, 4 });
Explanation:
forEach()
takes a function as an argument. This function is called for each element of the array.- In the function, we pass the variable
num
, which at each step of the loop will be equal to the current array element. - Output each element using
console.log(num)
.
map()
method
A method that creates a new array with the results of calling the provided function on each element of the original array.
const arr = [1, 2, 3, 4]; const doubled = arr.map(num => num * 2); console.log(doubled); // Outputs [2, 4, 6, 8]
Explanation:
map()
applies the function to each element of the array and creates a new array with the results of these operations.- We multiply each array element by 2 and get a new array with doubled values.
- It is important that the
map()
method does not modify the original array.
The filter()
Method
The filter()
method creates a new array with all elements that satisfy the condition specified in the function.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; const evenArr = arr.filter(function(num) { return num % 2 === 0; // Returns true if the number is even }); console.log(evenArr); // Output: [2, 4, 6, 8]
Explanation:
filter()
checks each array element against the condition we specify in the function.- In our case, we check if the number is even using the condition
num % 2 === 0
. - The returned array contains only the elements that satisfy the condition (in this case, even numbers).
The reduce()
Method
This method reduces an array to a single value by calling a function for each element and returning the result.
const arr = [1, 2, 3, 4, 5]; const sum = arr.reduce(function(acc, num) { return acc + num; // Adds the current value to the accumulator }, 0); console.log(sum); // Output: 15
Explanation:
reduce()
takes a function that works with two arguments:acc
(accumulator): This is the value that accumulates during the array iteration.num
(current element): This is the current element of the array.- The first parameter of the
reduce()
function defines the initial value of the accumulator. In our case, it is0
. - At each step, the function adds the current element to the accumulator, and at the end, we get the sum of all array elements.
Array Destructuring and the Spread Operator
Array destructuring is a syntax in JavaScript that allows “unpacking” values from arrays into separate variables. This is a very convenient way to access array elements without manually referring to them by indices. It is used when you need to extract multiple elements from an array. If you need to work with many array elements, then destructuring is not used, and instead, other array methods and iterations, explained above, are used.
Destructuring Syntax
Suppose we have an array:
const arr = [1, 2, 3];
Usually, to get array elements, we do this:
const first = arr[0]; const second = arr[1]; const third = arr[2];
With destructuring, this can be done much simpler:
const [first, second, third] = arr;
Now:
first
equals1
,second
equals2
,third
equals3
.
Spread operator (...
) allows unpacking elements from arrays or objects into new data structures. It is used for copying, merging arrays, adding new elements to arrays, and passing elements as function arguments. The spread operator can be very useful for working with arrays as it allows manipulating data in a compact and understandable way.
General syntax:
const newArray = [...existingArray];
The spread operator allows creating a copy of an array. This is useful when you need to modify the new array without changing the original. For example:
const arr1 = [1, 2, 3]; const arr2 = [...arr1]; // Copy all elements from arr1 to arr2 arr2.push(4); // Add a new element to arr2 console.log(arr1); // Output: [1, 2, 3] console.log(arr2); // Output: [1, 2, 3, 4]
Explanation:
- We use the spread operator to copy all elements of the array
arr1
into a new arrayarr2
. - Since this is a shallow copy, changes in
arr2
do not affectarr1
.
The spread operator allows merging two or more arrays into one. For example:
const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const combinedArr = [...arr1, ...arr2]; // Merge two arrays console.log(combinedArr); // Output: [1, 2, 3, 4, 5, 6]
You can use the spread operator to add elements in the middle of an array or at the beginning or end:
const arr = [2, 3, 4]; // Add an element at the beginning of the array const newArrStart = [1, ...arr]; console.log(newArrStart); // Output: [1, 2, 3, 4] // Add an element at the end of the array const newArrEnd = [...arr, 5]; console.log(newArrEnd); // Output: [2, 3, 4, 5]
Explanation:
- First, we add the element
1
at the beginning of the arrayarr
using the spread operator. - Then we add the element
5
at the end of the arrayarr
using the spread operator.
The spread operator can be used to “unpack” array elements in functions or constructors.
const arr = [1, 2, 3, 4]; function sum(a, b, c, d) { return a + b + c + d; } console.log(sum(...arr)); // Output: 10
Explanation:
- We pass the array
arr
to the functionsum()
using the spread operator, which unpacks the array elements as separate arguments. - The function receives these elements as separate arguments
a
,b
,c
,d
and performs the operation on them.
The spread operator in functions with an indefinite number of arguments (Rest/Spread)
If you have a function that takes multiple parameters, the spread operator can help you gather arguments into an array. This is also known as the Rest operator. Example:
function printAll(...args) { console.log(args); } printAll(1, 2, 3, 4); // Output: [1, 2, 3, 4]
Explanation:
- The
...args
operator gathers all passed arguments into theargs
array. - In this case, all numbers are passed as an array.
Spread operator in combination with other methods
You can not only copy an array but also modify elements while copying using the spread operator. For example:
const arr = [1, 2, 3, 4]; // Create a new array where we replace the third element const modifiedArr = [...arr.slice(0, 2), 100, ...arr.slice(3)]; console.log(modifiedArr); // Output: [1, 2, 100, 4]
Explanation:
- First, using the
slice(0, 2)
method, we copy the first two elements. - Then we insert the value
100
at the third position. - Finally, we use
slice(3)
to copy elements from index 3 onwards.
Destructuring Examples
1. Skipping elements.
If you need to get only certain elements, you can skip others using commas:
const arr = [1, 2, 3, 4, 5]; const [first, , third] = arr; console.log(first); // 1 console.log(third); // 3
Here, we skipped the second element (index 1).
2. Destructuring with remaining elements
If you need to get the first few elements and collect the rest into a separate array, use the ...rest
syntax:
const arr = [1, 2, 3, 4, 5]; const [first, second, ...rest] = arr; console.log(first); // 1 console.log(second); // 2 console.log(rest); // [3, 4, 5]
The rest
variable contains all the remaining elements.
3. Default values
If an array element is missing or equals undefined
, you can specify a default value:
const arr = [1, 2]; const [first, second, third = 3] = arr; console.log(first); // 1 console.log(second); // 2 console.log(third); // 3 (default value)
4. Swapping variable values
Destructuring allows easy swapping of variable values:
let a = 1; let b = 2; [a, b] = [b, a]; console.log(a); // 2 console.log(b); // 1
5. Nested array destructuring
If an array contains other arrays, you can destructure them as well:
const arr = [1, [2, 3], 4]; const [first, [second, third], fourth] = arr; console.log(first); // 1 console.log(second); // 2 console.log(third); // 3 console.log(fourth); // 4
6. Destructuring during iteration
Destructuring can be useful when working with arrays in loops:
const users = [ { name: "John", age: 30 }, { name: "Jane", age: 25 }, ]; for (const { name, age } of users) { console.log(`${name} is ${age} years old.`); }
7. Destructuring from functions
Destructuring can also be used to get values returned from functions:
function getNumbers() { return [1, 2, 3]; } const [first, second, third] = getNumbers(); console.log(first); // 1 console.log(second); // 2 console.log(third); // 3
Advantages of Destructuring
- Code reduction: No need to manually access array elements by indices.
- Convenience: Easy to get values from nested structures.
- Readability: Code becomes more understandable and concise.
Limitations of Destructuring
- If the array is shorter than the number of variables, “extra” variables will get the value
undefined
. - If you try to destructure something that is not iterable (e.g.,
null
orundefined
), an error will occur.
Questions about Arrays You Should Know
How to merge two arrays?
For this, you can use the concat()
method or the spread operator:
const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = arr1.concat(arr2); // or const combined2 = [...arr1, ...arr2];
What is the difference between the spread operator and the rest operator?
Spread operator and rest operator may seem similar since both use the same syntax (...
). However, these terms are used in different contexts and have different applications.
1. Spread Operator
The spread operator is used to unpack elements from arrays or objects to pass them or insert them into other data structures.
Usage in arrays:
The spread operator is applied when you want to merge arrays, copy an array, or add elements to an array.
Example:
const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; // Merge two arrays into one const combinedArr = [...arr1, ...arr2]; console.log(combinedArr); // Output: [1, 2, 3, 4, 5, 6]
Usage in objects:
The spread operator can also be used to clone objects or merge objects.
Example:
const obj1 = { name: 'John', age: 30 }; const obj2 = { city: 'New York' }; // Merge two objects const combinedObj = { ...obj1, ...obj2 }; console.log(combinedObj); // Output: { name: 'John', age: 30, city: 'New York' }
2. Rest Operator
The rest operator is also the ...
syntax, but it is used to gather elements, not to “unpack” them. This allows collecting an indefinite number of arguments into an array or object.
Usage in arrays:
In the rest operator, we gather the remaining elements into an array.
Example:
function sum(a, b, ...rest) { console.log(a, b); // Output: 1 2 console.log(rest); // Output: [3, 4, 5] } sum(1, 2, 3, 4, 5);
In this example:
- The first two arguments (
a
andb
) receive the values1
and2
. - The
...rest
operator gathers the rest of the arguments into an array.
Usage in objects:
The rest operator can also be applied to objects to gather remaining properties.
Example:
const obj1 = { name: 'John', age: 30, city: 'New York' }; const { name, ...rest } = obj1; console.log(name); // Output: 'John' console.log(rest); // Output: { age: 30, city: 'New York' }
In this case:
- From the
obj1
object, we extracted thename
property, and the rest of the properties were gathered into therest
object.
Spread operator (...
) is used to unpack elements or properties.
Rest operator (...
) is used to gather elements or properties.
How to Work With Nested Arrays in JavaScript?
Nested arrays can be used to group related elements. For example, take a look at the following multidimensional array:
let multiDimensionalArray = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]; console.log(multiDimensionalArray[0][0][0]); // 1 console.log(multiDimensionalArray[1][1][2]); // 12 // adding elements to array multiDimensionalArray[0][1].push(13); console.log(multiDimensionalArray[0][1]); // [4, 5, 6, 13] // looping array for (let i = 0; i < multiDimensionalArray.length; i++) { for (let j = 0; j < multiDimensionalArray[i].length; j++) { for (let k = 0; k < multiDimensionalArray[i][j].length; k++) { console.log(multiDimensionalArray[i][j][k]); } } }
Easily update the array by accessing the index and reassigning its value:
multiDimensionalArray[0][1][2] = 77;
You can also reassign a whole array (and not just one of its values) to a particular position in the main array using the index. This example changes the first nested array from rabbit to chicken:
const animalPairs = [ ['doe', 'buck'], ['ewe', 'ram'], ['peahen', 'peacock'], ['cow', 'bull'], ] animalPairs[0] = ['hen', 'rooster']
Using nested arrays is less common in real-world use cases. It’s much more common to nest multiple objects in an array (e.g. an array of user objects, each with properties like id
, username
, and so on).
Objects in JavaScript
Objects in JavaScript are data structures that allow storing collections of key-value pairs. Keys (also called properties) are strings (or symbols), and values can be of any data type: numbers, strings, arrays, functions, other objects, etc. They are used for working with DOM, AJAX, and other APIs.
Creating Objects
1. Object Literal
The most common way to create an object is by using an object literal:
let person = { firstName: "John", lastName: "Doe", age: 30, isStudent: false, greet: function() { console.log("Hello, my name is " + this.firstName); } };
In this example, person
is an object with four properties: firstName
, lastName
, age
, isStudent
, and a method greet
.
2. Using the Object
Constructor
You can also create an object using the Object
constructor:
let person = new Object(); person.firstName = "John"; person.lastName = "Doe"; person.age = 30; person.isStudent = false; person.greet = function() { console.log("Hello, my name is " + this.firstName); };
This method is less common but also works.
3. Using Constructor Functions
You can create an object using a constructor function:
function Person(firstName, lastName, age, isStudent) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.isStudent = isStudent; this.greet = function() { console.log("Hello, my name is " + this.firstName); }; } let person = new Person("John", "Doe", 30, false);
This method is useful when you need to create many objects with the same structure.
Accessing Object Properties
You can access object properties in two ways:
1. Using dot notation
console.log(person.firstName); // "John" console.log(person.age); // 30
2. Using square brackets
console.log(person["firstName"]); // "John" console.log(person["age"]); // 30
Square brackets are useful when the property name is stored in a variable:
let key = "firstName"; console.log(person[key]); // "John"
Adding and Deleting Properties
You can add new properties to an object or delete existing ones:
Adding a property
person.email = "john.doe@example.com"; console.log(person.email); // "john.doe@example.com"
Deleting a property
delete person.age; console.log(person.age); // undefined
Example of existence check:
'name' in user; // true
Object Destructuring
Destructuring is a syntactic convenience in JavaScript that allows you to extract values from objects and arrays and assign them to variables in a single line of code. This makes the code more compact, readable, and expressive.
Basic Syntax of Object Destructuring:
const object = { key1: value1, key2: value2 }; const { key1, key2 } = object;
In this example:
object
is our source object.{ key1, key2 }
is the pattern that specifies which properties we want to extract from the object.const { key1, key2 } = object;
is the destructuring operation that assigns the values ofkey1
andkey2
to variables with the same names.
Example of Object Destructuring:
const person = { name: 'John Doe', age: 30, city: 'New York' }; const { name, age } = person; console.log(name); // Output: John Doe console.log(age); // Output: 30
You can rename variables during destructuring:
const person = { firstName: 'John', lastName: 'Doe' }; const { firstName: name, lastName: surname } = person; console.log(name); // Output: John console.log(surname); // Output: Doe
Object Methods
Methods are functions that are properties of an object. They can use this
to access other properties of the object.
let person = { firstName: "John", lastName: "Doe", greet: function() { console.log("Hello, my name is " + this.firstName); } }; person.greet(); // "Hello, my name is John"
Default Usage:
If a property is missing in the object, you can set a default value:
const person = { name: 'John' }; const { name, age = 25 } = person; console.log(age); // Output: 25
Destructuring also works with nested objects:
const user = { name: 'Alice', address: { city: 'London', street: 'Oxford Street' } }; const { name, address: { city } } = user; console.log(city); // Output: London
You can use destructuring to unpack function arguments:
function greet({ name, age }) { console.log(`Hello, ${name}! You are ${age} years old.`); } const person = { name: 'Bob', age: 35 }; greet(person);
Advantages of Destructuring:
- Improved Code Readability: The code becomes more concise and understandable.
- Reduced Code Lines: You can extract multiple values from an object in a single line.
- Better Maintenance: Destructuring is part of the ECMAScript 2015 standard and is supported by most modern browsers.
When to Use Destructuring:
- To extract values from objects and arrays.
- To pass function arguments.
- To create more readable and expressive constructs.
Object Methods and this
this
in a JavaScript object method is a special keyword that refers to the object itself within which the method is called. In other words, it is the execution context of the function that is a method of the object.
Illustrative Example:
const person = { firstName: "John", lastName: "Doe", fullName() { return this.firstName + " " + this.lastName; } }; console.log(person.fullName()); // Output: "John Doe"
In this example:
person
is the object.fullName
is a method of this object.this
inside thefullName
method refers to theperson
object. Therefore, when callingperson.fullName()
,this
will contain all the properties of theperson
object.
How JavaScript Determines the Value of this
?
Call as an Object Method:
When a method is called using dot notation after the object name (as in the example above), this
refers to that object.
Call as a Function:
If a method is called as a regular function, this
will refer to the global object (in the browser, this is window
).
Call in the Context of Another Object:
If a method is called in the context of another object using call()
or apply()
, this
will refer to that other object.
Arrow Functions:
In arrow functions, this
inherits the value from the outer lexical environment, rather than being determined at the time of the call.
Important to understand: the value of this
can change depending on the execution context. Misunderstanding this
can lead to errors in the code. this
is a powerful tool for creating object-oriented code in JavaScript.
Additional nuances: this
can be null
or undefined
in some cases, for example, when calling an arrow function in strict mode without an object. bind()
is a method that allows you to fix the value of this
for a function.
Iterating Over Object Properties
You can iterate over object properties using the for...in
loop:
for (let key in person) { console.log(key + ": " + person[key]); }
This code will output all keys and their values from the person
object.
Nested Objects
Objects can contain other objects:
let person = { firstName: "John", lastName: "Doe", address: { street: "123 Main St", city: "Anytown", country: "USA" } }; console.log(person.address.city); // "Anytown"
Copying Objects
Copying objects can be tricky because simple assignment is not enough for deep copying.
Shallow Copy
Shallow copying creates a new object but does not copy nested objects. Instead, nested objects remain references to the original objects. This means that if you modify a nested object in the copy, it will affect the original object, and vice versa.
let person = { firstName: "John", lastName: "Doe" }; let personCopy = Object.assign({}, person); console.log(personCopy); // { firstName: "John", lastName: "Doe" }
Deep Copy
Deep copying creates a completely new object, including all nested objects. After deep copying, changes in the copy do not affect the original, and vice versa. For deep copying, you can use JSON.parse
and JSON.stringify
:
let person = { firstName: "John", lastName: "Doe", address: { street: "123 Main St", city: "Anytown" } }; let personCopy = JSON.parse(JSON.stringify(person)); console.log(personCopy); // { firstName: "John", lastName: "Doe", address: { street: "123 Main St", city: "Anytown" } }
The static method JSON.stringify()
converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified, or including only specified properties if a replacer array is specified.
The static method JSON.parse()
parses a JSON string, constructing the JavaScript value or object described by the string. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned.
Method | Pros | Cons |
shallow copy with = |
clear and direct, the default | only shallow copies objects |
JSON.stringify() and JSON.parse()
| deep copies nested objects | doesn't copy functions |
Object.assign() | copies the immediate members of an object—including functions | doesn't deep copy nested objects |
the ... spread operator |
simple syntax, the preferred way to copy an object | doesn't deep copy nested objects |
Lodash cloneDeep() |
clones nested objects including functions | adds an external dependency to your project |
Static Methods of the Object
Object
JavaScript provides several static methods for working with objects:
Object.keys(obj)
— returns an array of the object's keys.Object.values(obj)
— returns an array of the object's values.Object.entries(obj)
— returns an array of[key, value]
pairs.
let person = { firstName: "John", lastName: "Doe", age: 30 }; console.log(Object.keys(person)); // ["firstName", "lastName", "age"] console.log(Object.values(person)); // ["John", "Doe", 30] console.log(Object.entries(person)); // [["firstName", "John"], ["lastName", "Doe"], ["age", 30]]
Examples of Iterating Over Objects with Explanations
Example 1. Iterating Over Properties of an Object and Modifying Them
Problem Description: Imagine we have an object with user data. We want to:
- Iterate over each property of the object (i.e., each key).
- Check if the value of this property is a string.
- If it is a string, convert it to uppercase (e.g.,
admin
->ADMIN
). - Save the changes back to the object.
Code with Detailed Explanations
// Create a user object with various properties const user = { name: "alice", role: "admin", age: 25, active: true }; // Use a for...in loop to iterate over all keys of the user object for (let key in user) { // key will sequentially have the values "name", "role", "age", "active" // Find the value of the current property of the object by its key let value = user[key]; // For example, when key = "name", value = "alice" // Check if value is a string if (typeof value === "string") { // If so, replace the string with the same string in uppercase user[key] = value.toUpperCase(); // For example, "alice" -> "ALICE" } } // Output the result to check the changes console.log(user);
Explanation
1. const user = { ... };
- Create a variable user
and assign it an object with properties:
active
(boolean value).name
(string),role
(string),age
(number),
2. for (let key in user) {
- Declare a for...in
loop, which allows iterating over all existing keys (property names) of the object. The variable key
will take the value of one of the object's keys on each iteration (first name
, then role
, etc.).
3. let value = user[key];
- Create a variable value
and assign it the value of the user
property with the key key
. If at this point key
is "name"
, then value
will be "alice"
.
4. if (typeof value === "string") {
- Check if value
is a string (string). Use the comparison operator ===
to check the data type.
5. user[key] = value.toUpperCase();
- If it is a string, call the toUpperCase()
method, which converts all letters to uppercase. Then update the user[key]
property with the new string. For example, "alice"
becomes "ALICE"
.
6. console.log(user);
- Output the result to the console to see if all the necessary changes have been made. As a result, we get an object where all string values are in uppercase.
Example 2. Iterating Over an Object Using Object.keys()
, Object.values()
, Object.entries()
Problem Description: Now imagine we have a product
object that contains information about a product. We want to:
- Get a list of all the object's keys.
- Get a list of all the values.
- Get a list of [key, value] pairs.
- Display this information in a readable format.
Code with Detailed Explanations:
const product = { title: "Laptop", price: 1200, currency: "USD", inStock: true }; // 1. Get an array of all the keys of the product object const keys = Object.keys(product); // keys -> ["title", "price", "currency", "inStock"] // 2. Get an array of all the values of the product object const values = Object.values(product); // values -> ["Laptop", 1200, "USD", true] // 3. Get an array of [key, value] pairs const entries = Object.entries(product); // entries -> [["title", "Laptop"], ["price", 1200], ["currency", "USD"], ["inStock", true]] // 4. Example of using a for...of loop for the entries array for (let [key, value] of entries) { console.log(`Key: ${key}, Value: ${value}`); }
Explanation of Each Line:
1. const product = { ... };
- Create a variable product
and assign it an object with product information:
inStock
– a boolean value indicating availability in stock.title
– product name,price
– price,currency
– currency,
2.const keys = Object.keys(product);
- Call the built-in method Object.keys()
, which returns an array of all keys of the product
object. Save the result in the variable keys
.
3. const values = Object.values(product);
- Call the Object.values()
method, which returns an array of all values of the product
object.
4. const entries = Object.entries(product);
- Call the Object.entries()
method, which returns an array of pairs [key, value]
.
5. for (let [key, value] of entries) { ... }
- Use a for...of
loop to iterate over each element of the entries
array. Since each element of the entries
array is a small array with two elements [key, value]
, we used destructuring let [key, value]
to immediately extract key
and value
into variables.
6. console.log(`Key: ${key}, Value: ${value}`);
- On each iteration, output a string showing the current key and its value.
As a result, in the console we will see sequentially:
Key: title, Value: Laptop
Key: price, Value: 1200
Key: currency, Value: USD
Key: inStock, Value: true
Note that iterating over objects in JavaScript can be done in several ways:
for...in
(iterating over properties of an object),
Object.keys()
/ Object.values()
/ Object.entries()
(getting an array of keys/values/pairs),
for...of
(iterating over elements of an array, if the object is an array or if you use Object.entries()
).
Always remember the data type you are working with (string, number, boolean, array, or object). Depending on it, there will be different methods and approaches.
When working with nested structures (e.g., an array inside an object, an object inside an array, or even an object inside an object's field), make sure you sequentially access the correct level of nesting (via dot . or brackets []).
Functions in Arrays and Objects in JavaScript: A Detailed Overview
In JavaScript, functions are first-class citizens, which means they can be:
Assigned to a variable:
let greet = function(name) { console.log(`Hello, ${name}!`); };
Passed as an argument to another function:
function callFunction(func) { func(); } callFunction(greet);
Returned as a result of another function:
function createGreeter(greeting) { return function(name) { console.log(`${greeting}, ${name}!`); }; }
Functions in Objects: Methods
When a function is a property of an object, it is called a method. Methods allow objects to perform certain actions.
const person = { firstName: "John", lastName: "Doe", fullName: function() { return this.firstName + " " + this.lastName; } }; console.log(person.fullName()); // Outputs: "John Doe"
this
: Inside a method, this
refers to the object itself. This allows the method to access other properties of the object.
Functions in Arrays: Array Methods
Arrays in JavaScript are objects, so they also have their own methods. Array methods allow you to perform various operations on array elements, such as adding, removing, searching, and sorting.
const numbers = [1, 2, 3, 4, 5]; // Adding an element to the end of the array numbers.push(6); // Removing the last element numbers.pop(); // Sorting the array numbers.sort(); // Checking if an element exists in the array console.log(numbers.includes(3)); // Outputs: true
Functions as Array Elements
Although arrays usually store primitive values or other objects, they can also store functions:
const functions = [ function() { console.log("Function 1"); }, function() { console.log("Function 2"); } ]; functions[0](); // Outputs: "Function 1"
Features of Using Functions in Arrays and Objects
- Execution Context (
this
): It is important to understand howthis
works inside functions, especially in object methods. - Context Binding: To control the value of
this
, you can usebind()
,call()
, andapply()
. - Arrow Functions: Arrow functions have special behavior regarding
this
. - Using
this
in Array Methods: Some array methods, such asmap
,filter
,reduce
, accept functions as arguments. In these functions,this
may refer to different objects depending on the context.
Why Are Arrow Functions Often Used with Array Methods?
Arrow functions are particularly convenient for use in array methods due to lexical this
.
this
: In array methods passed as arguments, this
usually refers to the global object. Arrow functions inherit this
from the surrounding context, allowing the use of this
from the object in which the array method is called. Conciseness: Arrow functions often make the code more readable and compact.
Conciseness: Arrow functions often make the code more readable and compact.
Array methods where functions are used:
map
: Creates a new array by applying a function to each element.
filter
: Creates a new array including only those elements for which the function returned true
.
forEach
: Executes a function for each element of the array.
find
: Returns the first element for which the function returned true
.
Conclusion
Arrays and objects are fundamental data structures in JavaScript that are used in literally every real-world project: from simple web pages to complex interfaces and server applications. Understanding their features, being able to work with methods like push()
, pop()
, map()
, filter()
for arrays and Object.keys()
, Object.values()
, Object.entries()
for objects helps to efficiently store, modify, and display information. It is also important to remember the features of copying (shallow vs. deep copy) and working with references, since in JavaScript arrays and objects are stored by reference.