Aabha · Learn by building

05. Model events with arrays and objects

Keep related details together

Separate arrays of titles, locations and capacities can drift out of alignment. An object groups one event's named properties. An array then holds several event objects. This mirrors the page: one record per card, several cards in a list.

const garden = {
  id: "garden-morning",
  title: "Garden morning",
  category: "outdoors",
  places: 7
};
console.log(garden.title);
const events = [garden, {
  id: "repair-cafe", title: "Repair café", category: "learning", places: 0
}];
console.log(events[0].id);

Curly braces create the object; property names and values are separated by a colon. Commas separate properties. Dot notation reads a named property. Square brackets on the array access a position: the first index is 0, the second is 1. events.length is 2, so the last valid index is length minus 1. Reading an absent position or property gives undefined; trying to read a property from undefined causes an error. Check assumptions rather than expecting every lookup to succeed.

An ID stays stable while a title may change. Our later saved list will store IDs, not displayed titles. This makes it possible to rename “Garden morning” without losing the visitor's choice. Use IDs that uniquely identify records. A duplicate ID is a data error, not another spelling of the same title.

Transform a collection without losing the original

We want only outdoor events. filter receives a callback and calls it for each element. If the callback returns true, that element goes into a new result array. The original array's membership is unchanged.

const outdoorEvents = events.filter(function (event) {
  return event.category === "outdoors";
});
const titles = events.map(function (event) {
  return event.title;
});
const repair = events.find(function (event) {
  return event.id === "repair-cafe";
});

For filter, the garden record returns true and stays; the repair record returns false and is left out. For map, both records stay represented, but each is transformed into its title string. For find, scanning stops at the first matching record; no match gives undefined. Choose the operation based on the output you need: some records, one output per record, or one record.

The same callback can be written as (event) => event.category === "outdoors". Read it in words before shortening it: “for this event, return whether its category is outdoors.” A forgotten return in a braces-based callback makes every filter result falsy and produces an empty array. This is a function mistake with a collection symptom.

Understand shared references

Assigning an object to another variable does not copy all its contents. Both names can refer to the same object. A filtered array also contains references to the original objects. This surprises learners who correctly remember that filter returns a new array:

const first = events[0];
first.places = 6;
console.log(events[0].places); // 6

Changing a property changes that shared object. const prevents assigning a different object to first, but does not prevent this property change. When you need a changed record without changing the old record, object spread can make a shallow copy: const updated = { ...first, places: 5 };. Shallow means nested objects would still be shared. Our records use simple primitive properties, so this is enough for our examples. The core capstone treats event fixture records as read-only and keeps saved IDs separately.

Search combines rules

A search query can be trimmed and lowercased once. Compare it with lowercased titles using includes. An empty string is included in every string, which conveniently means an empty query matches all titles. Combine that rule with the category rule using &&.

const query = " garden ".trim().toLowerCase();
const results = events.filter((event) => {
  return event.title.toLowerCase().includes(query)
    && event.category === "outdoors";
});

Our search is intentionally modest: title matching, case-insensitive, with no ranking, accent folding or fuzzy spelling correction. Describe that scope rather than calling it a complete search engine. If the UI says “search titles,” its behavior will match the promise.

Inspect data before blaming the interface

Run version 05. Inspect the whole events collection, then the filtered IDs. If a page later shows the wrong number of cards, first inspect the array returned by the filter. Correct data with wrong cards suggests a rendering problem. Wrong data before rendering suggests a filter or input problem. Separating these questions makes debugging much less mysterious.

References: indexed collections, objects, filter.

Practice and next step

Open exercise 05. Attempt the prediction before running code. Record the observed result and explain any mismatch. The explained solution is separate so you can choose when to look.