Interview Prep
Problems modelled on what technical interviews ask. Solve them in JavaScript, TypeScript, Python or SQL.
Difficulty
Topics
Language
Real throttle is hard to test deterministically with timers. Instead, implement solve(events, interval) that simulates a leading-edge throttle. events is a list of [timestampMs, value] pairs representing calls to a throttled function. Return only the [timestamp, value] pairs that would actually fire (one at most per interval window). Semantics (leading edge) The first call in a window fires immediately. Subsequent calls within the same window are dropped. A new window starts interval ms after the last fired call. solve([[0,'a'],[5,'b'],[10,'c'],[20,'d']], 15) → [[0,'a'],[20,'d']]
Real debounce is timing-based and hard to test deterministically. Instead, implement solve(events, wait) that simulates trailing-edge debounce over a fixed timeline. events is a list of [timestampMs, value] pairs representing calls to a debounced function. Return a list of [emitTimeMs, value] pairs that the debounced function would actually fire. Semantics (trailing edge) Every call resets the timer. The timer fires wait ms after the last call inside a quiet window. If a new call arrives after the previous schedule fired, the previous emission stays in the result. Example solve([[0,'a'], [50,'b'], [200,'c']], 100) → [[150,'b'], [300,'c']]
Callback hell makes code impossible to follow and debug. Refactor solve(userData, ordersData, productsData) using async/await so the function is flat and readable. The fake fetchers are already provided as Promises — no callbacks. solve({ id: 'u1', name: 'Alice' }, [{ id: 'o1' }], [{ id: 'p1' }]) → Promise<{ user, orders, products }>
Implement solve(start, stop, step) as a generator function that yields integers from start up to (but not including) stop, incrementing by step each time. Examples list(solve(0, 10, 2)) → [0, 2, 4, 6, 8] list(solve(1, 6, 1)) → [1, 2, 3, 4, 5] list(solve(5, 0, -1)) → [5, 4, 3, 2, 1] list(solve(0, 0, 1)) → [] Constraints step may be negative (count down). If step == 0, raise a ValueError. Do not use Python's built-in range(). Your function must use the yield keyword. In JavaScript / TypeScript — Write range as a generator function (function*) using yield; solve spreads it into an array. A zero step throws a RangeError.
jest.useFakeTimers() replaces the real clock with one the test controls: timers only fire when the test advances time. solve(ops) runs a list of operations against a fresh fake clock (starting at 0) and returns their results: | Op | Result | |---|---| | ['setTimeout', label, delay] | timer ID (0, 1, …). The timer is due at now + delay. | | ['clearTimeout', id] | true if a pending timer was cancelled, false otherwise (unknown, already fired or already cleared) | | ['advance', ms] | moves the clock forward by ms, fires every due timer, returns their labels in firing order | | ['runAll'] | fires every pending timer, returns their labels; the clock ends at the last fired timer's due time | | ['pending'] | number of timers still waiting | | ['now'] | current fake time | Timers fire in order of due time; timers due at the same time fire in creation order.
Implement solve(start, end, step) using a generator function that yields numbers from start (inclusive) to end (exclusive) with the given step. The solve function must return the values collected into an array. Internally it must use a function* generator (not a plain loop + push). Bonus: Make the generator lazily evaluated — it should only compute the next value when requested by the iterator protocol.
Implement withTimeout(promise, ms) that races a given promise against a timeout. If the promise resolves before ms milliseconds, return its value. Otherwise throw/reject with 'TIMEOUT'. solve(resolveAfter, timeoutMs) simulates this by creating a promise that resolves with 'done' after resolveAfter ms.
Implement a minimal Observable: solve(ops) replays a sequence of operations and returns all emitted values. Operations: { type: 'next'|'error'|'complete'|'unsubscribe', value? }. Emissions after complete or unsubscribe must be silently ignored.
Implement memoizeAsync(fn) that memoizes an async function. Crucially, concurrent calls with the same key must share the in-flight promise — not launch separate requests. solve(callGroups) simulates concurrent callers. Each group is an array of argument lists that "arrive simultaneously". Return { results, calls }.
Implement promiseRace(promises) — return a promise that resolves or rejects with the first promise that settles. solve(delays, shouldReject) simulates a race: each entry is a [delayMs, value] pair. If the corresponding shouldReject flag is true the promise rejects with value instead. Constraint: Do NOT use Promise.race itself.
Implement promiseAllSettled(promises) — returns a promise that resolves once all input promises have settled (either fulfilled or rejected), with an array of {status, value} or {status, reason} descriptors. Do NOT use Promise.allSettled itself.
Implement retry(fn, maxAttempts) that calls fn() up to maxAttempts times. Return the first successful result. If all attempts fail, throw the last error. solve(failCount, value) creates a function that rejects the first failCount times then resolves with value. It returns {result, attempts}.
Re-implement the semantics of Promise.all as a function promiseAll. Don't delegate to native Promise.all — write the orchestration yourself using a single new Promise and the .then callbacks of each input. Requirements Resolves with an array of results in input order, regardless of which promise settled first. Rejects with the first encountered rejection reason. An empty input must resolve with []. solve(values, options) is provided to drive your implementation against test inputs and return a JSON-friendly outcome.
Implement solve(items, num_consumers) using the producer/consumer pattern: A producer thread puts each item from items into a queue.Queue. num_consumers consumer threads each pop items, double the value, and store results. Return the results sorted ascending (order is non-deterministic with threads). Examples solve([1, 2, 3], 2) → [2, 4, 6] solve([5], 1) → [10] Constraints Use queue.Queue for thread-safe communication. Signal consumers with sentinel values (None) when production is done. All threads must finish before returning. In JavaScript / TypeScript — JavaScript has no threads, but the pattern is identical with async tasks: implement the starter's AsyncQueue (get() waits until an item arrives), run one producer and numConsumers consumers concurrently, and stop each consumer on a null sentinel.
Build a lazy three-stage pipeline using generators: read_numbers(data) — yields each integer from data (a list). square_odds(source) — consumes the previous generator, yields the square of each odd number. take(source, n) — yields the first n items from source. Implement solve(data, n) that chains the three generators and returns the results as a list. Examples solve([1,2,3,4,5,6,7], 3) → [1, 9, 25] solve([2,4,6], 3) → [] (no odds) solve([1,3,5,7,9], 2) → [1, 9] In JavaScript / TypeScript — Use generator functions (function*): readNumbers, squareOdds and take. take must stop pulling from its source once it has yielded n items.
Implement solve(items, limit) that processes a list of integers concurrently but allows at most limit tasks running at the same time. Each "task" doubles its input value. Return the results in the same order as input. Examples solve([1, 2, 3, 4, 5], 2) → [2, 4, 6, 8, 10] solve([10], 1) → [20] Constraints Use asyncio.Semaphore(limit) to cap concurrency. Results must preserve input order. In JavaScript / TypeScript — Implement the small Semaphore class of the starter (acquire() waits for a free slot, release() hands it to the next waiter), then process every item through it with Promise.all.
Implement solve(delays) that: Creates one coroutine per item in delays — each coroutine waits delay seconds then returns delay 10. Runs all coroutines concurrently with asyncio.gather. Returns the list of results in the same order as the input. Examples solve([0.1, 0.2, 0.05]) → [1, 2, 0] (delays × 10, rounded down) solve([0.3]) → [3] Constraints Use asyncio.gather — do not run coroutines sequentially. Results must preserve input order (gather guarantees this). Total wall-clock time should be ≈ max(delays), not sum(delays). The checker controls sleep completion to verify that every worker waits, all workers start before any finishes, and results preserve input order. In JavaScript / TypeScript — worker(delay) waits delay seconds with setTimeout, then returns Math.floor(delay 10). Promise.all is the counterpart of asyncio.gather: concurrent, results in input order.
Implement a Scheduler class that limits the number of concurrently running async tasks. When more tasks than concurrency are added, the excess tasks are queued and executed as slots free up. solve(tasks, concurrency) runs tasks modelled as [delayMs, value] pairs and returns the order they complete in.
Build an AsyncQueue(concurrency) class that runs at most concurrency async tasks in parallel and queues the rest. add(task) accepts a thunk returning a Promise and returns a Promise that resolves with the task's value once it actually runs. The queue must: never run more than concurrency tasks at once; resolve each add Promise individually as the task settles; not deadlock if a task throws — failed tasks must release their slot. The harness's solve returns the maximum number of in-flight tasks observed alongside the ordered results, so the test suite can verify the concurrency cap.