Get In Touch
admin@victoriaweb.me
Back

Arrays and Objects in JavaScript

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

  1. push() — adds one or more elements to the end of the array. Returns the new length of the array.
  2. pop() — removes the last element from the array and returns it.
  3. shift() — removes the first element from the array and returns it.
  4. unshift(…elements) — adds one or more elements to the beginning of the array.
  5. splice(start, deleteCount ,…items, …items,…items) — adds, removes, or replaces elements in the array at the specified index. Returns an array of removed elements.
  6. reverse() — reverses the order of elements in the array. Returns a reference to the modified array.
  7. sort(compareFunction) — sorts the elements of the array (as strings by default). Returns a reference to the sorted array.
  8. fill(value, start, end) — fills all elements of the array from the start index to the end index with a single value.
  9. copyWithin(target, start, end) – copies a sequence of array elements within the array from positions [start, end) to the target position. Returns a reference to the modified array.

Methods that do not mutate the array

  1. concat() — returns a new array that is the result of merging two or more arrays.
  2. slice() — returns a new array that contains a copy of part of the original array.
  3. map() — returns a new array that is the result of applying a function to each element of the original array.
  4. filter() — returns a new array that contains elements that satisfy a condition.
  5. reduce() — returns a single value that is the result of applying a function to each element of the array.
  6. reduceRight() — similar to reduce(), but processes the array from right to left.
  7. flat() — returns a new array with all subarrays “flattened” to the specified depth.
  8. flatMap() — combines map() and flat(), returns a new array.
  9. join() — returns a string that is the result of joining all elements of the array.
  10. toString() — returns a string representation of the array.
  11. indexOf() — returns the index of the first found element or -1 if the element is not found.
  12. lastIndexOf() — returns the index of the last found element or -1 if the element is not found.
  13. includes() — returns true or false, depending on whether the element is in the array.
  14. find() — returns the first element that satisfies the condition, or undefined.
  15. findIndex() — returns the index of the first element that satisfies the condition, or -1.
  16. every() — returns true if all elements satisfy the condition.
  17. some() — returns true 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:

  1. Uniqueness: All values in a Set are unique.
  2. Iterability: Set can be iterated using loops or methods like forEach.
  3. No Indexes: Unlike arrays, Set does not have indexes, so elements cannot be accessed by index.
  4. 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 ([])?

  1. Most cases: array literal is the standard and most common way to create arrays. It is shorter, clearer, and less error-prone.
  2. When you need to create an array with known elements: if you know the array elements in advance, array literal is the best choice.
  3. When you need to create an empty array: array literal is also suitable for creating an empty array.
  4. 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:

  1. 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).
  2. When you need to dynamically create an array: if the array length or its elements are determined dynamically, the Array() constructor can be useful.
  3. 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 the Array() constructor can also be used.
  4. 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:

  1. View or output array values (e.g., console.log()).
  2. Modify each element as needed (e.g., multiply all numbers by 2).
  3. Collect the necessary data into a new structure (e.g., create another array or calculate a total value).
  4. Filter the array, selecting only those elements that meet a certain condition.
  5. 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:

  1. Initialize an empty array positiveDoubled.
  2. Using the counter i, iterate over all indices of arr.
  3. Check if the element (arr[i]) is positive (> 0).
  4. If so, multiply it by 2 and add (push) it to the new array.
  5. 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:

  1. for...of iterates directly over the values of the array elements, so item is already the element (without the index).
  2. For each item, square it item * item.
  3. Add the result to the squares array.
  4. As a result, we get the array [4, 1, 25, 0, 49].

while loop

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:

  1. Array arr = [1, 2, 3, 4] contains four elements. We will iterate over this array.
  2. We create a variable i and initialize it with the value 0, which corresponds to the index of the first element of the array.
  3. The while loop checks the condition: is the value of the variable i less than the length of the array (arr.length).
  4. Since the array has 4 elements, the condition will be true as long as i is not equal to 4.
  5. Inside the loop, we output the array element pointed to by index i using console.log(arr[i]).
  6. Each time the loop executes, the index i is incremented by 1 (i++) to move to the next element of the array.
  7. 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:

  1. Array arr = [1, 2, 3, 4] again contains four elements. We create a variable i and initialize it with the value 0. The do...while loop guarantees that the loop body will execute at least once, even if the condition was initially false. The loop body executes:
  2. Output the array element pointed to by index i.
  3. Increment i to move to the next element of the array.
  4. The condition is checked after the loop body executes. If i is still less than the array length, the loop continues. Once i becomes equal to 4 (the length of the array), the condition becomes false, and the loop ends.

forEach() loop

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:

  1. forEach() takes a function as an argument. This function is called for each element of the array.
  2. In the function, we pass the variable num, which at each step of the loop will be equal to the current array element.
  3. 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:

  1. map() applies the function to each element of the array and creates a new array with the results of these operations.
  2. We multiply each array element by 2 and get a new array with doubled values.
  3. 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:

  1. filter() checks each array element against the condition we specify in the function.
  2. In our case, we check if the number is even using the condition num % 2 === 0.
  3. 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:

  1. reduce() takes a function that works with two arguments:
  2. acc (accumulator): This is the value that accumulates during the array iteration.
  3. num (current element): This is the current element of the array.
  4. The first parameter of the reduce() function defines the initial value of the accumulator. In our case, it is 0.
  5. 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 equals 1,
  • second equals 2,
  • third equals 3.

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:

  1. We use the spread operator to copy all elements of the array arr1 into a new array arr2.
  2. Since this is a shallow copy, changes in arr2 do not affect arr1.

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:

  1. First, we add the element 1 at the beginning of the array arr using the spread operator.
  2. Then we add the element 5 at the end of the array arr 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:

  1. We pass the array arr to the function sum() using the spread operator, which unpacks the array elements as separate arguments.
  2. 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:

  1. The ...args operator gathers all passed arguments into the args array.
  2. 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:

  1. First, using the slice(0, 2) method, we copy the first two elements.
  2. Then we insert the value 100 at the third position.
  3. 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

  1. Code reduction: No need to manually access array elements by indices.
  2. Convenience: Easy to get values from nested structures.
  3. Readability: Code becomes more understandable and concise.

Limitations of Destructuring

  1. If the array is shorter than the number of variables, “extra” variables will get the value undefined.
  2. If you try to destructure something that is not iterable (e.g., null or undefined), 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 and b) receive the values 1 and 2.
  • 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 the name property, and the rest of the properties were gathered into the rest 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 idusername, 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 of key1 and key2 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 the fullName method refers to the person object. Therefore, when calling person.fullName(), this will contain all the properties of the person 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:

  1. Iterate over each property of the object (i.e., each key).
  2. Check if the value of this property is a string.
  3. If it is a string, convert it to uppercase (e.g., admin -> ADMIN).
  4. 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:

  1. Get a list of all the object's keys.
  2. Get a list of all the values.
  3. Get a list of [key, value] pairs.
  4. 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 how this works inside functions, especially in object methods.
  • Context Binding: To control the value of this, you can use bind(), call(), and apply().
  • Arrow Functions: Arrow functions have special behavior regarding this.
  • Using this in Array Methods: Some array methods, such as map, 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.

Victoriia Hladka
Victoriia Hladka
https://victoriaweb.me