ace-interview-prep 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/commands/add.js +92 -0
- package/dist/commands/feedback.js +133 -0
- package/dist/commands/generate.js +224 -0
- package/dist/commands/init.js +100 -0
- package/dist/commands/list.js +107 -0
- package/dist/commands/reset.js +68 -0
- package/dist/commands/score.js +70 -0
- package/dist/commands/setup.js +84 -0
- package/dist/commands/test.js +85 -0
- package/dist/index.js +72 -0
- package/dist/lib/categories.js +103 -0
- package/dist/lib/config.js +61 -0
- package/dist/lib/llm.js +134 -0
- package/dist/lib/paths.js +38 -0
- package/dist/lib/scaffold.js +110 -0
- package/dist/lib/scorecard.js +116 -0
- package/dist/prompts/code-review.md +59 -0
- package/dist/prompts/design-review.md +67 -0
- package/dist/prompts/question-brainstorm.md +31 -0
- package/dist/prompts/question-generate.md +65 -0
- package/dist/templates/design/notes.md.hbs +27 -0
- package/dist/templates/js-ts/solution.test.ts.hbs +11 -0
- package/dist/templates/js-ts/solution.ts.hbs +11 -0
- package/dist/templates/leetcode-algo/solution.test.ts.hbs +11 -0
- package/dist/templates/leetcode-algo/solution.ts.hbs +11 -0
- package/dist/templates/leetcode-ds/solution.test.ts.hbs +11 -0
- package/dist/templates/leetcode-ds/solution.ts.hbs +11 -0
- package/dist/templates/react-apps/App.test.tsx.hbs +16 -0
- package/dist/templates/react-apps/App.tsx.hbs +16 -0
- package/dist/templates/readme.md.hbs +9 -0
- package/dist/templates/web-components/component.test.ts.hbs +11 -0
- package/dist/templates/web-components/component.ts.hbs +22 -0
- package/dist/templates/web-components/index.html.hbs +12 -0
- package/package.json +72 -0
- package/questions/design-be/url-shortener/README.md +23 -0
- package/questions/design-be/url-shortener/notes.md +27 -0
- package/questions/design-be/url-shortener/scorecard.json +1 -0
- package/questions/design-fe/news-feed/README.md +22 -0
- package/questions/design-fe/news-feed/notes.md +27 -0
- package/questions/design-fe/news-feed/scorecard.json +1 -0
- package/questions/design-full/google-docs/README.md +22 -0
- package/questions/design-full/google-docs/notes.md +27 -0
- package/questions/design-full/google-docs/scorecard.json +1 -0
- package/questions/js-ts/debounce/README.md +86 -0
- package/questions/js-ts/debounce/scorecard.json +9 -0
- package/questions/js-ts/debounce/solution.test.ts +128 -0
- package/questions/js-ts/debounce/solution.ts +4 -0
- package/questions/leetcode-algo/two-sum/README.md +58 -0
- package/questions/leetcode-algo/two-sum/scorecard.json +1 -0
- package/questions/leetcode-algo/two-sum/solution.test.ts +55 -0
- package/questions/leetcode-algo/two-sum/solution.ts +4 -0
- package/questions/leetcode-ds/lru-cache/README.md +70 -0
- package/questions/leetcode-ds/lru-cache/scorecard.json +1 -0
- package/questions/leetcode-ds/lru-cache/solution.test.ts +82 -0
- package/questions/leetcode-ds/lru-cache/solution.ts +14 -0
- package/questions/react-apps/todo-app/App.test.tsx +130 -0
- package/questions/react-apps/todo-app/App.tsx +10 -0
- package/questions/react-apps/todo-app/README.md +23 -0
- package/questions/react-apps/todo-app/scorecard.json +9 -0
- package/questions/web-components/star-rating/README.md +45 -0
- package/questions/web-components/star-rating/component.test.ts +64 -0
- package/questions/web-components/star-rating/component.ts +28 -0
- package/questions/web-components/star-rating/index.html +14 -0
- package/questions/web-components/star-rating/scorecard.json +9 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{#if testCode}}
|
|
2
|
+
{{{testCode}}}
|
|
3
|
+
{{else}}
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { render, screen } from '@testing-library/react';
|
|
6
|
+
import App from './App';
|
|
7
|
+
|
|
8
|
+
describe('{{title}}', () => {
|
|
9
|
+
it('renders without crashing', () => {
|
|
10
|
+
render(<App />);
|
|
11
|
+
expect(screen.getByText('{{title}}')).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it.todo('add more test cases');
|
|
15
|
+
});
|
|
16
|
+
{{/if}}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{#if solutionCode}}
|
|
2
|
+
{{{solutionCode}}}
|
|
3
|
+
{{else}}
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
// TODO: implement your React app here
|
|
7
|
+
|
|
8
|
+
export default function App() {
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<h1>{{title}}</h1>
|
|
12
|
+
{/* TODO: implement */}
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
{{/if}}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{{#if solutionCode}}
|
|
2
|
+
{{{solutionCode}}}
|
|
3
|
+
{{else}}
|
|
4
|
+
{{#if signature}}
|
|
5
|
+
{{{signature}}}
|
|
6
|
+
{{else}}
|
|
7
|
+
// TODO: implement your web component here
|
|
8
|
+
|
|
9
|
+
export class MyComponent extends HTMLElement {
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this.attachShadow({ mode: 'open' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectedCallback() {
|
|
16
|
+
// TODO: implement
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// customElements.define('my-component', MyComponent);
|
|
21
|
+
{{/if}}
|
|
22
|
+
{{/if}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{title}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<!-- TODO: use your web component here -->
|
|
10
|
+
<script type="module" src="./component.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ace-interview-prep",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for frontend interview preparation — scaffolds questions with tests, tracks progress, and provides LLM-powered feedback",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ace": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"questions/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup && node scripts/postbuild.js",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"ace": "tsx cli/index.ts",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"interview",
|
|
27
|
+
"frontend",
|
|
28
|
+
"practice",
|
|
29
|
+
"cli",
|
|
30
|
+
"typescript",
|
|
31
|
+
"react",
|
|
32
|
+
"leetcode",
|
|
33
|
+
"system-design",
|
|
34
|
+
"web-components"
|
|
35
|
+
],
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/neel/ace-interview-prep.git"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/neel/ace-interview-prep#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/neel/ace-interview-prep/issues"
|
|
43
|
+
},
|
|
44
|
+
"author": "Neel",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@anthropic-ai/sdk": "^0.74.0",
|
|
51
|
+
"chalk": "^5.6.2",
|
|
52
|
+
"cli-table3": "^0.6.5",
|
|
53
|
+
"dotenv": "^17.3.1",
|
|
54
|
+
"handlebars": "^4.7.8",
|
|
55
|
+
"openai": "^6.22.0",
|
|
56
|
+
"prompts": "^2.4.2"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
60
|
+
"@testing-library/react": "^16.3.2",
|
|
61
|
+
"@types/prompts": "^2.4.9",
|
|
62
|
+
"@types/react": "^19.2.14",
|
|
63
|
+
"@types/react-dom": "^19.2.3",
|
|
64
|
+
"happy-dom": "^20.6.1",
|
|
65
|
+
"react": "^19.2.4",
|
|
66
|
+
"react-dom": "^19.2.4",
|
|
67
|
+
"tsup": "^8.5.1",
|
|
68
|
+
"tsx": "^4.21.0",
|
|
69
|
+
"typescript": "^5.9.3",
|
|
70
|
+
"vitest": "^4.0.18"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Design a URL Shortener
|
|
2
|
+
|
|
3
|
+
**Category:** Backend Design
|
|
4
|
+
**Difficulty:** Medium
|
|
5
|
+
**Suggested Time:** ~40 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Design a URL shortening service (like bit.ly) from a **backend perspective**.
|
|
12
|
+
|
|
13
|
+
Consider the following aspects:
|
|
14
|
+
|
|
15
|
+
- **Encoding scheme** — How to generate short codes (base62, UUID, etc.)
|
|
16
|
+
- **Database design** — Schema for URLs, mappings, metadata
|
|
17
|
+
- **Read/write ratio** — Optimize for heavy read traffic
|
|
18
|
+
- **Caching** — Cache hot short URLs for fast redirects
|
|
19
|
+
- **Analytics** — Click tracking, geographic data
|
|
20
|
+
- **Expiration** — Optional TTL for short links
|
|
21
|
+
- **Horizontal scaling** — Sharding, load balancing
|
|
22
|
+
|
|
23
|
+
Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Design a URL Shortener — Design Notes
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
### Functional Requirements
|
|
6
|
+
<!-- List the core features and user-facing requirements -->
|
|
7
|
+
|
|
8
|
+
### Non-Functional Requirements
|
|
9
|
+
<!-- Performance, scalability, availability, security, etc. -->
|
|
10
|
+
|
|
11
|
+
## High-Level Architecture
|
|
12
|
+
<!-- Describe the overall system architecture -->
|
|
13
|
+
|
|
14
|
+
## Data Model
|
|
15
|
+
<!-- Define key data structures, schemas, etc. -->
|
|
16
|
+
|
|
17
|
+
## API Design
|
|
18
|
+
<!-- Endpoint design, data contracts, etc. -->
|
|
19
|
+
|
|
20
|
+
## Deep Dive
|
|
21
|
+
<!-- Pick 1-2 areas to go deep on -->
|
|
22
|
+
|
|
23
|
+
## Scaling Considerations
|
|
24
|
+
<!-- How does this scale? -->
|
|
25
|
+
|
|
26
|
+
## Trade-offs
|
|
27
|
+
<!-- What trade-offs did you make? -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"title":"Design a URL Shortener","category":"design-be","difficulty":"medium","suggestedTime":40,"status":"untouched","attempts":[],"llmFeedback":null}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Design a News Feed
|
|
2
|
+
|
|
3
|
+
**Category:** Frontend Design
|
|
4
|
+
**Difficulty:** Hard
|
|
5
|
+
**Suggested Time:** ~55 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Design a social media news feed (like Facebook or Twitter feed) from a **frontend perspective**.
|
|
12
|
+
|
|
13
|
+
Consider the following aspects:
|
|
14
|
+
|
|
15
|
+
- **Infinite scroll** — Loading more content as the user scrolls
|
|
16
|
+
- **Real-time updates** — New posts appearing without a full refresh
|
|
17
|
+
- **Optimistic UI** — Immediate feedback for likes, comments, shares
|
|
18
|
+
- **Caching** — Stale-while-revalidate, cache invalidation strategies
|
|
19
|
+
- **Virtualization** — Rendering only visible items for performance
|
|
20
|
+
- **Accessibility** — Screen readers, keyboard navigation, focus management
|
|
21
|
+
|
|
22
|
+
Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Design a News Feed — Design Notes
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
### Functional Requirements
|
|
6
|
+
<!-- List the core features and user-facing requirements -->
|
|
7
|
+
|
|
8
|
+
### Non-Functional Requirements
|
|
9
|
+
<!-- Performance, scalability, availability, security, etc. -->
|
|
10
|
+
|
|
11
|
+
## High-Level Architecture
|
|
12
|
+
<!-- Describe the overall system architecture -->
|
|
13
|
+
|
|
14
|
+
## Data Model
|
|
15
|
+
<!-- Define key data structures, schemas, etc. -->
|
|
16
|
+
|
|
17
|
+
## API Design
|
|
18
|
+
<!-- Endpoint design, data contracts, etc. -->
|
|
19
|
+
|
|
20
|
+
## Deep Dive
|
|
21
|
+
<!-- Pick 1-2 areas to go deep on -->
|
|
22
|
+
|
|
23
|
+
## Scaling Considerations
|
|
24
|
+
<!-- How does this scale? -->
|
|
25
|
+
|
|
26
|
+
## Trade-offs
|
|
27
|
+
<!-- What trade-offs did you make? -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"title":"Design a News Feed","category":"design-fe","difficulty":"hard","suggestedTime":55,"status":"untouched","attempts":[],"llmFeedback":null}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Design Google Docs
|
|
2
|
+
|
|
3
|
+
**Category:** Full-Stack Design
|
|
4
|
+
**Difficulty:** Hard
|
|
5
|
+
**Suggested Time:** ~60 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Design a collaborative document editor (like Google Docs) from a **full-stack perspective**.
|
|
12
|
+
|
|
13
|
+
Consider the following aspects:
|
|
14
|
+
|
|
15
|
+
- **Real-time collaboration** — Multiple users editing simultaneously
|
|
16
|
+
- **Conflict resolution** — Operational Transformation (OT) vs CRDTs
|
|
17
|
+
- **Document storage** — Versioning, persistence, recovery
|
|
18
|
+
- **Permissions** — View/edit access, sharing, roles
|
|
19
|
+
- **Offline support** — Edit offline, sync when reconnected
|
|
20
|
+
- **Cursor presence** — Showing other users' cursors and selections
|
|
21
|
+
|
|
22
|
+
Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Design Google Docs — Design Notes
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
### Functional Requirements
|
|
6
|
+
<!-- List the core features and user-facing requirements -->
|
|
7
|
+
|
|
8
|
+
### Non-Functional Requirements
|
|
9
|
+
<!-- Performance, scalability, availability, security, etc. -->
|
|
10
|
+
|
|
11
|
+
## High-Level Architecture
|
|
12
|
+
<!-- Describe the overall system architecture -->
|
|
13
|
+
|
|
14
|
+
## Data Model
|
|
15
|
+
<!-- Define key data structures, schemas, etc. -->
|
|
16
|
+
|
|
17
|
+
## API Design
|
|
18
|
+
<!-- Endpoint design, data contracts, etc. -->
|
|
19
|
+
|
|
20
|
+
## Deep Dive
|
|
21
|
+
<!-- Pick 1-2 areas to go deep on -->
|
|
22
|
+
|
|
23
|
+
## Scaling Considerations
|
|
24
|
+
<!-- How does this scale? -->
|
|
25
|
+
|
|
26
|
+
## Trade-offs
|
|
27
|
+
<!-- What trade-offs did you make? -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"title":"Design Google Docs","category":"design-full","difficulty":"hard","suggestedTime":60,"status":"untouched","attempts":[],"llmFeedback":null}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Implement Debounce
|
|
2
|
+
|
|
3
|
+
**Category:** JS/TS Puzzles
|
|
4
|
+
**Difficulty:** Medium
|
|
5
|
+
**Suggested Time:** ~30 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Implement a `debounce` function that delays invoking a function until after a specified `delay` (in milliseconds) has elapsed since the last time it was invoked. If the debounced function is called again before the delay expires, the timer resets.
|
|
12
|
+
|
|
13
|
+
Debouncing is commonly used for search inputs, resize handlers, scroll handlers, and other events that fire rapidly—ensuring the wrapped function only runs once after the user stops triggering it.
|
|
14
|
+
|
|
15
|
+
## Function Signature
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
function debounce(fn: (...args: any[]) => void, delay: number): (...args: any[]) => void
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **`fn`** — The function to debounce. It may accept any arguments and returns `void`.
|
|
22
|
+
- **`delay`** — The debounce delay in milliseconds.
|
|
23
|
+
- **Returns** — A new debounced function with the same signature.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Example 1: Basic delay
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const log = (msg: string) => console.log(msg);
|
|
31
|
+
const debouncedLog = debounce(log, 100);
|
|
32
|
+
|
|
33
|
+
debouncedLog('hello'); // nothing logged yet
|
|
34
|
+
// ... 100ms passes ...
|
|
35
|
+
// logs: "hello"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Example 2: Multiple rapid calls trigger only once
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const save = (data: string) => console.log('Saving:', data);
|
|
42
|
+
const debouncedSave = debounce(save, 200);
|
|
43
|
+
|
|
44
|
+
debouncedSave('a');
|
|
45
|
+
debouncedSave('b');
|
|
46
|
+
debouncedSave('c');
|
|
47
|
+
// ... 200ms passes ...
|
|
48
|
+
// logs: "Saving: c" (only the last call runs)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Example 3: Timer resets on new calls
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const fn = vi.fn();
|
|
55
|
+
const debounced = debounce(fn, 100);
|
|
56
|
+
|
|
57
|
+
debounced();
|
|
58
|
+
// advance 50ms
|
|
59
|
+
debounced();
|
|
60
|
+
// advance 50ms (total 100ms since first call, but only 50ms since last)
|
|
61
|
+
// fn not called yet
|
|
62
|
+
// advance 50ms more (100ms since last call)
|
|
63
|
+
// fn called once
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Constraints
|
|
67
|
+
|
|
68
|
+
- The debounced function should pass through all arguments to the original function.
|
|
69
|
+
- The debounced function should preserve the `this` context when the original function is invoked.
|
|
70
|
+
- With `delay === 0`, the function should still debounce (i.e., schedule for the next tick / microtask boundary, or run immediately on the next call—implementation-dependent but should not fire synchronously on every call).
|
|
71
|
+
- The debounced function returns `void`; you do not need to return the original function's return value.
|
|
72
|
+
|
|
73
|
+
## Expected Behavior
|
|
74
|
+
|
|
75
|
+
1. **Basic delay** — The wrapped function is invoked only after `delay` ms of no further calls.
|
|
76
|
+
2. **Rapid calls** — Multiple calls in quick succession result in a single invocation with the arguments from the last call.
|
|
77
|
+
3. **Timer reset** — Each new call resets the timer; the function runs only after `delay` ms of inactivity.
|
|
78
|
+
4. **Arguments** — All arguments passed to the debounced function are forwarded to the original function.
|
|
79
|
+
5. **`this` context** — When the debounced function is called as a method, the original function receives the correct `this`.
|
|
80
|
+
6. **Zero delay** — Behavior with `delay === 0` should still debounce (no synchronous double-invocation on rapid calls).
|
|
81
|
+
|
|
82
|
+
## Hints
|
|
83
|
+
|
|
84
|
+
- Use `setTimeout` to schedule the invocation.
|
|
85
|
+
- Store the timeout ID so you can clear it when a new call comes in.
|
|
86
|
+
- Use `apply` or spread to pass arguments and preserve `this`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { debounce } from './solution';
|
|
3
|
+
|
|
4
|
+
describe('debounce', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('delays invocation by the specified delay', () => {
|
|
14
|
+
const fn = vi.fn();
|
|
15
|
+
const debounced = debounce(fn, 100);
|
|
16
|
+
|
|
17
|
+
debounced();
|
|
18
|
+
expect(fn).not.toHaveBeenCalled();
|
|
19
|
+
|
|
20
|
+
vi.advanceTimersByTime(99);
|
|
21
|
+
expect(fn).not.toHaveBeenCalled();
|
|
22
|
+
|
|
23
|
+
vi.advanceTimersByTime(1);
|
|
24
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('invokes only once when called multiple times rapidly', () => {
|
|
28
|
+
const fn = vi.fn();
|
|
29
|
+
const debounced = debounce(fn, 100);
|
|
30
|
+
|
|
31
|
+
debounced();
|
|
32
|
+
debounced();
|
|
33
|
+
debounced();
|
|
34
|
+
|
|
35
|
+
vi.advanceTimersByTime(100);
|
|
36
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('passes through all arguments to the original function', () => {
|
|
40
|
+
const fn = vi.fn();
|
|
41
|
+
const debounced = debounce(fn, 50);
|
|
42
|
+
|
|
43
|
+
debounced('a', 1, { key: 'value' });
|
|
44
|
+
vi.advanceTimersByTime(50);
|
|
45
|
+
|
|
46
|
+
expect(fn).toHaveBeenCalledWith('a', 1, { key: 'value' });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('preserves this context when called as a method', () => {
|
|
50
|
+
const methodFn = vi.fn(function (this: { value: number }) {
|
|
51
|
+
return this.value;
|
|
52
|
+
});
|
|
53
|
+
const obj = {
|
|
54
|
+
value: 42,
|
|
55
|
+
method: methodFn,
|
|
56
|
+
};
|
|
57
|
+
obj.method = debounce(obj.method, 50);
|
|
58
|
+
|
|
59
|
+
obj.method();
|
|
60
|
+
vi.advanceTimersByTime(50);
|
|
61
|
+
|
|
62
|
+
expect(methodFn).toHaveBeenCalled();
|
|
63
|
+
expect(methodFn.mock.results[0]?.value).toBe(42);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('resets timer when called again before delay expires', () => {
|
|
67
|
+
const fn = vi.fn();
|
|
68
|
+
const debounced = debounce(fn, 100);
|
|
69
|
+
|
|
70
|
+
debounced();
|
|
71
|
+
vi.advanceTimersByTime(50);
|
|
72
|
+
debounced();
|
|
73
|
+
vi.advanceTimersByTime(50);
|
|
74
|
+
expect(fn).not.toHaveBeenCalled();
|
|
75
|
+
|
|
76
|
+
vi.advanceTimersByTime(50);
|
|
77
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('uses arguments from the last call when multiple rapid calls occur', () => {
|
|
81
|
+
const fn = vi.fn();
|
|
82
|
+
const debounced = debounce(fn, 100);
|
|
83
|
+
|
|
84
|
+
debounced('first');
|
|
85
|
+
debounced('second');
|
|
86
|
+
debounced('third');
|
|
87
|
+
|
|
88
|
+
vi.advanceTimersByTime(100);
|
|
89
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
90
|
+
expect(fn).toHaveBeenCalledWith('third');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('works with 0 delay (debounces to next tick)', () => {
|
|
94
|
+
const fn = vi.fn();
|
|
95
|
+
const debounced = debounce(fn, 0);
|
|
96
|
+
|
|
97
|
+
debounced();
|
|
98
|
+
debounced();
|
|
99
|
+
debounced();
|
|
100
|
+
|
|
101
|
+
vi.advanceTimersByTime(0);
|
|
102
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('returns void (does not return the original function result)', () => {
|
|
106
|
+
const fn = vi.fn(() => 'result');
|
|
107
|
+
const debounced = debounce(fn, 50);
|
|
108
|
+
|
|
109
|
+
const result = debounced();
|
|
110
|
+
expect(result).toBeUndefined();
|
|
111
|
+
|
|
112
|
+
vi.advanceTimersByTime(50);
|
|
113
|
+
expect(fn).toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('can be called again after the delay has fired', () => {
|
|
117
|
+
const fn = vi.fn();
|
|
118
|
+
const debounced = debounce(fn, 50);
|
|
119
|
+
|
|
120
|
+
debounced();
|
|
121
|
+
vi.advanceTimersByTime(50);
|
|
122
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
123
|
+
|
|
124
|
+
debounced();
|
|
125
|
+
vi.advanceTimersByTime(50);
|
|
126
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Two Sum
|
|
2
|
+
|
|
3
|
+
**Category:** LeetCode Algorithms
|
|
4
|
+
**Difficulty:** Easy
|
|
5
|
+
**Suggested Time:** ~15 minutes
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
Given an array of integers `nums` and an integer `target`, return the indices of the two numbers that add up to `target`.
|
|
12
|
+
|
|
13
|
+
You may assume that each input has **exactly one solution**, and you may not use the same element twice. You can return the answer in any order.
|
|
14
|
+
|
|
15
|
+
## Function Signature
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
function twoSum(nums: number[], target: number): [number, number]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **`nums`** — Array of integers.
|
|
22
|
+
- **`target`** — The target sum.
|
|
23
|
+
- **Returns** — A tuple `[i, j]` where `nums[i] + nums[j] === target` and `i !== j`.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Example 1
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
twoSum([2, 7, 11, 15], 9);
|
|
31
|
+
// returns [0, 1] because nums[0] + nums[1] = 2 + 7 = 9
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Example 2
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
twoSum([3, 2, 4], 6);
|
|
38
|
+
// returns [1, 2] because nums[1] + nums[2] = 2 + 4 = 6
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Example 3
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
twoSum([3, 3], 6);
|
|
45
|
+
// returns [0, 1]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Constraints
|
|
49
|
+
|
|
50
|
+
- `2 <= nums.length <= 10^4`
|
|
51
|
+
- `-10^9 <= nums[i] <= 10^9`
|
|
52
|
+
- `-10^9 <= target <= 10^9`
|
|
53
|
+
- Only one valid answer exists.
|
|
54
|
+
|
|
55
|
+
## Hints
|
|
56
|
+
|
|
57
|
+
- A brute-force approach checks every pair in O(n²).
|
|
58
|
+
- A hash map can store "complement" (target - current) for O(n) time and O(n) space.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"title":"Two Sum","category":"leetcode-algo","difficulty":"easy","suggestedTime":15,"status":"untouched","attempts":[],"llmFeedback":null}
|