A Deep Dive into Data Structures in JavaScript

Data structures are the basis of JavaScript, without them we couldn't really manipulate everything, and despite variables being the most basic feature, data structures use these variables to actually achieve anything so, I'm going to talk everything about Data Structures in the next few days.

image.png

As always, we being our code with:

"use scrict";

Destructuring arrays

This is an ES6 features, and it is a way of unpacking values from an array or an object into separate values. We break a comples data structure into a smaller data structure like a variable. We retrieve elements from an array to store them into variables.

const restaurant = {
  name: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Lasagna", "Pesto Pasta", "Four Cheese Spaghetti"],
  mainMenu: ["Pizza", "Pasta", "Risotto"],
};

const array1 = [2, 3, 4];
const a = array1[0];
const b = array1[1];
const c = array1[2];

console.log(a, b, c); // Output: 2, 3, 4

But we can also declare them at the same time:

const [x, y, z] = array1; // 1
// 2
console.log(x, y, z); // Output: 2, 3, 4

1. x becomes the first element of the array, y the second and z the third.
2.Whenever JS sees [] on the left side of the equal sign, it knows that it should do destructuring.

We don't need to take all of the elements from the array, we can do only some:

const [first, second] = restaurant.categories; // 3
console.log(first, second); // Output: Italian Pizzeria

3.We use first and second as keywords to only take the first two, but we can actually use any word we want.

This means that we can actually take the first and the third by leaving a blank space, using any kind of word.

const [one, , two] = restaurant.categories;
console.log(one, two); // Output: Italian Vegetarian

Let's say we want to switch the categories in the third property of the array:

let [primary, , secondary] = restaurant.categories;
[primary, , secondary] = [secondary, , primary]; // 4
console.log(primary, secondary); // Output: Vegetarian Italian

4.We don't use const or let because we are not storing them, we are just switching them.

We can make a function return an array and then immediately destructure the result into different variables. This allows us to return multiple values from a function.

const restaurant = {
  name: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Risotto"],

  order: function (starterIndex, mainIndex) {
    // 5
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },
};
console.log(restaurant.order(1, 0)); // Output: ["Garlic Bread", "Pizza"]

5.This function will accept two parameters, one for the starter menu and the other for the main menu.

But we haven't destructured that, we just called the elements, to destructure them we:

This is how we receive 2 values from a function
const [starterCourse, mainCourse] = restaurant.order(1, 0);
console.log([starterCourse, mainCourse]); // Output: [ 'Garlic Bread', 'Pizza' ]
console.log(starterCourse); // Output: Garlic Bread

Now, let's talk nested arrays and destructuring them

Take the next array as an example:

const nestedArray = [2, 3, [4, 5]];
const [first, , second] = nestedArray;
console.log(first, second); // Output: 2 [4, 5]

And if we want to take each element and store it into a variable, including the two elements inside the nested array, we would need to do as follows:

const nestedArray = [2, 3, [4, 5]];
const [first, second, [third, fourth]] = nestedArray;
console.log(first, second, third, fourth); // Output: 2 3 4 5

Another feature of destructuring is setting default values for the values when we are extracting them. This is useful in case we don't know the length of the array (which happens very often irl)

const [d = "no element", e = "no element", f = "no element"] = [8, 9]; // 5
console.log(d, e, f); //Output: 8 9 no element

5.Let's pretend we don't know the length of the array

When we call 3 elements from an array with only two elements, we'll receive the existing position values, and those that don't exist, we'll get the default value.

Destructuring objects

Let's take the example from the italian restaurant because I don't feel like coming up with another example:

const restaurant = {
  name: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },
};

To destructure objects we use curly braces {}, all we have to do is provide the variable names that match exactly the property names that we want to retrieve from the object.

Remember, unlike in arrays, in objects the order of the elements doesn't matter so we don't need to skip elements.

const { name, openingHours, categories } = restaurant;
// This creates three new variables based on the restaurant object.
console.log(name, openingHours, categories);

Output:
Ristorante di Paolo {
thu: { open: 12, close: 22 },
fri: { open: 11, close: 23 },
sat: { open: 0, close: 24 }
} [ 'Italian', 'Pizzeria', 'Vegetarian' ]

But what if we want the variable names to be diffferent from the property names? We still need to reference the properties, check it out:

const {
  name: restaurantName,
  openingHours: Hours,
  categories: tags,
} = restaurant;
console.log(restaurantName, Hours, tags);

This is very useful when we are dealing with a third party data.

We can also set default values for properties that do not exist in the object.

const { menu = [], starterMenu: starters = [] } = restaurant; // 6.
console.log(menu, starters); // Output: [] [ 'Salad', 'Garlic Bread', 'Cheese' ]

6.We set the default value to an empty array in case the property doesn't exist

To mutate variables while destructuring objects:

let a = 111;
let b = 999;
const obj = { a: 23, b: 7, c: 14 };

({ a, b } = obj); // 7.
console.log(a, b, { a, b }); // Output: 23 7 { a: 23, b: 7} // 8.

7.We are rewriting the values of a and b with the values from the object.

  1. And these are the values of the first variables declared, not the ones in the array

Now, if we really want to destructure an object, we have to take it to the next level.

