Destructuring - Exercise 2

The rest pattern uses three dots (...) to collect remaining properties into a new object. This is useful when you want to extract specific properties and group the rest together.

javascript

const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com',
  country: 'USA'
};

// Extract name, collect the rest
const { name, ...otherInfo } = user;

console.log(name); // "Alice"
console.log(otherInfo); // { age: 25, email: "alice@example.com", country: "USA" }

// Using rest with multiple extracted properties
const { name: userName, age, ...contact } = user;
console.log(contact); // { email: "alice@example.com", country: "USA" }

You can destructure directly in the loop variable declaration. This allows you to extract properties from each object or elements from each array as you iterate.

javascript

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 }
];

// Destructure object properties in the loop
for (const { name, age } of users) {
  console.log(`${name} is ${age} years old`);
}

// Works with arrays too
const coordinates = [[10, 20], [30, 40], [50, 60]];

for (const [x, y] of coordinates) {
  console.log(`x: ${x}, y: ${y}`);
}
// Output: x: 10, y: 20 / x: 30, y: 40 / x: 50, y: 60

When a function returns an array or object, you can destructure the result directly. This is a clean way to extract multiple return values from a function.

javascript

// Function returning an array
function getMinMax(numbers) {
  return [Math.min(...numbers), Math.max(...numbers)];
}

const [min, max] = getMinMax([5, 2, 9, 1, 7]);
console.log(min, max); // 1 9

// Function returning an object
function getUser() {
  return { name: 'Alice', age: 25, role: 'admin' };
}

const { name, role } = getUser();
console.log(name, role); // "Alice" "admin"

// Ignoring some returned values
const [smallest] = getMinMax([3, 1, 4]);
console.log(smallest); // 1

Use square brackets to destructure using a dynamic property name stored in a variable. This is called computed property names and allows flexible property access.

javascript

const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com'
};

// Dynamic property name
const propName = 'email';
const { [propName]: userEmail } = user;

console.log(userEmail); // "alice@example.com"

// Using computed names in a function
function extractProperty(obj, key) {
  const { [key]: value } = obj;
  return value;
}

console.log(extractProperty(user, 'name')); // "Alice"
console.log(extractProperty(user, 'age')); // 25

You can destructure function parameters directly in the function signature and provide default values. This creates clean APIs where callers can pass partial options objects.

javascript

// Destructuring with defaults in parameters
function createUser({ name = 'Guest', age = 18, role = 'user' } = {}) {
  return { name, age, role };
}

console.log(createUser({ name: 'Alice' }));
// { name: "Alice", age: 18, role: "user" }

console.log(createUser());
// { name: "Guest", age: 18, role: "user" }

// Array destructuring in parameters
function processCoordinates([x = 0, y = 0] = []) {
  return `Point at (${x}, ${y})`;
}

console.log(processCoordinates([10, 20])); // "Point at (10, 20)"
console.log(processCoordinates([5])); // "Point at (5, 0)"
console.log(processCoordinates()); // "Point at (0, 0)"

When a function receives an array, you can destructure it directly in the parameter list. This extracts array elements into named variables within the function body.

javascript

// Destructure array parameter
function displayPerson([name, age, city]) {
  console.log(`${name}, ${age} years old, lives in ${city}`);
}

displayPerson(['Alice', 25, 'New York']);
// "Alice, 25 years old, lives in New York"

// With rest parameter
function sumFirst([first, second, ...rest]) {
  console.log(`First two: ${first + second}`);
  console.log(`Remaining: ${rest}`);
}

sumFirst([1, 2, 3, 4, 5]);
// "First two: 3"
// "Remaining: 3,4,5"

// Skipping elements
function getThird([, , third]) {
  return third;
}

console.log(getThird(['a', 'b', 'c', 'd'])); // "c"

You can chain destructuring patterns to extract values from deeply nested objects or arrays. Match the nesting structure in your destructuring pattern to reach inner values.

javascript

const company = {
  name: 'TechCorp',
  location: {
    city: 'San Francisco',
    address: {
      street: '123 Main St',
      zip: '94102'
    }
  },
  employees: [
    { name: 'Alice', role: 'Developer' },
    { name: 'Bob', role: 'Designer' }
  ]
};

// Deeply nested object destructuring
const {
  location: {
    city,
    address: { street, zip }
  }
} = company;

