// ============================================================ // PART A FINAL DRILL — Tuesday morning, before 11:30am. // 8 snippets covering the most likely categories: // nested loops + string accumulation, reference vs copy, // sort() defaults, slice/immutability/off-by-one, // Object.keys strings, splice-while-iterating, // hidden-space parity, string coercion. // // RULES (same as the real thing): // 1. No paper. No running the code until AFTER you answer. // 2. For each snippet, deliver the full narration OUT LOUD: // signature -> structures -> trace -> pattern name -> EXACT output. // 3. Then scroll to the ANSWER KEY at the bottom and check. // 4. Log any miss against the gotcha taxonomy. // // Target: < 90 seconds per snippet. Do NOT peek early. // ============================================================ // ---- SNIPPET 1: nested loop + string accumulation ---- function s1(str) { let out = ""; for (let i = str.length - 1; i >= 0; i--) { for (let j = 0; j < i; j++) out += "*"; out += str[i]; } return out; } // console.log(s1("abc")); // ---- SNIPPET 2: reference vs shallow copy ---- function s2() { const a = [1, 2, 3]; const b = a; const c = [...a]; b.push(4); c.push(5); return [a.length, b.length, c.length]; } // console.log(s2()); // ---- SNIPPET 3: sort() default behavior ---- function s3(arr) { arr.sort(); return arr; } // console.log(s3([5, 100, 25, 3])); // ---- SNIPPET 4: slice + string immutability + off-by-one ---- function s4(str) { str.slice(0, 3); const tail = str.slice(-2); return tail + str.slice(1, 2); } // console.log(s4("planet")); // ---- SNIPPET 5: Object.keys returns strings ---- function s5(nums) { const freq = {}; for (let n of nums) freq[n] = (freq[n] || 0) + 1; const keys = Object.keys(freq); return keys[0] + keys[1]; } // console.log(s5([9, 9, 30, 30])); // ---- SNIPPET 6: splice while iterating ---- function s6(arr) { for (let i = 0; i < arr.length; i++) { if (arr[i] < 0) arr.splice(i, 1); } return arr; } // console.log(s6([-1, -2, 3, -4, -5])); // ---- SNIPPET 7: hidden character parity ---- function s7(str) { let out = ""; for (let i = 0; i < str.length; i++) { out += i % 2 === 0 ? str[i] : "_"; } return out; } // console.log(s7("go far")); // ---- SNIPPET 8: string coercion mid-loop ---- function s8(arr) { let total = 0; for (let x of arr) total += x; return total; } // console.log(s8([1, 2, "3", 4])); // ============================================================ // ============================================================ // // S T O P. // // Narrate all 8 out loud first. Exact outputs stated. // Then read the answer key below and check yourself. // // ============================================================ // ============================================================ // ---- ANSWER KEY ---- // // SNIPPET 1 -> "**c*ba" // Loop runs BACKWARDS (i from 2 down to 0). Each pass pads // i stars, then appends str[i]: // i=2: "**" + "c" -> "**c" // i=1: "*" + "b" -> "**c*b" // i=0: (no stars) + "a" -> "**c*ba" // Traps: reverse iteration direction + star count comes from // the INDEX, not the character. Pattern name: "reverse walk // with index-sized padding." // Narration line: "The outer loop descends, so the last // character is emitted first." // // SNIPPET 2 -> [4, 4, 4] // b = a is an ALIAS (same array). c = [...a] is a real copy // taken BEFORE the pushes. b.push(4) grows a AND b to 4. // c was [1,2,3], its own push(5) grows it to 4. // All three read 4 — for two different reasons. If you said // [4, 4, 5] you forgot c was copied before b.push. If you // said [3, 4, 4] you missed the alias. // Narration line: "Assignment aliases; spread copies — and // the copy freezes the state at the moment it's taken." // // SNIPPET 3 -> [100, 25, 3, 5] // Default sort converts to STRINGS: "100" < "25" < "3" < "5" // lexicographically (compares char by char: "1" < "2" < "3" // < "5"). Also note: sort() MUTATES arr in place and returns // the same reference. // Narration line: "No comparator, so JavaScript sorts these // as strings — 100 comes first because the character '1' is // smallest." // // SNIPPET 4 -> "etl" // Line 1 is a DECOY: strings are immutable and the slice // result is thrown away — str is unchanged. // str.slice(-2) = last two chars = "et". // str.slice(1, 2) = index 1 only (end-exclusive) = "l". // "et" + "l" = "etl". // Traps: the dead line, negative slice, end-exclusivity. // Narration line: "The first slice does nothing — the result // isn't assigned. Strings are immutable." // // SNIPPET 5 -> "930" // freq = {9: 2, 30: 2}. Object.keys returns ["9", "30"] — // STRINGS, in ascending numeric order (JS orders integer-like // keys numerically). "9" + "30" is string CONCATENATION, // not addition: "930". If you said 39, you added numbers // that were never numbers. // Narration line: "Object.keys always returns strings, so // plus means concatenate here." // // SNIPPET 6 -> [-2, 3, -5] // The splice-while-iterating bug, adjacent-negatives flavor: // i=0: -1 removed, everything shifts left -> [-2, 3, -4, -5] // ...but i increments to 1, so -2 (now at index 0) // is SKIPPED. // i=1: 3, not negative, stays. // i=2: -4 removed -> [-2, 3, -5]; -5 slides to index 2, // i increments to 3, past the end. -5 SKIPPED. // Every removal skips its right neighbor. Fix if asked: // iterate backwards, or use filter. // Narration line: "Each splice shifts the array left while i // still moves right, so the element after every removal gets // skipped." // // SNIPPET 7 -> "g_ _a_" // "go far" = g(0) o(1) ' '(2) f(3) a(4) r(5). // Even indices kept, odd replaced with underscore: // g, _, ' ', _, a, _ -> "g_ _a_" // The trap: the SPACE sits at an even index, so it's KEPT — // the middle of the output is underscore-space-underscore, // which looks wrong but isn't. Spaces are characters. They // have indices. // Narration line: "Index 2 is the space and 2 is even, so // the space survives." // // SNIPPET 8 -> "334" // total starts as NUMBER 0: // 0 + 1 = 1, 1 + 2 = 3 (still numbers) // 3 + "3" = "33" (+ with a string CONCATENATES, // and total is now a STRING) // "33" + 4 = "334" // One string element permanently flips the accumulator's // type. Everything after it concatenates. // Narration line: "The plus operator concatenates the moment // either side is a string — and the poison spreads." // // ============================================================ // SCORING: 8/8 = ready. 6-7 = re-trace the misses out loud // once and you're ready. Same gotcha missed twice = say that // taxonomy row out loud three times, then stop. // // To verify any answer for real: uncomment its console.log // and run `node partA-final-drill.js`. // // After this: cheatsheet once, decision rule out loud, // close the laptop. 11:30 is yours. // ============================================================