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.
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.
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.
// 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.
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.
// 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.
// 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.
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.
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.
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.
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.
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.
// 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"