Course outline

JavaScript / Basic: values and browser foundations / Lesson 5

Objects, arrays, and deliberate updates

Represent course records and transform collections without accidental shared mutation. Learn property access, missing values, array callbacks, and the limits of copying.

About 4 min readContent reviewed
On this page

Lesson goals

  • Choose records and collections for different purposes.
  • Transform arrays with explicit callback results.
  • Explain identity and shallow copying.

Properties model named facts

An object record groups named facts such as id, title, and seats. Dot access uses a literal property name; bracket access evaluates a key expression, useful when a field is selected dynamically. Distinguish a missing property from an own property whose value is undefined when the contract cares, using Object.hasOwn. Do not infer presence from truthiness because a stored zero can be valid. For dictionaries populated with arbitrary keys, Map can express key/value intent without accidental prototype inheritance. Keep the public shape small and validate incoming records before treating their fields as trustworthy domain values.

Filter, map, and reduce with a visible purpose

Array methods describe different transformations. filter selects elements according to a Boolean decision, map returns a corresponding output for each visited element, and reduce combines a sequence into an accumulator. Use a named intermediate result when a long chain hides the business rule. The example selects available courses before extracting titles, so unavailable courses never reach the title result. These methods do not automatically clone the element objects. A new filtered array can still contain references to the original records, which matters if a later step mutates an element instead of producing a new value.

javascript Selection and aggregation are separate operations
const courses = [
  { title: "DOM", seats: 2 },
  { title: "Async", seats: 0 }
];
const titles = courses.filter(c => c.seats > 0).map(c => c.title);
const seats = courses.reduce((sum, c) => sum + c.seats, 0);
console.log(JSON.stringify(titles), seats);
Expected output
["DOM"] 2

The title list contains only DOM. The explicit initial zero also makes the empty-array total well-defined.

Sorting can mutate and compare unexpectedly

The default sort comparison treats values as strings, so numeric amounts need a numeric comparator. sort mutates the array it receives; copy first when the original order is meaningful, or use a supported copying alternative such as toSorted. A comparator should consistently describe ordering and return a negative number, zero, or a positive number. Avoid callbacks that merely return a Boolean because equal and ordered cases become ambiguous. For human language, consider locale-aware comparison. Tests should check both the expected sorted values and whether preserving the input was part of the contract.

Copy the path you intend to change

Spread syntax creates a new array or object at one level. Nested records remain shared unless they too are copied. For a settings update, copy the outer record and the nested preference record you change, while reusing immutable unrelated fields deliberately. JSON serialization is not a universal clone: it changes or rejects several values and loses behavior and identity relationships. structuredClone supports many data structures but is also a host facility with constraints. Select a copy strategy from the actual data contract rather than attaching “deep copy” to a convenient one-liner.

javascript Independent nested update and numeric ordering
const original = { name: "A", preferences: { theme: "light" } };
const draft = { ...original, preferences: { ...original.preferences } };
draft.preferences.theme = "dark";
const amounts = [100, 20, 3];
const ordered = [...amounts].sort((a, b) => a - b);
console.log(original.preferences.theme, draft.preferences.theme);
console.log(ordered.join(","), amounts.join(","));
Expected output
light dark
3,20,100 100,20,3

Only the changed nested path is copied. Copying the amounts array prevents sort from changing the original order.

Empty collections and absent records differ

find returns the first matching element or undefined, while filter always returns an array of matches. Reading find(...).title without checking the result can therefore throw. An empty array is truthy; check length when asking whether matches exist. Avoid sparse arrays as a substitute for explicit missing records because callbacks and iteration can treat holes differently. A good collection boundary normalizes a known valid list and makes absence a deliberate result. Keep item identifiers stable so filtering or sorting does not change which record a user action refers to.

Common errors and fixes

Numeric values sort as text

Why it happens
sort is called without an appropriate comparator.
How to fix it
Use a numeric comparator and choose mutation or copying deliberately.
Prevent it next time
Test values with different digit lengths.

A draft changes original preferences

Why it happens
Only the outer object was copied.
How to fix it
Copy the nested path being changed.
Prevent it next time
Check identity and values at each relevant depth.

A missing search result causes TypeError

Why it happens
find is assumed to always return a record.
How to fix it
Handle undefined before reading fields.
Prevent it next time
Test no-match and empty-input cases.

Check your understanding

1. Find total cents for [125, 250, 0] without changing the array.

Show worked solution

Use reduce((sum, cents) => sum + cents, 0), producing 375. The initial zero handles an empty array as zero too. Validate incoming amounts before aggregation; a string could turn addition into concatenation and an invalid negative may violate the domain.

2. Why does {...user} not isolate user.address.city?

Show worked solution

The spread copies the address reference, not that nested object. Create {...user, address: {...user.address, city: "New"}} for this known record shape. The result has a new outer record and address while unchanged nested fields still follow the documented sharing policy.

Key takeaways

  • Transformation methods have distinct return and mutation contracts.
  • Copying one level does not isolate every nested value.

Official references