const restaurant = {
  restName: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },
};

To deal with nested objects we need to do the following:

const { restName, openingHours, categories } = restaurant; // 9

9.We created these 3 variables above, just copying them to use them.

We want two variables, one called open and one called close.

const {
  fri: { open, close },
} = openingHours; // 10
console.log(open, close); // Output: 11 23 (as variables)

10.We know that [fri] is an object, so we can destructure it using the

Many times in JS we have several functions with several parameters, and it can be hard to know the order of parameters so instead of defining the parameters manually, we can pass an object into the function as an argument and the function will immediately destructure that object.

const restaurant = {
  restName: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderDelivery: function ({ starterIndex, mainIndex, time, address }) {
    console.log(
      `Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`
    );
  },
};

const { restName, openingHours, categories } = restaurant;
const {
  fri: { open, close },
} = openingHours;
console.log(open, close);

restaurant.orderDelivery({
  time: "22:30",
  address: "Elm street 8",
  mainIndex: 2,
  starterIndex: 1,
});

We passed an object, not four variables, this is extremely important. The function received the object with the exact same name of the parameters as the arguments and thus, was able to destructure it and pass in the values. And notice that the order of the properties doesn't need to match.

We can also pass default values to said function in case the client doesn't specify the values.

 orderDelivery: function ({ starterIndex = 1, mainIndex =1, time = 20:00, address = "Pick up at store" }) 

Spread operator

We can use it to expand into all its elemeents. Like unpacking all the elements at once.

const arr = [7, 8, 9]; // 11
const badNewArr = [1, 2, arr[0], arr[1], arr[2]]; // 12

11.We want to create a new array with new elements.
12.But this manual method is just unefficient.

Using the spread operator:

const newArr = [1, 2, ...arr];
console.log(newArr); // Output: [ 1, 2, 7, 8, 9 ] But in a cleaner way

The spread operator takes all the values of an array and writes them individually as if we did it just like in the first way.

We can use the spread operator whenever we write multiple an array literal or when we pass arguments into functions.

Using the old restaurant object we can go deeper into the spread operator functionalities:

const restaurant = {
  restName: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],
};
const newMainMenu = [...restaurant.mainMenu, "Gnocci"]; // `13
console.log(newMainMenu); // Ouput: [ 'Pizza', 'Pasta', 'Lasagna', 'Gnocci' ]


13.We are creating a new array, not manipulating the original array.

The difference between the spread operator and destructuring arrays is that the spread operator takes all the elements from the array, and it doesn't create new variables, so we can only use it in situations when we would write values separated by commas.

Use Cases:

Creating shallow copies of arrays

const mainMenuCopy = [restaurant.mainMenu];

Merge two arrays together

const fullMenu = [...restaurant.mainMenu, ...restaurant.starterMenu];
console.log(fullMenu); // Output: [ 'Pizza', 'Pasta', 'Lasagna', 'Salad', 'Garlic Bread', 'Cheese' ]

Note to self: Iterables are arrays, string, maps and sets, but not objects.

const str = "Eric";
const letters = [...str, " ", "S."];
console.log(letters); // 14

14.We unpacked the string and built an array from each letter: [ 'E', 'r', 'i', 'c', ' ', 'S.' ]

We can also build functions and use the spread operator to modify them, let's use the restaurant example for that:

const restaurant = {
  restName: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],

  openingHours: {
    thu: {
      open: 12,
      close: 22,
    },
    fri: {
      open: 11,
      close: 23,
    },
    sat: {
      open: 0, // Open 24 hours
      close: 24,
    },
  },

  order: function (starterIndex, mainIndex) {
    return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
  },

  orderDelivery: function ({ starterIndex, mainIndex, time, address }) {
    console.log(
      `Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`
    );
  },
  orderPasta: function (ing1, ing2, ing3) {
    console.log(`Here's your pasta with ${ing2}, ${ing2} and ${ing3}`);
  },
};

const ingredients = [
  prompt("Let's make pasta! Ingredient 1?"),
  prompt("Let's make pasta! Ingredient 2?"),
  prompt("Let's make pasta! Ingredient 3?"),
];
console.log(ingredients);

restaurant.orderPasta(...ingredients);

The spread operator also works with objects even though they are not iterable, and it works as follows:

const restaurant = {
  restName: "Ristorante di Paolo",
  location: "Chipilo, Mexico",
  categories: ["Italian", "Pizzeria", "Vegetarian"],
  starterMenu: ["Salad", "Garlic Bread", "Cheese"],
  mainMenu: ["Pizza", "Pasta", "Lasagna"],
};

const newRestaurant = {
  ...restaurant,
  founder: "Paolo DiPaolini",
  employees: 2,
  employeesNames: ["Luca", "Marlena"],
}; // We added a lot of properties to the object
console.log(newRestaurant); // Output:

const restaurantCopy = { ...restaurant }; // 15

15.we are creating a copy so we can modify this one without changing the original.




There's still a lot to learn about data structures, I'm barely scratching the surface but, I'm just getting warm but this post was already too long. Hopefully you found it useful, I hope I can post again about this topic tonight.

H2
H3
H4
3 columns
2 columns
1 column
4 Comments
Ecency