Go One Primitive Lower
The button that says "I still don't understand how to implement this" does one thing: it makes the thing smaller. addItem → Find Item → Loop Through Array → Compare IDs. At some rung you already know what to type, and that rung is the start.
The situation, the reflex, and why it stalls
Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.
You have the pseudocode for addItem in front of you and it still does not turn into code. What is the one move that always makes progress — and how far down does it go?
The concept builder shows item = find entry in cart.items with item.productId == productId. You read it and nod. You try to write it and your hands stop. "Find entry" is not a thing your editor knows. You are not sure whether you are stuck on the cart, on the language, or on yourself.
Read the reference implementation. It is one click away, it is four languages, and cart.items.find((i) => i.productId === productId) is a single line; reading it feels like the fastest way to become unstuck. Or search "find item in array typescript" and paste the first answer, which is the same line.
The line is copied and the stuck feeling is intact. find with an arrow function is exactly the part you did not understand, so you now have an addItem you can run and cannot modify — the next requirement ("find by product id and size") has no place to go.
- The line is copied and the stuck feeling is intact.
findwith an arrow function is exactly the part you did not understand, so you now have an addItem you can run and cannot modify — the next requirement ("find by product id and size") has no place to go. - The search result is for a different question. "Find item in array" gives you the syntax; your question was "what does finding mean here, and what would I do if the language had no
find?" — the question whose answer would have transferred toremoveItem. - Progress is measured by lines in the editor and the count went up by one. Ask what you could now write from a blank file and the answer is still nothing.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- If you do not know how to build the thing, make the thing smaller until you reach something you do know how to build. That is the whole principle, and it is a button, not a course: press it once and the thing you are stuck on is replaced by a smaller thing. Press it again if the smaller thing is still too big.
- Each press names the next rung. addItem is stuck → the stuck part is Find Item → Find Item is "look at each entry" → looking at each entry is Loop Through Array → inside the loop, the only work is Compare IDs. Four presses, and the last rung is an
ifwith an==in it, which is something everyone who has written any code has written. - Notice what you are asking at each rung. Not "how do I write this?" but "what is this made of?" — the first question invites a search for syntax, the second produces the next rung. Syntax is looked up once, at the bottom, for a thing you already understand.
- Then climb: the comparison goes inside the loop, the loop becomes findItem, findItem goes inside addItem. The line
cart.items.find(…)is what the climb collapses into once you have written the loop by hand — and now it reads as a loop you understand rather than a spell.
The ladder the button walks
Each rung is one press of "I still don't understand how to implement this". Reading downward, every level says what it is made of; reading upward, every level says why the one below it exists. The ladder stops where this learner's knowledge starts — for someone else it would stop one rung higher, and the cart would come out the same.
The bottom rung is deliberately boring. A comparison inside a loop is the kind of thing nobody would put in a lesson, and that is the point: the principle works because the boring thing is already known.
- addItemCheck product, check quantity, find existing entry, increase or append, return the cart. — The operation as the concept wrote it; the stuck part is one step of it.
- Find ItemGiven a product id, hand back the entry with that id, or nothing. — The step that could not be written; named so it can be tested alone and used as one line above.
- Loop Through ArrayVisit each entry of cart.items, in order, once. — "Find" means "look at each until one matches"; the looking is a loop. If loops are new, this is where Arrays is one link away.
- Compare IDsitem.productId == productId — true or false for this entry. — The only work inside the loop. An equality on two strings is something already known, so the button stops here.
The bottom rung, then the rung above it
Pseudocode first, because the algorithm is language-neutral and the concept ladder shows the same thing in four languages. The bottom rung is the loop; the rung above uses it as a single call. When the reference collapses the loop into find, the behaviour is unchanged — which you can now check, because you wrote the long form.
1function findItem(cart, productId):2 for each item in cart.items: -- Loop Through Array3 if item.productId == productId: -- Compare IDs4 return item -- return early on the first match5 return nothing6 7function addItem(cart, productId, quantity = 1):8 if not catalog.has(productId): reject "unknown product"9 if quantity <= 0: reject "quantity must be positive"10 existing = findItem(cart, productId) -- the rung below, as one line11 if existing exists:12 existing.quantity = existing.quantity + quantity13 else:14 append { productId, quantity } to cart.items15 return cartThe two comments in findItem are the two bottom rungs. The reference's cart.items.find((i) => i.productId === productId) is this loop with the comparison passed in — same visits, same early return, O(n) either way.
Asking for the rung, not the syntax
The question you ask when stuck decides whether you get a rung or a spell. "How do I find an item in an array in TypeScript" returns syntax for a thing you do not yet understand. The best form asks what the step is made of, which is answerable without any language and transfers to the next operation.
why The best form produces the ladder: a loop, a comparison, an early return. It can be answered on paper, it makes the reference's one-liner readable afterwards, and it is the same question that unlocks removeItem and total. The middle form produces a line that works and teaches nothing about the next line.
The implementation ladder
Concept, examples, pseudocode, code, tests, production — for the concept this lesson is about. Code is the fourth tab, not the first.
Shopping Cart = A temporary collection of products the user intends to purchase, held between browsing and checkout.
- Does a cart have identity? Yes, weakly. Two carts with the same items are still two carts, because each belongs to someone and will become a different order. It needs an id once it leaves memory; in memory the variable is the identity.
- Who owns it? A shopper — a logged-in user or an anonymous session. The owner is part of the state because "my cart" has to be findable again.
- How long does it exist? From the first add until checkout or abandonment. Whether it survives a reload, a closed browser or a login is not a property of the concept; it is a persistence decision made later, and each answer changes where the cart lives.
- Should it survive reload? Usually yes for a store, usually no for a demo. V1 in memory says no; V2 browser storage says yes on one device; V3 server storage says yes everywhere the user is logged in.
- Should it survive login? Only if an anonymous cart and a logged-in cart are merged — a rule that does not exist in V1 and appears as a modification later.
- itemscollection of CartItemkeepThe cart is its items; without them nothing else means anything.
- items[].productIdidkeepThe reference to what is being bought. The catalog owns the product; the cart only points at it.
- items[].quantityinteger > 0keepTwo laptops is one entry with quantity 2, not two entries — the rule "one entry per product" needs a quantity to hold.
- owneruser id or session iddependsSo the cart can be found again by the person it belongs to.
- items[].productNamestringderiveIt would be convenient to render the cart without a catalog lookup.
- items[].pricemoneydependsThe total needs a price per item.
- totalmoneydropEvery screen shows the total.
- currencycodedependsPrices need a currency to be added.
- createdAttimestampdropAbandoned carts might be expired or emailed about.
- update Add item — the updated cart
- delete Remove item — the updated cart
- update Change quantity — the updated cart
- read View items — the list of entries — product id and quantity — for rendering
- domain Calculate total — the sum of price × quantity over the entries
- delete Clear cart — the empty cart
- • Every quantity is greater than zero.
- • One logical entry per product.
- • The total is never negative.
- • An unknown product cannot be added.
- • Quantity cannot exceed available stock — if inventory is enforced here.
How to do it
Most important first.
- Say out loud the exact line or phrase you cannot write. Not "addItem" — "find the entry". Precision about where you are stuck is most of the move (Unknown to Specific Question).
- Ask "what is that made of?" and write the answer as a rung below it. One rung per press; do not skip to the bottom.
- Stop at the first rung you could write with your eyes closed. If you cannot say whether you could, try to write it — thirty seconds decides it.
- Write that rung as its own function with a name from the concept, test it with one example, then climb one rung and use it (Implement One Operation).
- If a rung is not "too big" but "I do not know what this word means" — callback, map, iterator — that is a foundation, not a size problem; route it and come back (When the Rung Below Is a Foundation).
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Press one: "I cannot write addItem" → the checks and the branch are fine; the stuck part is "find the existing entry". Press two: "find the existing entry" → look at every entry until one has the right product id. Press three: "look at every entry" → a loop over cart.items. Press four: "the right product id" →
item.productId == productId. Stop: this is anif. - Written from the bottom:
for each item in cart.items: if item.productId == productId: return itemand, after the loop,return nothing. Tested: on[Laptop × 1]with "laptop" it returns the entry; on[]it returns nothing. NamedfindItem. - Climbed:
existing = findItem(cart, productId)thenif existing: existing.quantity += quantity else: append { productId, quantity }. The example "Laptop × 1, add Laptop → Laptop × 2" runs through it by hand, with the loop visiting one entry and matching. - The reference's one-liner, read afterwards:
cart.items.find((i) => i.productId === productId)is the loop with the comparison handed in as a function. It is shorter, it is the same thing, and the learner can now say which line would change if the rule became "find by product id and size".
How you know it worked
What now exists that did not before, and what question you can now ask.
- You can name the rung you are on. "I am stuck on Find Item, not on the cart" is a different sentence from "I cannot do this".
- The bottom rung was written in under a minute, because it was already known — the button stopped at the right place.
- The one-liner in the reference reads as a compression of something you wrote, not as a thing to memorise.
- The next stuck moment is shorter, because "what is this made of?" is now the first thing you ask.
The questions you can now ask
The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.
- ?What is the exact phrase I cannot write — one step, not the whole operation?
- ?What is that step made of, and can I write any of the pieces right now?
- ?Is this rung too big, or is it a word I do not know — and which of those needs a different move?
- ?Once the bottom rung works, which rung above it uses it, and what example proves that level?
What can go wrong
- Pressing the button past the boundary. Going from "compare ids" to "how does string equality work in memory" is a fascinating detour that does not produce addItem; stop at the first rung you can write.
- Pressing it on the wrong thing. If the whole cart is on the stuck line, the first press must pick one operation; "make the cart smaller" gives "make addItem" and nothing smaller than that until you choose.
- Treating the button as a way to avoid learning. A learner who cannot write a loop at all will press it into "visit each element" and be stuck at a foundation; the button finds the gap, it does not fill it.
- Never climbing back. The point of the bottom rung is to be used by the rung above it; a folder of tiny functions that were never assembled into an operation is the recursion without its return.
- Writing the loop by hand and then reading the one-liner takes longer than reading the one-liner. The extra minutes buy the ability to write the next one-liner unaided.
- The hand-written loop is more code than
find; in production it would be replaced. The replacement is safe precisely because the loop was understood first. - The button is personal and a lab can only approximate where it stops for you; it may offer a rung you did not need, and you should skip it.
- "So the right way to write addItem is with a for loop." The reference uses
find; the loop is how you earn the right to usefind. Once you can say whatfindis made of, use it. - "Going lower means learning the language internals." One rung lower than the stuck line, not all the way to the machine. The button stops at the first thing you can write.
- "If I have to go this low, I am not ready for the cart." The cart is a beginner concept precisely because its primitives are a loop and a comparison; reaching them is finishing the cart, not failing it.
Where this applies
Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.
- GENERALMaking a thing smaller until a known piece appears applies to any implementation, in any language, at any level of experience; what varies is the rung it stops on.
- TEAM-SPECIFICA solo learner meets the button at "find item"; an engineer new to a language meets it at "how do I express a loop here"; an engineer new to a domain meets it at the operation. Each stops one rung below their own boundary — see the next lesson.
- ILLUSTRATIVELaptop × 1, Laptop × 2, four presses and the thirty seconds are for the shape of the move; nothing was timed.
Where the depth lives
This domain asks the question and hands the answer off by name.