Collections
Use array positions and iteration deliberately.
CodeBhavyaModel collections, preserve source data and explain every transformation.
Level 14 calculated one workshopโs remaining seats. Real pages often manage a collection of workshops. This lesson groups values into arrays and objects, derives useful fields, selects matching records, orders the results and calculates a summary.
You will explain which methods return new arrays, which operations mutate data, and why a new outer array does not necessarily contain newly copied objects. You will also test empty collections, full workshops and equal sort values instead of assuming every collection has a matching first item.
Use array positions and iteration deliberately.
Give related properties meaningful names.
Distinguish map, filter, find and reduce.
Check source preservation, no-match results and tie behavior.
Our task is a workshop catalog. Each record needs a stable ID, title, capacity and reserved count. The collection has an order, and a learner may search it or choose only workshops with available places. A recordโs identity should not depend on its current display position.
Use an array when a sequence or collection is useful. Use an object to group named properties of one record. An array of objects combines these roles without forcing capacity, title and reserved values into several separate arrays that must stay synchronized.
State the data contract before calculating: capacity and reserved are small non-negative integers, reserved does not exceed capacity, and each workshop has a unique ID. This lessonโs lab supplies trusted, predefined records; a real external data source would need its own validation.
The ordered collection of workshops.
One workshop record with named fields.
A value such as title, capacity or reserved.
An array is an object designed for indexed collections. Ordinary array indexes start at zero. An array with three consecutive items has indexes 0, 1 and 2, while its length is 3. The final index in that dense array is length - 1.
Reading a missing ordinary index produces undefined. It does not magically create a valid workshop. Checking the collection length or finding a record explicitly is safer than assuming records[0] exists.
Length and the number of explicitly present elements can differ for sparse arrays. Our examples use dense arrays to avoid hiding a beginner logic error behind empty slots. Avoid assigning far beyond the current end just to represent a workshop ID.
const topics = ["HTML", "CSS", "JavaScript"];
console.log(topics[0]); // HTML
console.log(topics.length); // 3
console.log(topics[3]); // undefined
console.log(Array.isArray(topics)); // trueTypeof topics is "object". Array.isArray is the useful test when the task requires an actual array. A numeric ID such as 103 is a property of a record, not a requirement to store it at array index 103.
Push appends items and returns the new length. Pop removes and returns the last item, or undefined for an empty array. Unshift and shift operate at the beginning. These methods change the array they are called on.
Splice can remove, replace or insert items in place; it returns an array of removed items. Slice returns a shallow copy of a selected range and does not remove those elements from the original. Similar names do not imply identical behavior.
A const binding can still refer to a mutable array. Const prevents rebinding the name to a different array; it does not disable push, splice or other allowed changes to the existing array.
const topics = ["HTML", "CSS"];
const length = topics.push("JavaScript"); // 3
const preview = topics.slice(0, 2); // ["HTML", "CSS"]
const removed = topics.splice(1, 1); // ["CSS"]
// topics is now ["HTML", "JavaScript"]Write down both the return value and the array after the method call. Do not write const topics = topics.push(...) and expect push to return the collection.
A traditional for loop is useful when the index is part of the task. For...of visits values from an iterable such as an array. For...in enumerates property keys and can include inherited enumerable properties; it is not the default choice for reading array values.
ForEach calls a callback for present array elements and returns undefined. It is suitable for a deliberate side effect, such as logging. It does not build the transformed output array that map builds, and a return inside its callback does not break the surrounding iteration.
For these synchronous beginner examples, a for...of loop can use break when the task should stop. Do not assume an asynchronous callback supplied to forEach will be awaited in sequence.
const topics = ["HTML", "CSS", "JavaScript"];
for (let index = 0; index < topics.length; index += 1) {
console.log(index, topics[index]);
}
for (const topic of topics) {
console.log(topic);
}The first loop exposes both index and value. The second directly names the value. Choose the clearer version for the job instead of using an index when it contributes nothing.
An object literal groups related fields. Dot notation reads a property with a suitable literal name. Bracket notation can use a variable key or a property name that does not fit ordinary dot notation. Missing properties generally read as undefined unless inheritance supplies them.
Property names and values have separate roles. In workshop.title, title names the field; "HTML" is the stored value. Keep numeric fields as numbers after validation rather than storing display strings such as "30 seats" and expecting arithmetic to work.
Use a stable ID when matching or updating records. A title can change and two titles can be the same. The array index can change when the collection is filtered or sorted.
const workshop = {
id: "w1", title: "HTML", capacity: 30, reserved: 12
};
console.log(workshop.title); // HTML
const key = "capacity";
console.log(workshop[key]); // 30
console.log(workshop.missing); // undefinedAssigning workshop.reserved = 15 changes that property on the referenced object. Object.keys and Object.entries can help inspect an objectโs own enumerable string-keyed fields. They do not promise to expose every inherited or non-enumerable property.
An object can contain a function-valued property, often used as a method. A regular method can access this when called with the expected receiver. This depends on how a function is invoked; it is not permanently the name of the object where the function was first written.
Arrow functions do not create their own this binding. For a beginner catalog calculation, a pure helper that takes a record argument is usually easier to test than relying on an invocation-dependent receiver.
function remainingSeats(workshop) {
return workshop.capacity - workshop.reserved;
}
const workshop = { id: "w1", title: "HTML", capacity: 30, reserved: 12 };
console.log(remainingSeats(workshop)); // 18
console.log(Object.keys(workshop));
// ["id", "title", "capacity", "reserved"]The helper assumes the record already meets the data contract. Inspecting keys or subtracting two fields is not a substitute for validating an external record.
Assigning another binding to an array or object does not automatically clone it. The two bindings can refer to the same object. An update through one is then observable through the other. Strict equality between objects checks identity, not a deep comparison of their fields.
Spread can copy the outer array or an objectโs own enumerable properties. The copy is shallow: nested objects remain shared unless you deliberately copy those nested levels too. Array methods that return new arrays can still retain references to the original item objects.
Our catalog uses flat records. The lab derives a new object for each record before doing later work and sorts a copied array. Do not generalize that approach into a promise that arbitrary nested data has been deeply cloned.
const original = [{ title: "HTML", meta: { room: "A" } }];
const outerCopy = [...original];
outerCopy[0].title = "Updated"; // changes original[0].title too
const recordCopy = { ...original[0] };
recordCopy.title = "Local"; // independent top-level field
recordCopy.meta.room = "B"; // nested meta remains sharedA new outer container and a new nested object are different claims. Explain exactly which levels are copied.
Destructuring gives local names to selected array positions or object properties. Object patterns match property names, not their order in the literal. A default initializer applies when the extracted value is undefined; it does not replace null or zero.
Spread collects existing values or properties into a new outer container. In an object literal, later properties can replace an earlier property with the same name. A remaining field written after ...workshop becomes the derived value we intended.
const workshop = { title: "HTML", capacity: 30, reserved: 12 };
const { title, capacity, reserved = 0 } = workshop;
const enriched = { ...workshop, remaining: capacity - reserved };
const [first, second] = ["HTML", "CSS"];Default reserved = 0 is a language behavior, not always the right data policy. If an external catalog requires reserved to be present, silently replacing a missing field could hide a data error. The labโs predefined records always include it.
Map creates a new array from the callbackโs returned values for present elements. For our dense array, it produces one output item per input item. It is the right fit when every workshop needs a derived remaining field.
Return a new record object to preserve the original flat record. Mutating a record inside a map callback would still mutate that referenced record even though map returns a new outer array.
An arrow function that directly returns an object literal wraps it in parentheses. Without the intended return, a block-bodied callback can produce undefined output elements.
const enriched = workshops.map(workshop => ({
...workshop,
remaining: workshop.capacity - workshop.reserved
}));Each dense input record produces a new object with the original fields plus remaining. Verify the source records still have their original fields and values after the transformation.
Filter returns a new array containing the elements whose predicate passes. It can return several matches, one match or an empty array. It does not create new record objects by itself.
Find returns the first matching element, or undefined if none matches. A found object still refers to an existing item; it is not automatically a detached copy. Check the no-match result before reading a property.
Includes is convenient for checking a primitive value such as an ID in an array of IDs. Passing a newly written object with the same fields does not make it match an existing object by structural equality.
const available = enriched.filter(workshop => workshop.remaining > 0);
const chosen = enriched.find(workshop => workshop.id === "w2");
if (chosen === undefined) {
console.log("Workshop not found");
}The predicate must return its decision. A callback with braces but no return can unintentionally select no workshops. An empty filtered array is still truthy; test length when the question is whether results exist.
Some asks whether at least one present element passes its predicate. Every asks whether all present elements pass. Both can stop once the answer is determined. They return booleans rather than a record or a collection.
On an empty array, some returns false and every returns true. That every result follows the universal-test rule: there is no failing item. It does not prove that an actual full workshop exists in the collection.
If the interface means โthere are records and all are full,โ combine a non-empty check with every. The lab shows the raw every result and explains it for an empty selection.
const anyAvailable = enriched.some(w => w.remaining > 0);
const allFull = enriched.every(w => w.remaining === 0);
const nonEmptyAndFull = enriched.length > 0 && allFull;For three available workshops, anyAvailable is true and allFull is false. For an empty selection, the raw values are false and true respectively, while nonEmptyAndFull is false.
Reduce combines present elements using an accumulator. For an available-seat total, start the accumulator at numeric zero and add each remaining count. The callback must return the next accumulator value.
Providing an initial value makes the empty case meaningful: an empty collectionโs total is zero. Reduce without an initial value on an empty array throws TypeError. A safe summary should not rely on there always being a first element.
Reduce can also build richer summaries, but readability matters. A short loop is a good alternative when it explains the task more clearly. Do not use reduce simply to make every algorithm a one-line expression.
const totalRemaining = enriched.reduce(
(total, workshop) => total + workshop.remaining,
0
);
// [] with this initial value gives 0In the normal catalog, the remaining values are 18, 0, 30 and 12. Their total is 60. The total after filtering is the total for the selected workshops, not necessarily the whole catalog.
Sort rearranges its array in place and returns that same array. Without a comparator, ordinary values are compared using string forms in UTF-16 code-unit order, which is unsuitable for many numeric tasks. Copy first if the source order must be preserved.
A numeric ascending comparator can return a - b. A negative return places the first value before the second; a positive return places it after; zero treats their order as equal for sorting. The comparator must describe a consistent ordering, not just return true or false.
Modern JavaScript specifies stable sort, but an explicit tie rule can also make the intended ranking visible. Our catalog sorts equal remaining counts by its stable ID using a simple comparison. This is a teaching policy, not a general locale-aware name-sorting solution.
const numbers = [2, 10, 3];
[...numbers].sort(); // [10, 2, 3]
[...numbers].sort((a, b) => a - b); // [2, 3, 10]
const bySeats = [...enriched].sort((a, b) =>
b.remaining - a.remaining || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
);The original enriched array keeps its order because the sort receiver is a new outer array. The items remain references to the derived records; sorting alone does not deep-copy them.
A useful pipeline gives each step one job. First derive remaining for every trusted workshop record. Then select records by title and availability. Next order the selected records without changing the source. Finally summarize the records that are actually displayed.
Normalize a search phrase deliberately: the lab trims it and uses lowercase matching for its fixed English titles. An empty search phrase matches all titles. It is a simple substring search, not fuzzy matching or a multilingual search engine.
Keep intermediate collections inspectable while learning. A compact chain is useful only if you can explain which value each stage receives and returns. The lab displays the source, selected records and summary separately.
Derive a remaining field on new flat records.
Select by title and availability.
Order a separate result array.
Total the selected remaining counts.
Filtering by available seats removes the full CSS workshop in the normal dataset. Sorting the remaining records from most seats to fewest gives JavaScript, HTML, Accessibility. Mapping their titles supplies those three display labels; reduction totals 60 available seats.
Follow the normal dataset through a fixed available-workshop query. Use the established numbered timeline and playback controls to inspect the role of each method. The source data stays separate from the displayed results.
The fixed catalog contains HTML, CSS, JavaScript and Accessibility.
capacity / reserved: 30/12, 20/20, 40/10, 12/0The predefined records meet the data contract. Preserve their original values and source order.
Choose a normal catalog, an empty catalog, an all-full catalog or equal-seat records. Search the title, select an availability filter and choose the result order. The lab executes fixed array/object operations on trusted predefined data; it does not evaluate arbitrary code or accept an external dataset.
Inspect the original records, the selected derived records, their title list and the summary. A no-match selection is reported explicitly. The raw every-full result is true for an empty selection and is explained rather than presented as proof that workshops exist.
Equal remaining counts use the ID as a tie rule. The fixed titles use simple lowercase comparison; this lab is not a general locale-aware sorting service. Each run begins from the chosen source dataset, so one run does not silently mutate the next runโs input.
Ready to select available workshops.
Confirm an actual array and the required record fields.
Check that callbacks return their mapped value or predicate decision.
Inspect both the outer array and referenced objects.
Check length, find results and reduction initial values.
If map yields undefined entries, inspect the callbackโs return path. If filter returns no records, inspect the predicate and the data types it compares. If a numeric sort gives 10 before 2, check whether a numeric comparator is missing. If an empty result crashes, look for a missing initial accumulator or unchecked records[0].
A changed source title after a copy can indicate shared item references. A changed source order can indicate sort was called directly on the source. Use targeted snapshots or known assertions to inspect the actual contract, rather than assuming every method that returns an array is deeply non-mutating.
For this lab, normal/available/descending yields three records and a total of 60. Empty/all yields zero records and total zero. Full/available yields no matches. Ties/available/descending keeps the ID tie rule explicit.
An array organizes an indexed collection; an object groups named properties. An array of objects can represent a catalog.
No. Const prevents reassignment of the binding, not allowed changes to the array it references.
Slice creates a shallow range copy. Splice changes its receiver and returns the removed items.
Map derives output values for present input elements. Filter selects existing elements whose predicate passes.
Undefined. Check that result before accessing properties.
There is no present item that fails the predicate. Combine it with a non-empty check when the task requires records to exist.
Sort mutates its receiver. Sorting a copied outer array preserves the original array order, while item references may still be shared.
It defines the accumulatorโs starting value and supports an empty collection. For seat totals, initial zero gives an empty total of zero.
Select normal/available/most first. Expected titles: JavaScript, HTML, Accessibility; total 60.
Select empty/all. Expected: zero records, total zero, some available false, raw every full true.
Select full/available. Expected: no matching records; do not read a nonexistent first title.
Use normal/all and search HTML. Expected: one HTML record, remaining total 18.
Search a phrase absent from every title. Expected: an empty selection and clear explanation.
Select ties/available/most first. Expected: IDs t1, t2, t3; each has 10 remaining, total 30.
Copy an array containing one object, then edit the copied item title. Expected: shared item changes the original until you copy that object too.
Total [18, 0, 30, 12] with a loop and reduce initial zero. Expected: both yield 60; empty input yields zero.
An object used for indexed collections and array operations.
A position key used to access an array item, commonly starting at zero.
A named or keyed field on an object.
An association with an object that can be shared by multiple bindings.
A change to an existing object or array.
A new outer container whose nested object references may remain shared.
A function passed to another operation to be called by it.
A function or expression used to decide whether a condition passes.
The carried summary value updated during a reduction.
A function that defines how two values should be ordered for sorting.
Write a pure query function that accepts a trusted flat workshop collection and options for title search, availability and order. Return derived selected records, their title list, a count and total remaining seats. Preserve the source fields and order. State the required input contract.
Use map to create new derived record objects, filter for the query, and copy before sort. Choose an explicit tie rule. Provide an initial zero to reduction. Check a missing find result before reading it, and keep an empty query result distinct from an invalid source record.
Test the four predefined scenarios, a title match and a no-match phrase. Snapshot the input before a query and compare it afterward. Verify 60 selected remaining seats for the normal available query, zero for an empty selection and 30 for the tie dataset. Explain the raw empty every result in plain language.
Success criterion: explain each methodโs input and return shape, prove source preservation for these flat records and report empty results clearly. Level 16 will connect these data results to DOM and browser programming.
Before marking complete, derive and select a catalog, sort a copy, summarize an empty collection and explain a shallow-copy limitation. Completion is a local study marker rather than an assessment score.