Codeskill

Learn to code, step by step

Arrays and objects properly

Using arrays and objects properly in real JavaScript – not just looping by hand, but the standard methods, destructuring, spread, and immutability habits that keep state predictable.

Array methods you will use weekly

You met forEach, map, and friends in the intro series. Here is how they fit together in practice.

map transforms each element and returns a new array. Use it when the output is the same length as the input.

const products = [
  { name: 'Pen', price: 1.2 },
  { name: 'Pad', price: 3.5 },
];

const labels = products.map((p) => `${p.name} - £${p.price.toFixed(2)}`);

filter keeps elements that pass a test. find returns the first match. some and every answer boolean questions about the collection.

const inStock = products.filter((p) => p.stock > 0);
const cheap = products.find((p) => p.price < 2);
const allUnderTen = products.every((p) => p.price < 10);

reduce folds a list into a single value – sum, group, index by id. It looks odd at first; it pays off when you stop rewriting the same loop.

const total = products.reduce((sum, p) => sum + p.price, 0);

const byCategory = products.reduce((acc, p) => {
  const key = p.category ?? 'Other';
  acc[key] = acc[key] ?? [];
  acc[key].push(p);
  return acc;
}, {});

Immutability habits

Many bugs come from mutating shared arrays or objects when something else still expects the old shape. Prefer creating new values when state changes.

// Avoid: mutating in place when others hold the reference
cart.items.push(newItem);

// Prefer: new array when updating React-like state or shared data
const nextItems = [...cart.items, newItem];
setCart({ ...cart, items: nextItems });

Spread (...) copies shallowly. Nested objects still share inner references – for deep trees, you may need structuredClone or a targeted copy at the level that changed.

const user = { name: 'Alex', prefs: { theme: 'light' } };
const updated = {
  ...user,
  prefs: { ...user.prefs, theme: 'dark' },
};

Destructuring

Destructuring pulls fields out cleanly – function parameters, API responses, swap variables without a temp.

function showOrder({ id, customer, lines }) {
  console.log(`Order ${id} for ${customer.name}`);
}

const [first, second, ...rest] = ids;
const { street, city, ...addressRest } = address;

Default values in destructuring save repetitive fallback code:

function createWidget({ title = 'Untitled', visible = true } = {}) {
  return { title, visible };
}

Object helpers

Object.keys, Object.values, and Object.entries turn objects into iterable data. Handy for config maps and building form state.

const settings = { autosave: true, fontSize: 16 };

for (const [key, value] of Object.entries(settings)) {
  console.log(key, value);
}

structuredClone deep-clones most plain data (dates, maps, sets included). Simpler than JSON.parse(JSON.stringify(…)) and handles more types.

Optional chaining and nullish coalescing

const label = user?.profile?.displayName ?? 'Guest';

?. stops early if something is null or undefined. ?? falls back only for null/undefined, not for 0 or empty string – which is usually what you want for defaults.

Next up: closures, this, and binding – the topics that confuse everyone once, then click.

PreviousModern module workflow (ESM)