console.log(city); // "San Francisco"
console.log(street); // "123 Main St"

// Nested array in object
const { employees: [firstEmployee] } = company;
console.log(firstEmployee.name); // "Alice"

// Combining nested object and array destructuring
const { employees: [{ name: firstName }] } = company;
console.log(firstName); // "Alice"

Use the colon syntax to rename a property during destructuring. The original property name comes first, followed by a colon and the new variable name you want to use.

javascript

const apiResponse = {
  user_name: 'alice_smith',
  user_age: 25,
  is_active: true
};

// Rename snake_case to camelCase
const {
  user_name: userName,
  user_age: userAge,
  is_active: isActive
} = apiResponse;

console.log(userName); // "alice_smith"
console.log(userAge); // 25
console.log(isActive); // true

// Rename with default value
const { user_email: userEmail = 'no-email@example.com' } = apiResponse;
console.log(userEmail); // "no-email@example.com"

// Rename in nested destructuring
const data = { info: { old_value: 100 } };
const { info: { old_value: newValue } } = data;
console.log(newValue); // 100

Destructure in the callback function parameter of map to extract specific properties from each object. This makes transforming arrays of objects much cleaner and more readable.

javascript

const products = [
  { id: 1, name: 'Laptop', price: 999 },
  { id: 2, name: 'Phone', price: 699 },
  { id: 3, name: 'Tablet', price: 499 }
];

// Extract specific properties with map
const productNames = products.map(({ name }) => name);
console.log(productNames); // ["Laptop", "Phone", "Tablet"]

// Transform using multiple destructured properties
const priceList = products.map(({ name, price }) => `${name}: $${price}`);
console.log(priceList); // ["Laptop: $999", "Phone: $699", "Tablet: $499"]

// Create new objects with destructuring
const discounted = products.map(({ id, name, price }) => ({
  id,
  name,
  originalPrice: price,
  salePrice: price * 0.9
}));
console.log(discounted[0].salePrice); // 899.1

Strings are iterable, so you can use array destructuring to extract individual characters. This works because strings behave like arrays of characters in this context.

javascript

const greeting = 'Hello';

// Extract first few characters
const [first, second, third] = greeting;
console.log(first, second, third); // "H" "e" "l"

// Get first character and rest
const [initial, ...remaining] = greeting;
console.log(initial); // "H"
console.log(remaining); // ["e", "l", "l", "o"]
console.log(remaining.join('')); // "ello"

// Skip characters
const [, , thirdChar] = greeting;
console.log(thirdChar); // "l"

// Spread string into array
const letters = [...greeting];
console.log(letters); // ["H", "e", "l", "l", "o"]

// Swap characters using destructuring
let a = 'X';
let b = 'Y';
[a, b] = [b, a];
console.log(a, b); // "Y" "X"

Default values only apply when the destructured value is undefined. This is important to understand because null and other falsy values will not trigger defaults.

javascript

const settings = {
  theme: undefined,
  fontSize: null,
  showSidebar: false,
  volume: 0
};

// Default only applies to undefined
const {
  theme = 'light',
  fontSize = 16,
  showSidebar = true,
  volume = 50
} = settings;

console.log(theme); // "light" (undefined triggers default)
console.log(fontSize); // null (null does NOT trigger default)
console.log(showSidebar); // false (false does NOT trigger default)
console.log(volume); // 0 (zero does NOT trigger default)

// Handle missing properties with defaults
const { missingProp = 'default value' } = settings;
console.log(missingProp); // "default value"

// Nullish coalescing for null handling
const size = settings.fontSize ?? 16;
console.log(size); // 16

Real world data often contains arrays within objects or objects within arrays. You can mix both destructuring patterns to navigate these complex structures in a single statement.

javascript

// Object containing array
const response = {
  status: 'success',
  data: [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]
};

const { status, data: [firstUser, secondUser] } = response;
console.log(status); // "success"
console.log(firstUser.name); // "Alice"

// Array containing objects
const users = [
  { profile: { name: 'Alice', age: 25 } },
  { profile: { name: 'Bob', age: 30 } }
];

const [{ profile: { name: firstName } }] = users;
console.log(firstName); // "Alice"

// Complex nested structure
const apiData = {
  results: [
    { user: { details: { email: 'alice@test.com' } } }
  ]
};

const { results: [{ user: { details: { email } } }] } = apiData;
console.log(email); // "alice@test.com"