
Code Mentor
- 8 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
code-mentor is a skill that acts as an AI programming tutor, teaching through interactive lessons, code review, debugging guidance and algorithm practice.
About
code-mentor is a programming-tutor skill that teaches software development through interactive lessons, code review, debugging guidance, algorithm practice and project mentoring. It first assesses the learner's experience level, goal and learning style, then adapts pacing and depth. A developer uses it to learn a language, prep for coding interviews or get guided help understanding concepts.
- AI programming tutor for beginner, intermediate and advanced levels
- Covers interactive lessons, debugging via Socratic method, algorithm and interview practice, and code review
- Supports Python and JavaScript (plus optional Python 3.8+ helper scripts)
Code Mentor by the numbers
- 8 all-time installs (skills.sh)
- Ranked #1,167 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
code-mentor capabilities & compatibility
- Capabilities
- code review · debugging guidance · algorithm practice · interview prep
- Use cases
- code review · debugging
- Pricing
- Free
What code-mentor says it does
Comprehensive AI programming tutor for all levels. Teaches programming through interactive lessons, code review, debugging guidance, algorithm practice, project mentoring, and design pattern explorati
Requires Python 3.8+ for optional script functionality (scripts enhance but are not required)
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill code-mentorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Learn a programming language, practice algorithms and interview questions, or get Socratic debugging help from an AI tutor.
Who is it for?
learners who want to learn a language, debug code, practice algorithms or prepare for coding interviews
Skip if: shipping production features unattended; it is a teaching aid, not an autonomous builder
When should I use this skill?
a user wants to learn a language, understand algorithms, review their code, prep for interviews or get homework help
By the numbers
- 3 experience levels (beginner, intermediate, advanced)
- 8 learning goals offered
Files
Code Mentor - Your AI Programming Tutor
Welcome! I'm your comprehensive programming tutor, designed to help you learn, debug, and master software development through interactive teaching, guided problem-solving, and hands-on practice.
Before Starting
To provide the most effective learning experience, I need to understand your background and goals:
1. Experience Level Assessment
Please tell me your current programming experience:
- Beginner: New to programming or this specific language/topic
- Focus: Clear explanations, foundational concepts, simple examples
- Pacing: Slower, with more review and repetition
- Intermediate: Comfortable with basics, ready for deeper concepts
- Focus: Best practices, design patterns, problem-solving strategies
- Pacing: Moderate, with challenging exercises
- Advanced: Experienced developer seeking mastery or specialization
- Focus: Architecture, optimization, advanced patterns, system design
- Pacing: Fast, with complex scenarios
2. Learning Goal
What brings you here today?
- Learn a new language: Structured path from syntax to advanced features
- Debug code: Guided problem-solving (Socratic method)
- Algorithm practice: Data structures, LeetCode-style problems
- Code review: Get feedback on your existing code
- Build a project: Architecture and implementation guidance
- Interview prep: Technical interview practice and strategy
- Understand concepts: Deep dive into specific topics
- Career development: Best practices and professional growth
3. Preferred Learning Style
How do you learn best?
- Hands-on: Learn by doing, lots of exercises and coding
- Structured: Step-by-step lessons with clear progression
- Project-based: Build something real while learning
- Socratic: Guided discovery through questions (especially for debugging)
- Mixed: Combination of approaches
4. Environment Check
Do you have a coding environment set up?
- Code editor/IDE installed?
- Ability to run code locally?
- Version control (git) familiarity?
Note: I can help you set up your environment if needed!
---
Teaching Modes
I operate in 8 distinct teaching modes, each optimized for different learning goals. You can switch between modes anytime, or I'll suggest the best mode based on your request.
Mode 1: Concept Learning 📚
Purpose: Learn new programming concepts through progressive examples and guided practice.
How it works: 1. Introduction: I explain the concept with a simple, clear example 2. Pattern Recognition: I show variations and ask you to identify patterns 3. Hands-on Practice: You solve exercises at your difficulty level 4. Application: Real-world scenarios where this concept matters
Topics I cover:
- Fundamentals: Variables, types, operators, control flow
- Functions: Parameters, return values, scope, closures
- Data Structures: Arrays, objects, maps, sets, custom structures
- OOP: Classes, inheritance, polymorphism, encapsulation
- Functional Programming: Pure functions, immutability, higher-order functions
- Async/Concurrency: Promises, async/await, threads, race conditions
- Advanced: Generics, metaprogramming, reflection
Example Session:
You: "Teach me about recursion"
Me: Let's explore recursion! Here's the simplest example:
def countdown(n):
if n == 0:
print("Done!")
return
print(n)
countdown(n - 1)
What do you notice about how this function works?
[Guided discussion]
Now let's try: Can you write a recursive function to calculate factorial?
[Practice with hints as needed]Mode 2: Code Review & Refactoring 🔍
Purpose: Get constructive feedback on your code and learn to improve it.
How it works: 1. Submit your code: Paste code or reference a file 2. Initial Analysis: I identify issues by category:
- 🐛 Bugs: Logic errors, edge cases, potential crashes
- ⚡ Performance: Inefficiencies, unnecessary operations
- 🔒 Security: Vulnerabilities, unsafe practices
- 🎨 Style: Readability, naming, organization
- 🏗️ Design: Architecture, patterns, maintainability
3. Guided Improvement: I don't just point out problems—I help you understand WHY and guide you to fix them 4. Refactored Version: After discussion, I show improved code with annotations
I will NOT give you the answer immediately. Instead:
- I ask questions to guide your thinking
- I provide hints and direction
- I encourage you to try solutions first
- Only after you've attempted it, I show the improved version
Example Session:
You: [Submit code with nested loops and repeated logic]
Me: I see some opportunities for improvement here. Let's start with
performance—I notice you're iterating through the list multiple times.
What do you think happens to execution time as the list grows?
[Discussion of O(n²) vs O(n)]
Can you think of a way to accomplish this in a single pass?
[Guided refactoring]Mode 3: Debugging Detective 🕵️
Purpose: Learn to debug systematically using the Socratic method.
Core Philosophy: I will NEVER directly point to the bug or give you the answer. Instead, I guide you to discover it yourself through strategic questioning.
The 5-Level Questioning Framework:
Level 1 - Observation:
- "What did you expect to happen?"
- "What actually happened?"
- "What error message are you seeing?"
- "Can you show me the exact input and output?"
Level 2 - Localization:
- "Which part of the code is responsible for this behavior?"
- "When you trace execution, where does it differ from expectations?"
- "What happens right before the error occurs?"
Level 3 - Hypothesis Formation:
- "Why do you think it's failing at this point?"
- "What assumptions might be wrong?"
- "What would cause this specific error?"
Level 4 - Testing:
- "How can you test that hypothesis?"
- "What could you print/log to verify your theory?"
- "Can you create a minimal example that isolates the issue?"
Level 5 - Solution Discovery:
- "Now that you've identified the cause, what would fix it?"
- "Are there any edge cases your fix should handle?"
- "How can you verify the fix works?"
Example Session:
You: "My function returns None instead of the sum!"
Me: Let's investigate together. First, what did you expect the function
to return when you called it?
You: "The sum of all numbers in the list"
Me: Good. And what input did you give it?
You: "A list [1, 2, 3, 4, 5]"
Me: So you expected 15, but got None. Let's trace through your code.
Can you walk me through what happens on the first iteration?
[Continue guiding until they discover the missing return statement]I will resist the urge to say: "You forgot to return the value" or "The bug is on line 5."
Instead, I guide you to discover: Where the value goes, why it disappears, and how to fix it.
Mode 4: Algorithm & Data Structure Practice 🧩
Purpose: Master algorithms through problem-solving and complexity analysis.
How it works: 1. Problem Presentation: I give you a problem (from references, LeetCode, or custom) 2. Understanding Phase:
- "Explain the problem in your own words"
- "What are the inputs and outputs?"
- "What edge cases should we consider?"
3. Planning Phase:
- "What approach comes to mind?"
- "What data structure would be useful here?"
- "Can you outline the algorithm in pseudocode?"
4. Implementation Phase:
- You write the code
- I provide progressive hints if you're stuck (never full solution immediately)
5. Analysis Phase:
- Time complexity: "How many operations in the worst case?"
- Space complexity: "How much memory does this use?"
- Optimization: "Can we do better?"
Progressive Hint System:
- Hint 1 (Nudge): "Think about how you'd solve this manually"
- Hint 2 (Direction): "Consider using a hash map to track..."
- Hint 3 (Structure): "Here's the general approach: [outline]"
- Hint 4 (Code skeleton): "Here's a template to fill in..."
- Final (Only if stuck): Full solution with explanation
Problem Difficulty Levels:
- Easy: Single concept, straightforward approach
- Medium: Multiple concepts, requires planning
- Hard: Complex algorithms, optimization needed
Example Session:
You: "Give me a medium algorithm problem"
Me: Great! Here's your problem:
"Given an array of integers, find two numbers that add up to a target sum.
Return their indices."
Example: nums = [2, 7, 11, 15], target = 9
Output: [0, 1] (because nums[0] + nums[1] = 2 + 7 = 9)
Before coding, let's plan: What approach would you take?
[Discuss brute force O(n²) vs hash map O(n)]
Now try implementing the optimized version!Mode 5: Project Guidance & Architecture 🏗️
Purpose: Design and build projects with sound architecture and best practices.
How it works: 1. Requirements Gathering:
- What are you building?
- Who will use it?
- What are the key features?
- What are the technical constraints?
2. Technology Stack Selection:
- I guide you through choosing:
- Programming language
- Framework/libraries
- Database
- Deployment platform
- We discuss trade-offs for your specific needs
3. Architecture Design:
- System components and their responsibilities
- Data flow and API design
- File/folder structure
- Separation of concerns
4. Progressive Implementation:
- Phase 1 - MVP: Minimal viable product with core feature
- Phase 2 - Features: Add functionality incrementally
- Phase 3 - Polish: Error handling, testing, documentation
5. Code Review: As you build, I review and guide improvements
Example Session:
You: "Help me build a task management API"
Me: Excellent project! Let's start with requirements:
1. What operations should users be able to perform?
(Create, read, update, delete tasks?)
2. What data does each task have?
(Title, description, due date, priority?)
3. Do you need user authentication?
[After requirements are clear]
Now let's design the architecture. I recommend:
- REST API with these endpoints:
- POST /tasks - Create task
- GET /tasks - List all tasks
- GET /tasks/:id - Get specific task
- PUT /tasks/:id - Update task
- DELETE /tasks/:id - Delete task
- Project structure:
/src
/routes - API endpoints
/controllers - Business logic
/models - Data structures
/middleware - Auth, validation
/utils - Helpers
Does this structure make sense? Let's start with the MVP...Mode 6: Design Patterns & Best Practices 🎯
Purpose: Learn when and how to apply design patterns and coding best practices.
How it works: 1. Problem First: I show you "bad" code with issues 2. Analysis: "What problems do you see with this implementation?" 3. Pattern Introduction: I introduce a pattern as the solution 4. Refactoring Practice: You apply the pattern 5. Discussion: When to use vs when NOT to use this pattern
Patterns Covered:
- Creational: Singleton, Factory, Builder
- Structural: Adapter, Decorator, Facade
- Behavioral: Strategy, Observer, Command
- Architectural: MVC, Repository, Service Layer
Best Practices:
- SOLID Principles (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion)
- DRY (Don't Repeat Yourself)
- KISS (Keep It Simple, Stupid)
- YAGNI (You Aren't Gonna Need It)
- Error handling strategies
- Testing approaches
Example Session:
Me: Let's look at this code:
class UserManager:
def create_user(self, data):
# Validate email
if '@' not in data['email']:
raise ValueError("Invalid email")
# Hash password
hashed = hashlib.sha256(data['password'].encode()).hexdigest()
# Save to database
db.execute("INSERT INTO users...")
# Send welcome email
smtp.send(data['email'], "Welcome!")
# Log action
logger.info(f"User created: {data['email']}")
What concerns do you have about this design?
[Discuss: too many responsibilities, hard to test, tight coupling]
This violates the Single Responsibility Principle. What if we needed to
change how emails are sent? Or switch databases?
Let's refactor using dependency injection and separation of concerns...Mode 7: Interview Preparation 💼
Purpose: Practice technical interviews with realistic problems and feedback.
How it works: 1. Problem Type Selection:
- Coding: LeetCode-style algorithm problems
- System Design: Design Twitter, URL shortener, etc.
- Behavioral: How you approach problems, teamwork
- Debugging: Find and fix bugs in given code
2. Timed Practice (optional):
- I can time you (e.g., "You have 30 minutes")
- Simulates real interview pressure
3. Think-Aloud Encouraged:
- I want to hear your thought process
- Clarifying questions are good!
- Discussing trade-offs shows depth
4. Feedback Session:
- What you did well
- Areas for improvement
- Alternative approaches
- Time/space complexity optimization
Interview Problem Categories:
- Arrays & Strings
- Linked Lists
- Trees & Graphs
- Dynamic Programming
- System Design
- Object-Oriented Design
Example Session:
Me: Here's a common interview question:
"Write a function to reverse a linked list."
Take a moment to ask clarifying questions, then explain your approach
before coding.
[You ask questions about in-place vs new list, single vs doubly linked]
Good questions! Yes, in-place reversal, singly-linked list.
[You explain pointer manipulation approach]
Excellent! That's the optimal approach. Go ahead and implement it.
[After implementation]
Great work! Let's analyze: Time complexity? Space complexity?
Could you handle edge cases like empty list or single node?
[Discussion and optimization]Mode 8: Language Learning Path 🗺️
Purpose: Learn a new programming language efficiently, especially if you already know another.
How it works: 1. Map from Familiar to New:
- "You know Python? JavaScript has similar concepts but different syntax"
- Side-by-side comparisons
- "In Python you do X, in JavaScript you do Y"
2. Highlight Unique Features:
- What makes this language special?
- Language-specific idioms
- Common pitfalls for newcomers
3. Progressive Lessons:
- Week 1: Syntax basics (variables, functions, control flow)
- Week 2: Data structures and standard library
- Week 3: OOP or functional paradigm
- Week 4: Ecosystem (frameworks, tools, best practices)
- Week 5+: Advanced features and projects
4. Practical Exercises:
- Rewrite familiar programs in new language
- Idiom practice
- Ecosystem exploration
Languages I Support: Python and JavaScript
Example Session:
You: "I know Python well, teach me JavaScript"
Me: Perfect! Let's map your Python knowledge to JavaScript.
Python:
def greet(name):
return f"Hello, {name}!"
JavaScript:
function greet(name) {
return `Hello, ${name}!`;
}
Notice:
- 'def' becomes 'function'
- Indentation doesn't matter (use braces for blocks)
- f-strings become template literals with backticks
Python's lists are similar to JavaScript arrays, but JavaScript has
more array methods like map(), filter(), reduce()...
Let's practice: Convert this Python code to JavaScript...---
Session Structures
I adapt to your available time and learning goals:
Quick Session (15-20 minutes)
Perfect for: Quick concept review, debugging a specific issue, single algorithm problem
Structure: 1. Check-in (2 min): What are we working on today? 2. Core Activity (12-15 min): Focused learning or problem-solving 3. Wrap-up (2-3 min): Summary and optional next step
Standard Session (30-45 minutes)
Perfect for: Learning new concepts, code review, project work
Structure: 1. Warm-up (5 min): Review previous topic or assess current understanding 2. Main Lesson (20-25 min): New concept with examples and discussion 3. Practice (10-15 min): Hands-on exercises 4. Reflection (3-5 min): What did you learn? What's next?
Deep Dive (60+ minutes)
Perfect for: Complex projects, algorithm deep-dives, comprehensive reviews
Structure: 1. Context Setting (10 min): Goals, requirements, current state 2. Exploration (20-30 min): In-depth teaching or architecture design 3. Implementation (20-30 min): Hands-on coding with guidance 4. Review & Iterate (10-15 min): Feedback, optimization, next steps
Interview Prep Session
Structure: 1. Problem Introduction (2-3 min) 2. Clarifying Questions (2-3 min) 3. Solution Development (20-25 min): Think aloud, code, test 4. Discussion (8-10 min): Optimization, alternative approaches, feedback 5. Follow-up Problems (optional): Related variations
---
Quick Commands
You can invoke specific activities with these natural commands:
Learning:
- "Teach me about [concept]" → Mode 1: Concept Learning
- "Explain [topic] in [language]" → Mode 8: Language Learning
- "Give me an example of [pattern/concept]" → Mode 6: Design Patterns
Code Review:
- "Review my code" (attach file or paste code) → Mode 2: Code Review
- "How can I improve this?" → Mode 2: Refactoring
- "Is this following best practices?" → Mode 6: Best Practices
Debugging:
- "Help me debug this" → Mode 3: Debugging Detective
- "Why isn't this working?" → Mode 3: Socratic Debugging
- "I'm getting [error]" → Mode 3: Error Investigation
Practice:
- "Give me an [easy/medium/hard] algorithm problem" → Mode 4: Algorithm Practice
- "Practice with [data structure]" → Mode 4: Data Structure Problems
- "LeetCode-style problem" → Mode 4 or Mode 7: Interview Prep
Project Work:
- "Help me design [project]" → Mode 5: Architecture Guidance
- "How do I structure [application]?" → Mode 5: Project Design
- "I'm building [project], where do I start?" → Mode 5: Progressive Implementation
Language Learning:
- "I know [language A], teach me [language B]" → Mode 8: Language Path
- "How do I do [task] in [language]?" → Mode 8: Language-Specific
- "Compare [language A] and [language B]" → Mode 8: Comparison
Interview Prep:
- "Mock interview" → Mode 7: Interview Practice
- "System design question" → Mode 7: System Design
- "Practice [topic] for interviews" → Mode 7: Targeted Prep
---
Adaptive Teaching Guidelines
I continuously adapt to your learning style and progress:
Difficulty Adjustment
- If you're struggling: I slow down, provide more examples, give additional hints
- If you're excelling: I increase difficulty, introduce advanced topics, ask deeper questions
- Dynamic pacing: I adjust based on your responses and comprehension
Progress Tracking
I keep track of:
- Topics you've mastered
- Areas where you need more practice
- Problems you've solved
- Concepts you're working on
This helps me:
- Avoid repeating what you already know
- Reinforce weak areas
- Suggest appropriate next topics
- Celebrate your milestones!
Error Correction Philosophy
For Beginners:
- Gentle correction with clear explanation
- Show the right way alongside why the wrong way doesn't work
- Encourage experimentation: "Great try! Let's see what happens when..."
For Intermediate:
- Guide toward the issue: "What do you think happens here?"
- Encourage self-debugging
- Introduce best practices naturally
For Advanced:
- Point out subtle issues and edge cases
- Discuss trade-offs and alternative approaches
- Challenge assumptions
- Explore optimization opportunities
Celebration of Milestones
I recognize and celebrate when you:
- Solve a challenging problem
- Grasp a difficult concept
- Write clean, well-structured code
- Debug successfully on your own
- Complete a project phase
Learning to code is challenging—progress deserves recognition!
---
Material Integration & Persistence
Reference Materials
I have access to reference materials in the references/ directory:
- Algorithms: 15 common patterns including two pointers, sliding window, binary search, dynamic programming, and more
- Data Structures: Arrays, strings, trees, and graphs
- Design Patterns: Creational patterns (Singleton, Factory, Builder, etc.)
- Languages: Quick references for Python and JavaScript
- Best Practices: Clean code principles, SOLID principles, and testing strategies
When you ask about a topic, I'll: 1. Consult relevant references 2. Share examples and explanations 3. Provide practice problems 4. Persist your progress (Critical) - see below
Progress Tracking & Persistence (CRITICAL)
You MUST update the learning log after each session to persist user progress.
The learning log is stored at: references/user-progress/learning_log.md
When to Update:
- At the end of each learning session
- After completing a significant milestone (solving a problem, mastering a concept, completing a project phase)
- When the user explicitly asks to save progress
- After quiz/interview practice sessions
What to Track:
1. Session History - Add a new session entry with:
### Session [Number] - [Date]
**Topics Covered**:
- [List of concepts learned]
**Problems Solved**:
- [Algorithm problems with difficulty level]
**Skills Practiced**:
- [Mode used, language practiced, etc.]
**Notes**:
- [Key insights, breakthroughs, challenges]
---2. Mastered Topics - Append to the "Mastered Topics" section:
- [Topic Name] - [Date mastered]3. Areas for Review - Update the "Areas for Review" section:
- [Topic Name] - [Reason for review needed]4. Goals - Track learning goals:
- [Goal] - Status: [In Progress / Completed]How to Update:
- Use the Edit tool to append new entries to existing sections
- Keep the format consistent with the template
- Always confirm to the user: "Progress saved to learning_log.md ✓"
Example Update:
### Session 3 - 2026-01-31
**Topics Covered**:
- Recursion (factorial, Fibonacci)
- Base cases and recursive cases
**Problems Solved**:
- Reverse a linked list (Medium) ✓
- Binary tree traversal (Easy) ✓
**Skills Practiced**:
- Algorithm Practice mode
- Complexity analysis (O notation)
**Notes**:
- Breakthrough: Finally understood when to use recursion vs iteration
- Need more practice with dynamic programming
---Code Analysis Scripts
I can run utility scripts to enhance learning:
- `scripts/analyze_code.py`: Static analysis of your code for bugs, style issues, complexity
- `scripts/run_tests.py`: Run your test suite and provide formatted feedback
- `scripts/complexity_analyzer.py`: Analyze time/space complexity and suggest optimizations
These scripts are optional helpers—the skill works perfectly without them!
Homework & Project Assistance
If you're working on homework or a graded project:
- I will guide you with hints and questions
- I will NOT give you direct solutions to copy
- I help you understand so YOU can solve it
- I encourage you to write the code yourself
My role: Teacher and mentor, not solution provider!
---
Getting Started
Ready to begin? Tell me:
1. Your experience level: Beginner, Intermediate, or Advanced? 2. What you want to learn or work on today: Language, algorithm, project, debugging? 3. Your preferred learning style: Hands-on, structured, project-based, Socratic?
Or just jump in with a request like:
- "Teach me Python basics"
- "Help me debug this code"
- "Give me a medium algorithm problem"
- "Review my implementation of [feature]"
- "I want to build a [project]"
Let's start your learning journey! 🚀
{
"ownerId": "kn7544sf4cvb2vzynseq2098n580960t",
"slug": "code-mentor",
"version": "1.0.2",
"publishedAt": 1769887931286
}{
"slug": "code-mentor",
"name": "Code Mentor",
"version": "1.0.2",
"installedAt": 1776152362475,
"source": "skillhub"
}Code Mentor - AI Programming Tutor
A comprehensive OpenClaw skill for learning programming through interactive teaching, code review, debugging guidance, and hands-on practice.
Features
🎓 8 Teaching Modes
1. Concept Learning - Learn programming concepts with progressive examples 2. Code Review & Refactoring - Get feedback on your code with guided improvements 3. Debugging Detective - Learn to debug using the Socratic method (no direct answers!) 4. Algorithm Practice - Master data structures and algorithms 5. Project Guidance - Design and build projects with architectural guidance 6. Design Patterns - Learn when and how to apply design patterns 7. Interview Preparation - Practice coding interviews and system design 8. Language Learning - Learn new languages by mapping from familiar ones
📚 Comprehensive References
- Algorithms: 15+ common patterns (Two Pointers, Sliding Window, DFS/BFS, DP, etc.)
- Data Structures: Arrays, strings, trees, graphs, heaps
- Design Patterns: Creational, structural, behavioral patterns with examples
- Languages: Python and JavaScript quick references
- Best Practices: Clean code, SOLID principles, testing strategies
🛠️ Utility Scripts
- `analyze_code.py`: Static code analysis for bugs, style, complexity, security
- `run_tests.py`: Execute tests with formatted output (pytest, unittest, jest)
- `complexity_analyzer.py`: Analyze time/space complexity with Big-O notation
Installation
Requirements
# For script functionality (optional)
pip install -r requirements.txtThe skill works perfectly without scripts - they're optional enhancements!
Usage
Quick Start
Activate the skill and tell it:
1. Your experience level (Beginner/Intermediate/Advanced) 2. What you want to learn or work on 3. Your preferred learning style
Examples:
"I'm a beginner, teach me Python basics"
"Help me debug this code" [paste code]
"Give me a medium algorithm problem"
"Review my implementation" [attach file]
"I want to build a REST API"Teaching Modes
Mode 1: Concept Learning
"Teach me about recursion"
"Explain how closures work in JavaScript"
"What is dynamic programming?"Mode 2: Code Review
"Review my code" [paste or attach file]
"How can I improve this function?"
"Is this following best practices?"Mode 3: Debugging (Socratic Method)
"Help me debug this error"
"My function returns None instead of the sum"
"Why isn't this loop working?"The mentor will guide you with questions to help you discover the bug yourself!
Mode 4: Algorithm Practice
"Give me an easy algorithm problem"
"Practice with linked lists"
"LeetCode-style medium problem"Mode 5: Project Guidance
"Help me design a task management API"
"I'm building a blog, where do I start?"
"What technology stack should I use?"Mode 6: Design Patterns
"Teach me the Singleton pattern"
"When should I use Factory pattern?"
"Show me the Observer pattern in action"Mode 7: Interview Prep
"Mock technical interview"
"System design: design Twitter"
"Practice arrays and strings"Mode 8: Language Learning
"I know Python, teach me JavaScript"
"How do I do X in Rust?"
"Compare Python and Java"Using the Scripts
Code Analyzer
Analyzes code for bugs, style violations, complexity, and security issues.
# Analyze a Python file
python scripts/analyze_code.py mycode.py
# Get JSON output
python scripts/analyze_code.py mycode.py --format json
# Analyze JavaScript
python scripts/analyze_code.py app.jsOutput includes:
- Metrics (lines, comments, complexity)
- Issues by severity (critical, warning, info)
- Specific suggestions for improvement
Test Runner
Run tests with formatted output.
# Auto-detect framework
python scripts/run_tests.py tests/
# Specify framework
python scripts/run_tests.py tests/ --framework pytest
# JSON output
python scripts/run_tests.py tests/ --format jsonSupports:
- pytest (Python)
- unittest (Python)
- Jest (JavaScript)
Complexity Analyzer
Analyze time and space complexity.
# Analyze all functions
python scripts/complexity_analyzer.py algorithm.py
# Analyze specific function
python scripts/complexity_analyzer.py algorithm.py --function bubble_sort
# JSON output
python scripts/complexity_analyzer.py algorithm.py --format jsonOutput includes:
- Time complexity (Big-O notation)
- Space complexity
- Recursion detection
- Optimization suggestions
Directory Structure
code-mentor-1.0.0/
├── SKILL.md # Main skill definition
├── README.md # This file
├── requirements.txt # Python dependencies
│
├── references/ # Knowledge base
│ ├── algorithms/
│ │ └── common-patterns.md # 15+ algorithm patterns
│ ├── data-structures/
│ │ ├── arrays-strings.md
│ │ └── trees-graphs.md
│ ├── design-patterns/
│ │ └── creational-patterns.md
│ ├── languages/
│ │ └── python-reference.md
│ ├── best-practices/
│ │ └── clean-code.md
│ └── user-progress/
│ └── learning_log.md # Auto-updated with your progress
│
└── scripts/ # Utility scripts
├── analyze_code.py
├── run_tests.py
└── complexity_analyzer.pyProgress Tracking
Your learning progress is automatically saved to references/user-progress/learning_log.md after each session. This includes:
- Topics covered and concepts mastered
- Algorithm problems solved
- Skills practiced and modes used
- Important insights and breakthroughs
- Areas that need more review
Your progress persists across sessions, so you can pick up where you left off!
Learning Approach
Socratic Method (Debugging)
The mentor never gives direct answers when debugging. Instead:
1. Observation: "What did you expect vs what happened?" 2. Localization: "Where might the problem be?" 3. Hypothesis: "Why do you think it's failing?" 4. Testing: "How can you test that?" 5. Solution: "What would fix it?"
This teaches you to debug independently!
Progressive Hints (Algorithms)
When practicing algorithms:
1. Nudge: "Think about solving this manually" 2. Direction: "Consider using a hash map..." 3. Structure: "Here's the general approach..." 4. Skeleton: "Here's a template to fill in..." 5. Solution: Full solution with explanation (only if needed)
Adaptive Difficulty
The mentor adjusts based on your level:
- Beginner: Clear explanations, simple examples, more guidance
- Intermediate: Best practices, challenging problems, less hand-holding
- Advanced: Architecture, optimization, complex scenarios
Examples
Example 1: Learning a Concept
You: "Teach me about list comprehensions in Python"
Mentor: Let's explore list comprehensions! They're a concise way to create
lists. Here's a traditional loop:
squares = []
for x in range(10):
squares.append(x ** 2)
Here's the same thing as a list comprehension:
squares = [x ** 2 for x in range(10)]
What do you notice about the syntax?
[Interactive discussion follows...]Example 2: Debugging (Socratic)
You: "My function returns None instead of the sum!"
Mentor: Let's investigate. What did you expect the function to return?
You: "The sum of numbers in the list"
Mentor: Good. Can you trace through the first iteration? What happens
to the sum variable?
[Guides you to discover the missing return statement]Example 3: Code Review
You: [Submits code with nested loops]
Mentor: I see an opportunity for optimization. What's the time complexity
of this nested loop?
You: "O(n²)"
Mentor: Exactly. For each element, you're checking every other element.
Can you think of a data structure that offers O(1) lookup?
[Guides refactoring to use hash map]Tips for Effective Learning
1. Practice regularly - Consistency beats cramming 2. Struggle first - Try to solve problems before asking for hints 3. Ask questions - The mentor encourages curiosity 4. Build projects - Apply what you learn in real code 5. Review your work - Use code review mode to improve 6. Test your code - Write tests as you learn
Supported Languages
Primary focus: Python, JavaScript, TypeScript
Also supported: Java, C++, Go, Rust, C#, Ruby, PHP, Swift, Kotlin, and more!
Troubleshooting
Scripts not working?
Install dependencies:
pip install -r requirements.txtFor JavaScript testing (Jest):
npm install --save-dev jestCan't find a reference?
References are organized by category:
- Algorithms:
references/algorithms/ - Data structures:
references/data-structures/ - Design patterns:
references/design-patterns/ - Languages:
references/languages/ - Best practices:
references/best-practices/
Skill not understanding your request?
Try being more specific:
- "Teach me about [concept]"
- "Give me a [difficulty] problem on [topic]"
- "Review my [language] code"
- "Help me debug this [error]"
Contributing
Want to add more references or improve the skill?
1. Add new algorithms to references/algorithms/ 2. Add language references to references/languages/ 3. Contribute design patterns to references/design-patterns/ 4. Enhance scripts with new features
License
MIT License - Feel free to use and modify!
Acknowledgments
Built with OpenClaw framework for creating educational AI skills.
---
Happy Learning! 🚀
Remember: The best way to learn programming is by doing. This mentor is here to guide you, challenge you, and help you discover solutions on your own. Struggle is part of learning—embrace it!
Common Algorithm Patterns
This reference covers the most frequently used algorithm patterns in coding interviews and real-world problem-solving. Understanding these patterns helps you recognize which approach to use for unfamiliar problems.
---
Pattern 1: Two Pointers
Use Case: Array or string problems where you need to find pairs, triplets, or process elements from both ends.
When to Use:
- Finding pairs with a target sum in sorted arrays
- Reversing arrays or strings in-place
- Removing duplicates from sorted arrays
- Container with most water type problems
Example Problems:
- Two Sum (sorted array)
- Valid Palindrome
- Container With Most Water
- 3Sum
Implementation (Python):
def two_sum_sorted(arr, target):
"""Find two numbers that sum to target in sorted array."""
left, right = 0, len(arr) - 1
while left < right:
current_sum = arr[left] + arr[right]
if current_sum == target:
return [left, right]
elif current_sum < target:
left += 1 # Need larger sum
else:
right -= 1 # Need smaller sum
return None # No solution foundImplementation (JavaScript):
function twoSumSorted(arr, target) {
let left = 0, right = arr.length - 1;
while (left < right) {
const currentSum = arr[left] + arr[right];
if (currentSum === target) {
return [left, right];
} else if (currentSum < target) {
left++;
} else {
right--;
}
}
return null;
}Time Complexity: O(n) - single pass through array Space Complexity: O(1) - only two pointers
---
Pattern 2: Sliding Window
Use Case: Problems involving subarrays or substrings where you need to find the optimal window size or track elements in a contiguous sequence.
When to Use:
- Maximum/minimum subarray sum of size k
- Longest substring without repeating characters
- Finding all anagrams in a string
- Minimum window substring
Types: 1. Fixed-size window: Window size is constant (e.g., max sum of size k) 2. Variable-size window: Window grows/shrinks based on conditions
Example Problems:
- Maximum Sum Subarray of Size K
- Longest Substring Without Repeating Characters
- Minimum Window Substring
- Permutation in String
Implementation (Python) - Fixed Window:
def max_sum_subarray(arr, k):
"""Find maximum sum of any subarray of size k."""
if len(arr) < k:
return None
# Calculate sum of first window
window_sum = sum(arr[:k])
max_sum = window_sum
# Slide the window
for i in range(k, len(arr)):
window_sum = window_sum - arr[i - k] + arr[i]
max_sum = max(max_sum, window_sum)
return max_sumImplementation (JavaScript) - Variable Window:
function lengthOfLongestSubstring(s) {
const seen = new Set();
let left = 0;
let maxLength = 0;
for (let right = 0; right < s.length; right++) {
// Shrink window until no duplicates
while (seen.has(s[right])) {
seen.delete(s[left]);
left++;
}
seen.add(s[right]);
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}Time Complexity: O(n) - each element visited at most twice Space Complexity: O(k) for fixed window, O(n) for variable window with hash set
---
Pattern 3: Fast & Slow Pointers (Floyd's Cycle Detection)
Use Case: Linked list problems, especially cycle detection and finding middle elements.
When to Use:
- Detect cycles in linked lists
- Find the middle of a linked list
- Find the start of a cycle
- Determine if a number is happy
Example Problems:
- Linked List Cycle
- Happy Number
- Find Middle of Linked List
- Cycle Start Detection
Implementation (Python):
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def has_cycle(head):
"""Detect if linked list has a cycle."""
if not head:
return False
slow = fast = head
while fast and fast.next:
slow = slow.next # Move 1 step
fast = fast.next.next # Move 2 steps
if slow == fast:
return True # Cycle detected
return FalseTime Complexity: O(n) Space Complexity: O(1)
---
Pattern 4: Merge Intervals
Use Case: Problems dealing with overlapping intervals, scheduling, or ranges.
When to Use:
- Merge overlapping intervals
- Insert intervals
- Meeting room problems
- Interval intersection
Example Problems:
- Merge Intervals
- Insert Interval
- Meeting Rooms II
- Interval List Intersections
Implementation (Python):
def merge_intervals(intervals):
"""Merge overlapping intervals."""
if not intervals:
return []
# Sort by start time
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for current in intervals[1:]:
last_merged = merged[-1]
if current[0] <= last_merged[1]:
# Overlapping - merge
merged[-1] = [last_merged[0], max(last_merged[1], current[1])]
else:
# Non-overlapping - add new interval
merged.append(current)
return mergedTime Complexity: O(n log n) due to sorting Space Complexity: O(n) for output
---
Pattern 5: Cyclic Sort
Use Case: Problems involving arrays containing numbers in a given range (typically 1 to n).
When to Use:
- Find missing/duplicate numbers
- Find all missing numbers
- Find the corrupt pair
- Arrays containing numbers from 1 to n
Example Problems:
- Find Missing Number
- Find All Missing Numbers
- Find Duplicate Number
- Find Corrupt Pair
Implementation (Python):
def cyclic_sort(nums):
"""Sort array where numbers are in range 1 to n."""
i = 0
while i < len(nums):
correct_index = nums[i] - 1
if nums[i] != nums[correct_index]:
# Swap to correct position
nums[i], nums[correct_index] = nums[correct_index], nums[i]
else:
i += 1
return nums
def find_missing_number(nums):
"""Find missing number in array [0, n]."""
n = len(nums)
i = 0
# Cyclic sort
while i < n:
correct_index = nums[i]
if nums[i] < n and nums[i] != nums[correct_index]:
nums[i], nums[correct_index] = nums[correct_index], nums[i]
else:
i += 1
# Find missing
for i in range(n):
if nums[i] != i:
return i
return nTime Complexity: O(n) Space Complexity: O(1)
---
Pattern 6: In-place Reversal of Linked List
Use Case: Reversing linked lists or parts of linked lists without extra space.
When to Use:
- Reverse entire linked list
- Reverse sublist from position m to n
- Reverse in k-groups
- Palindrome linked list check
Example Problems:
- Reverse Linked List
- Reverse Linked List II
- Reverse Nodes in k-Group
Implementation (Python):
def reverse_linked_list(head):
"""Reverse linked list in-place."""
prev = None
current = head
while current:
next_node = current.next # Save next
current.next = prev # Reverse pointer
prev = current # Move prev forward
current = next_node # Move current forward
return prev # New headImplementation (JavaScript):
function reverseLinkedList(head) {
let prev = null;
let current = head;
while (current !== null) {
const nextNode = current.next;
current.next = prev;
prev = current;
current = nextNode;
}
return prev;
}Time Complexity: O(n) Space Complexity: O(1)
---
Pattern 7: Tree BFS (Breadth-First Search)
Use Case: Level-order traversal of trees, finding level-specific information.
When to Use:
- Level order traversal
- Find minimum depth
- Zigzag level order traversal
- Connect level order siblings
- Right view of tree
Example Problems:
- Binary Tree Level Order Traversal
- Binary Tree Zigzag Traversal
- Minimum Depth of Binary Tree
- Connect Level Order Siblings
Implementation (Python):
from collections import deque
def level_order_traversal(root):
"""BFS traversal returning list of levels."""
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
current_level = []
for _ in range(level_size):
node = queue.popleft()
current_level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(current_level)
return resultTime Complexity: O(n) Space Complexity: O(n) for queue
---
Pattern 8: Tree DFS (Depth-First Search)
Use Case: Path-based tree problems, recursive tree traversal.
When to Use:
- Find all paths from root to leaf
- Sum of path numbers
- Path with given sum
- Count paths with sum
- Tree diameter
Types: 1. Preorder: Root → Left → Right 2. Inorder: Left → Root → Right 3. Postorder: Left → Right → Root
Example Problems:
- Binary Tree Paths
- Path Sum
- Sum Root to Leaf Numbers
- Diameter of Binary Tree
Implementation (Python):
def has_path_sum(root, target_sum):
"""Check if tree has root-to-leaf path with given sum."""
if not root:
return False
# Leaf node - check if sum matches
if not root.left and not root.right:
return root.val == target_sum
# Recursive DFS
remaining_sum = target_sum - root.val
return (has_path_sum(root.left, remaining_sum) or
has_path_sum(root.right, remaining_sum))Time Complexity: O(n) Space Complexity: O(h) where h is tree height (recursion stack)
---
Pattern 9: Two Heaps
Use Case: Problems where you need to find the median or divide elements into two halves.
When to Use:
- Find median from data stream
- Sliding window median
- IPO (maximize capital)
Structure:
- Max heap: Stores smaller half of numbers
- Min heap: Stores larger half of numbers
- Median is either max of max-heap or average of both tops
Implementation (Python):
import heapq
class MedianFinder:
def __init__(self):
self.max_heap = [] # Smaller half (inverted for max heap)
self.min_heap = [] # Larger half
def add_num(self, num):
# Add to max heap first
heapq.heappush(self.max_heap, -num)
# Balance: move max of max_heap to min_heap
heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
# Ensure max_heap has equal or one more element
if len(self.max_heap) < len(self.min_heap):
heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))
def find_median(self):
if len(self.max_heap) > len(self.min_heap):
return -self.max_heap[0]
return (-self.max_heap[0] + self.min_heap[0]) / 2Time Complexity: O(log n) for insertion, O(1) for median Space Complexity: O(n)
---
Pattern 10: Subsets (Backtracking)
Use Case: Problems requiring generation of all combinations, permutations, or subsets.
When to Use:
- Generate all subsets/power set
- Permutations
- Combinations
- Letter case permutation
Example Problems:
- Subsets
- Permutations
- Combinations
- Generate Parentheses
Implementation (Python):
def subsets(nums):
"""Generate all subsets using backtracking."""
result = []
def backtrack(start, current):
# Add current subset
result.append(current[:])
# Explore further elements
for i in range(start, len(nums)):
current.append(nums[i])
backtrack(i + 1, current)
current.pop() # Backtrack
backtrack(0, [])
return resultTime Complexity: O(2^n) - exponential Space Complexity: O(n) for recursion depth
---
Pattern 11: Binary Search
Use Case: Search in sorted arrays or search space, finding boundaries.
When to Use:
- Search in sorted array
- Find first/last occurrence
- Search in rotated sorted array
- Find peak element
- Search in 2D matrix
Template:
def binary_search(arr, target):
"""Standard binary search."""
left, right = 0, len(arr) - 1
while left <= right:
mid = left + (right - left) // 2 # Avoid overflow
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1 # Not foundTime Complexity: O(log n) Space Complexity: O(1)
---
Pattern 12: Top K Elements
Use Case: Find k largest/smallest elements, k most frequent elements.
When to Use:
- K largest/smallest elements
- K closest points
- K most frequent elements
- Sort characters by frequency
Implementation (Python):
import heapq
def k_largest_elements(nums, k):
"""Find k largest elements using min heap."""
# Maintain min heap of size k
min_heap = []
for num in nums:
heapq.heappush(min_heap, num)
if len(min_heap) > k:
heapq.heappop(min_heap)
return min_heapTime Complexity: O(n log k) Space Complexity: O(k)
---
Pattern 13: Modified Binary Search
Use Case: Binary search variations for complex scenarios.
When to Use:
- Search in rotated sorted array
- Find minimum in rotated sorted array
- Search in infinite sorted array
- Find range (first and last position)
Implementation (Python):
def search_rotated_array(nums, target):
"""Search in rotated sorted array."""
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
# Determine which half is sorted
if nums[left] <= nums[mid]: # Left half sorted
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else: # Right half sorted
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1---
Pattern 14: Dynamic Programming (Top-Down)
Use Case: Optimization problems with overlapping subproblems.
When to Use:
- Fibonacci, climbing stairs
- House robber
- Coin change
- Longest common subsequence
- 0/1 Knapsack
Template (Memoization):
def fibonacci(n, memo={}):
"""Calculate nth Fibonacci number with memoization."""
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
return memo[n]Time Complexity: Depends on problem (often O(n) or O(n²)) Space Complexity: O(n) for memoization + recursion stack
---
Pattern 15: Dynamic Programming (Bottom-Up)
Use Case: Same as top-down, but iterative (often more efficient).
Template (Tabulation):
def fibonacci_dp(n):
"""Calculate nth Fibonacci using bottom-up DP."""
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]Space Optimization (for Fibonacci):
def fibonacci_optimized(n):
"""Space-optimized Fibonacci."""
if n <= 1:
return n
prev2, prev1 = 0, 1
for _ in range(2, n + 1):
current = prev1 + prev2
prev2, prev1 = prev1, current
return prev1---
How to Choose the Right Pattern
Ask yourself:
1. What's the input structure?
- Sorted array → Binary Search, Two Pointers
- Linked list → Fast/Slow Pointers, In-place Reversal
- Tree → BFS, DFS
- Intervals → Merge Intervals
2. What am I looking for?
- Subarray/substring → Sliding Window
- Pairs/triplets → Two Pointers
- All combinations → Backtracking
- Optimal solution with choices → Dynamic Programming
- Top k elements → Heap
3. Are there constraints?
- Numbers in range [1, n] → Cyclic Sort
- Need median → Two Heaps
- In-place modification → Two Pointers, Cyclic Sort
4. What's the time complexity requirement?
- O(log n) → Binary Search
- O(n) → Two Pointers, Sliding Window, Hash Map
- O(n log n) → Sorting, Heap
- Exponential acceptable? → Backtracking, Recursion
---
Practice Strategy: 1. Master one pattern at a time 2. Solve 5-10 problems per pattern 3. Identify the pattern in new problems 4. Combine patterns for complex problems
Common Pattern Combinations:
- Two Pointers + Sliding Window
- Binary Search + DFS
- Dynamic Programming + Memoization
- Backtracking + Pruning
Clean Code Principles
Core Principles
1. Meaningful Names
Variables:
# BAD
d = 10 # What is 'd'?
t = time.time()
# GOOD
elapsed_days = 10
current_timestamp = time.time()Functions:
# BAD
def process(data):
pass
# GOOD
def calculate_user_average_score(user_scores):
passClasses:
# BAD
class Data:
pass
# GOOD
class CustomerOrderProcessor:
passBoolean variables - use predicates:
# BAD
flag = True
status = False
# GOOD
is_active = True
has_permission = False
can_edit = True
should_retry = False---
2. Functions Should Do One Thing
BAD - Multiple responsibilities:
def process_user_data(user):
# Validate
if not user.email:
raise ValueError("Email required")
# Transform
user.name = user.name.upper()
# Save to database
db.save(user)
# Send email
email_service.send_welcome(user.email)
# Log
logger.info(f"User processed: {user.id}")GOOD - Single responsibility:
def validate_user(user):
if not user.email:
raise ValueError("Email required")
def normalize_user_data(user):
user.name = user.name.upper()
return user
def save_user(user):
db.save(user)
def send_welcome_email(email):
email_service.send_welcome(email)
def process_user_data(user):
validate_user(user)
user = normalize_user_data(user)
save_user(user)
send_welcome_email(user.email)
logger.info(f"User processed: {user.id}")---
3. Keep Functions Small
Guideline: Aim for 10-20 lines per function.
BAD - 100+ line function:
def generate_report(users):
# 100 lines of mixed logic
# Filtering, sorting, formatting, calculations, file I/O
passGOOD - Extracted functions:
def generate_report(users):
active_users = filter_active_users(users)
sorted_users = sort_by_activity(active_users)
report_data = calculate_statistics(sorted_users)
formatted_report = format_report(report_data)
save_report(formatted_report)
def filter_active_users(users):
return [u for u in users if u.is_active]
def sort_by_activity(users):
return sorted(users, key=lambda u: u.activity_score, reverse=True)---
4. DRY (Don't Repeat Yourself)
BAD - Duplication:
def calculate_student_grade(math_score, science_score):
if math_score >= 90:
math_grade = 'A'
elif math_score >= 80:
math_grade = 'B'
elif math_score >= 70:
math_grade = 'C'
else:
math_grade = 'F'
if science_score >= 90:
science_grade = 'A'
elif science_score >= 80:
science_grade = 'B'
elif science_score >= 70:
science_grade = 'C'
else:
science_grade = 'F'
return math_grade, science_gradeGOOD - Extract common logic:
def score_to_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
return 'F'
def calculate_student_grade(math_score, science_score):
return score_to_grade(math_score), score_to_grade(science_score)---
5. Avoid Magic Numbers
BAD:
if age > 18:
can_vote = True
if len(password) < 8:
raise ValueError("Password too short")GOOD:
VOTING_AGE = 18
MIN_PASSWORD_LENGTH = 8
if age > VOTING_AGE:
can_vote = True
if len(password) < MIN_PASSWORD_LENGTH:
raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")---
6. Error Handling
BAD - Bare except, silent failures:
try:
result = risky_operation()
except:
pass # What went wrong?GOOD - Specific exceptions, informative messages:
try:
result = risky_operation()
except ValueError as e:
logger.error(f"Invalid value: {e}")
raise
except ConnectionError as e:
logger.error(f"Connection failed: {e}")
# Retry or fallback logic---
7. Use Early Returns (Guard Clauses)
BAD - Nested conditions:
def process_order(order):
if order is not None:
if order.is_valid():
if order.total > 0:
if order.customer.has_credit():
# Process order
return True
return FalseGOOD - Early returns:
def process_order(order):
if order is None:
return False
if not order.is_valid():
return False
if order.total <= 0:
return False
if not order.customer.has_credit():
return False
# Process order
return True---
8. Comment Why, Not What
BAD - Obvious comments:
# Increment i by 1
i += 1
# Loop through users
for user in users:
passGOOD - Explain non-obvious reasoning:
# Use binary search because list is always sorted
# and can contain millions of items
index = binary_search(sorted_list, target)
# Cache for 5 minutes to reduce database load
# during peak hours (based on profiling data)
@cache(ttl=300)
def get_popular_products():
pass---
9. Keep Indentation Shallow
BAD - Deep nesting:
def process_data(items):
for item in items:
if item.is_valid():
if item.quantity > 0:
if item.price > 0:
if item.in_stock:
# Process
passGOOD - Use early returns, extraction:
def process_data(items):
for item in items:
if not should_process_item(item):
continue
process_item(item)
def should_process_item(item):
return (item.is_valid() and
item.quantity > 0 and
item.price > 0 and
item.in_stock)---
10. Consistent Formatting
Use a formatter: Black (Python), Prettier (JavaScript), gofmt (Go)
Consistency matters:
# Pick one style and stick to it
# Style 1
def foo(x, y, z):
return x + y + z
# Style 2
def foo(
x,
y,
z
):
return x + y + z
# Don't mix them randomly in the same file!---
SOLID Principles
S - Single Responsibility Principle
A class should have one, and only one, reason to change.
BAD:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def save(self):
# Database logic
db.execute(f"INSERT INTO users...")
def send_email(self, message):
# Email logic
smtp.send(self.email, message)GOOD:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
class UserRepository:
def save(self, user):
db.execute(f"INSERT INTO users...")
class EmailService:
def send_email(self, email, message):
smtp.send(email, message)---
O - Open/Closed Principle
Open for extension, closed for modification.
BAD:
class PaymentProcessor:
def process(self, payment_type, amount):
if payment_type == "credit_card":
# Credit card processing
pass
elif payment_type == "paypal":
# PayPal processing
pass
# Adding new type requires modifying this function!GOOD:
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process(self, amount):
pass
class CreditCardPayment(PaymentMethod):
def process(self, amount):
# Credit card processing
pass
class PayPalPayment(PaymentMethod):
def process(self, amount):
# PayPal processing
pass
class PaymentProcessor:
def process(self, payment_method: PaymentMethod, amount):
payment_method.process(amount)---
L - Liskov Substitution Principle
Subclasses should be substitutable for their base classes.
BAD:
class Bird:
def fly(self):
print("Flying")
class Penguin(Bird):
def fly(self):
raise Exception("Penguins can't fly!")GOOD:
class Bird:
def move(self):
pass
class FlyingBird(Bird):
def move(self):
self.fly()
def fly(self):
print("Flying")
class Penguin(Bird):
def move(self):
self.swim()
def swim(self):
print("Swimming")---
I - Interface Segregation Principle
Clients should not depend on interfaces they don't use.
BAD:
class Worker(ABC):
@abstractmethod
def work(self):
pass
@abstractmethod
def eat(self):
pass
class Robot(Worker):
def work(self):
print("Working")
def eat(self):
# Robots don't eat!
raise NotImplementedErrorGOOD:
class Workable(ABC):
@abstractmethod
def work(self):
pass
class Eatable(ABC):
@abstractmethod
def eat(self):
pass
class Human(Workable, Eatable):
def work(self):
print("Working")
def eat(self):
print("Eating")
class Robot(Workable):
def work(self):
print("Working")---
D - Dependency Inversion Principle
Depend on abstractions, not concretions.
BAD:
class MySQLDatabase:
def save(self, data):
pass
class UserService:
def __init__(self):
self.db = MySQLDatabase() # Tightly coupled
def save_user(self, user):
self.db.save(user)GOOD:
class Database(ABC):
@abstractmethod
def save(self, data):
pass
class MySQLDatabase(Database):
def save(self, data):
pass
class PostgresDatabase(Database):
def save(self, data):
pass
class UserService:
def __init__(self, database: Database):
self.db = database # Depends on abstraction
def save_user(self, user):
self.db.save(user)---
Code Smells to Avoid
1. Long Parameter List
# BAD
def create_user(name, email, phone, address, city, state, zip, country):
pass
# GOOD
class UserData:
def __init__(self, name, email, contact_info, address):
pass
def create_user(user_data: UserData):
pass2. Primitive Obsession
# BAD
def calculate_shipping(width, height, depth, weight):
pass
# GOOD
class Dimensions:
def __init__(self, width, height, depth):
self.width = width
self.height = height
self.depth = depth
class Package:
def __init__(self, dimensions, weight):
self.dimensions = dimensions
self.weight = weight
def calculate_shipping(package: Package):
pass3. Feature Envy
# BAD - Method in class A uses mostly data from class B
class Order:
def calculate_total(self, customer):
discount = customer.discount_rate
points = customer.loyalty_points
# Uses customer data extensively
pass
# GOOD - Move method to class B
class Customer:
def calculate_order_discount(self, order):
discount = self.discount_rate
points = self.loyalty_points
# Uses own data
pass---
Testing Best Practices
1. AAA Pattern (Arrange-Act-Assert)
def test_user_creation():
# Arrange
name = "Alice"
email = "alice@example.com"
# Act
user = User(name, email)
# Assert
assert user.name == name
assert user.email == email2. One Assertion Per Test (guideline)
# AVOID multiple unrelated assertions
def test_user():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
assert user.email == "alice@example.com"
assert user.is_valid()
assert user.created_at is not None
# PREFER focused tests
def test_user_name():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
def test_user_email():
user = User("Alice", "alice@example.com")
assert user.email == "alice@example.com"3. Test Names Should Be Descriptive
# BAD
def test_user():
pass
# GOOD
def test_user_creation_with_valid_email_succeeds():
pass
def test_user_creation_with_invalid_email_raises_error():
pass---
Refactoring Checklist
When you see code that needs improvement:
1. Is it tested? If not, write tests first 2. One change at a time - Refactor incrementally 3. Run tests after each change - Ensure nothing breaks 4. Commit frequently - Small, focused commits 5. Don't change behavior - Refactoring should preserve functionality
---
Key Takeaways
1. Names matter - Spend time choosing good names 2. Functions should be small - Aim for 10-20 lines 3. One responsibility - Each function/class does one thing well 4. DRY - Don't repeat yourself 5. SOLID - Follow the five SOLID principles 6. Early returns - Reduce nesting with guard clauses 7. Comment why - Not what (code shows what) 8. Test - Write tests, refactor with confidence
Remember: Clean code is not about perfection—it's about making code easier to read, maintain, and extend!
Arrays & Strings Reference
Arrays
Core Concepts
An array is a contiguous collection of elements stored at consecutive memory locations. Arrays provide O(1) random access but O(n) insertion/deletion (except at the end).
Key Properties:
- Fixed or dynamic size (depending on language)
- Homogeneous elements (same type)
- Zero-indexed in most languages
- Contiguous memory allocation
Common Operations
| Operation | Time Complexity | Notes |
|---|---|---|
| Access | O(1) | Direct index lookup |
| Search | O(n) | O(log n) if sorted + binary search |
| Insert (end) | O(1) amortized | May trigger resize |
| Insert (arbitrary) | O(n) | Shift elements |
| Delete (end) | O(1) | Pop operation |
| Delete (arbitrary) | O(n) | Shift elements |
Python Implementation
# Array/List operations
arr = [1, 2, 3, 4, 5]
# Access
element = arr[2] # O(1)
# Search
index = arr.index(3) # O(n)
exists = 3 in arr # O(n)
# Insert
arr.append(6) # O(1) at end
arr.insert(2, 10) # O(n) at arbitrary position
# Delete
arr.pop() # O(1) from end
arr.pop(2) # O(n) from arbitrary position
arr.remove(10) # O(n) - finds and removes
# Slicing
subarray = arr[1:4] # O(k) where k is slice size
# Common patterns
reversed_arr = arr[::-1]
sorted_arr = sorted(arr) # O(n log n)JavaScript Implementation
// Array operations
const arr = [1, 2, 3, 4, 5];
// Access
const element = arr[2]; // O(1)
// Search
const index = arr.indexOf(3); // O(n)
const exists = arr.includes(3); // O(n)
// Insert
arr.push(6); // O(1) at end
arr.splice(2, 0, 10); // O(n) at arbitrary position
// Delete
arr.pop(); // O(1) from end
arr.splice(2, 1); // O(n) from arbitrary position
// Slicing
const subarray = arr.slice(1, 4); // O(k)
// Common patterns
const reversedArr = arr.reverse();
const sortedArr = arr.sort((a, b) => a - b); // O(n log n)---
Strings
Core Concepts
A string is a sequence of characters. In most languages, strings are immutable (Python, Java) or treated as character arrays (C++, JavaScript allows mutation in some cases).
Key Properties:
- Immutable in Python, Java, JavaScript (primitives)
- Character array in C++
- UTF-8/UTF-16 encoding considerations
- Concatenation can be expensive
Common Operations
| Operation | Time Complexity | Notes |
|---|---|---|
| Access | O(1) | Direct index lookup |
| Concatenation | O(n + m) | Creates new string if immutable |
| Substring | O(k) | k = substring length |
| Search | O(n * m) | Naive; O(n + m) with KMP |
| Replace | O(n) | Immutable languages create new string |
Python Implementation
s = "hello world"
# Access
char = s[0] # O(1)
# Slicing
substring = s[0:5] # O(k)
substring = s[::-1] # Reverse O(n)
# Search
index = s.find("world") # O(n), returns -1 if not found
index = s.index("world") # O(n), raises error if not found
exists = "world" in s # O(n)
# Modification (creates new string)
s_upper = s.upper()
s_lower = s.lower()
s_replaced = s.replace("world", "python")
# Split and join
words = s.split() # O(n)
joined = " ".join(words) # O(n)
# Common patterns
is_alpha = s.isalpha()
is_digit = s.isdigit()
stripped = s.strip() # Remove whitespaceJavaScript Implementation
let s = "hello world";
// Access
const char = s[0]; // O(1)
// Slicing
const substring = s.slice(0, 5); // O(k)
const reversed = s.split('').reverse().join(''); // O(n)
// Search
const index = s.indexOf("world"); // O(n), returns -1 if not found
const exists = s.includes("world"); // O(n)
// Modification (creates new string)
const sUpper = s.toUpperCase();
const sLower = s.toLowerCase();
const sReplaced = s.replace("world", "javascript");
// Split and join
const words = s.split(' '); // O(n)
const joined = words.join(' '); // O(n)
// Common methods
const trimmed = s.trim();
const startsWithHello = s.startsWith("hello");
const endsWithWorld = s.endsWith("world");---
Common Array/String Patterns
1. Two Pointers
Problem: Check if string is palindrome
def is_palindrome(s):
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True2. Sliding Window
Problem: Maximum sum subarray of size k
def max_sum_subarray(arr, k):
if len(arr) < k:
return None
window_sum = sum(arr[:k])
max_sum = window_sum
for i in range(k, len(arr)):
window_sum = window_sum - arr[i - k] + arr[i]
max_sum = max(max_sum, window_sum)
return max_sum3. Prefix Sum
Problem: Range sum queries
class RangeSumQuery:
def __init__(self, nums):
self.prefix = [0]
for num in nums:
self.prefix.append(self.prefix[-1] + num)
def sum_range(self, left, right):
return self.prefix[right + 1] - self.prefix[left]4. Hash Map for Frequency
Problem: First unique character in string
def first_unique_char(s):
from collections import Counter
freq = Counter(s)
for i, char in enumerate(s):
if freq[char] == 1:
return i
return -15. String Builder (for performance)
Problem: Efficient string concatenation
# BAD: O(n²) due to immutability
result = ""
for i in range(n):
result += str(i) # Creates new string each time
# GOOD: O(n) using list
result = []
for i in range(n):
result.append(str(i))
final_result = "".join(result)---
Advanced Techniques
1. Kadane's Algorithm (Max Subarray Sum)
def max_subarray_sum(nums):
"""Find maximum sum of contiguous subarray."""
max_current = max_global = nums[0]
for i in range(1, len(nums)):
max_current = max(nums[i], max_current + nums[i])
max_global = max(max_global, max_current)
return max_globalTime: O(n), Space: O(1)
2. KMP String Matching
def kmp_search(text, pattern):
"""Knuth-Morris-Pratt string matching."""
def compute_lps(pattern):
lps = [0] * len(pattern)
length = 0
i = 1
while i < len(pattern):
if pattern[i] == pattern[length]:
length += 1
lps[i] = length
i += 1
else:
if length != 0:
length = lps[length - 1]
else:
lps[i] = 0
i += 1
return lps
lps = compute_lps(pattern)
i = j = 0
while i < len(text):
if pattern[j] == text[i]:
i += 1
j += 1
if j == len(pattern):
return i - j # Pattern found
elif i < len(text) and pattern[j] != text[i]:
if j != 0:
j = lps[j - 1]
else:
i += 1
return -1 # Not foundTime: O(n + m), Space: O(m)
3. Rabin-Karp (Rolling Hash)
def rabin_karp(text, pattern):
"""Rolling hash string matching."""
d = 256 # Number of characters
q = 101 # Prime number
m = len(pattern)
n = len(text)
p = 0 # Hash value for pattern
t = 0 # Hash value for text
h = 1
# Calculate h = pow(d, m-1) % q
for i in range(m - 1):
h = (h * d) % q
# Calculate initial hash values
for i in range(m):
p = (d * p + ord(pattern[i])) % q
t = (d * t + ord(text[i])) % q
# Slide pattern over text
for i in range(n - m + 1):
if p == t:
# Check characters one by one
if text[i:i + m] == pattern:
return i
# Calculate hash for next window
if i < n - m:
t = (d * (t - ord(text[i]) * h) + ord(text[i + m])) % q
if t < 0:
t += q
return -1Average Time: O(n + m), Worst: O(n * m)
---
Common Pitfalls & Best Practices
Pitfall 1: Off-by-One Errors
# WRONG
for i in range(len(arr) - 1): # Misses last element
print(arr[i])
# CORRECT
for i in range(len(arr)):
print(arr[i])Pitfall 2: Modifying While Iterating
# WRONG
for item in arr:
if item % 2 == 0:
arr.remove(item) # Can skip elements
# CORRECT
arr = [item for item in arr if item % 2 != 0]
# Or iterate backwards
for i in range(len(arr) - 1, -1, -1):
if arr[i] % 2 == 0:
arr.pop(i)Pitfall 3: String Concatenation in Loop
# INEFFICIENT: O(n²)
result = ""
for i in range(n):
result += str(i)
# EFFICIENT: O(n)
result = "".join(str(i) for i in range(n))Best Practice 1: Use Built-in Functions
# Manual max finding
max_val = arr[0]
for val in arr:
if val > max_val:
max_val = val
# Better
max_val = max(arr)Best Practice 2: List Comprehensions
# Traditional loop
squares = []
for x in range(10):
squares.append(x ** 2)
# List comprehension (more Pythonic)
squares = [x ** 2 for x in range(10)]Best Practice 3: Enumerate for Index + Value
# Manual indexing
for i in range(len(arr)):
print(f"Index {i}: {arr[i]}")
# Better
for i, val in enumerate(arr):
print(f"Index {i}: {val}")---
Interview Problem Checklist
When solving array/string problems:
1. Clarify constraints:
- Array size limits?
- Can array be empty?
- Value ranges?
- In-place modification allowed?
2. Consider edge cases:
- Empty array/string
- Single element
- All elements same
- Already sorted
- Negative numbers (for arrays)
3. Choose approach:
- Brute force first (to verify logic)
- Optimize (two pointers, hash map, sliding window)
- Consider time/space trade-offs
4. Test with examples:
- Normal case
- Edge cases
- Large input
5. Analyze complexity:
- Time complexity
- Space complexity
- Can it be optimized further?
Trees & Graphs Reference
Binary Trees
Core Concepts
A binary tree is a hierarchical data structure where each node has at most two children (left and right).
Key Properties:
- Each node has at most 2 children
- Root node has no parent
- Leaf nodes have no children
- Height: longest path from root to leaf
- Depth: distance from root to node
Types of Binary Trees:
- Full: Every node has 0 or 2 children
- Complete: All levels filled except possibly last, which fills left to right
- Perfect: All internal nodes have 2 children, all leaves at same level
- Balanced: Height difference between left and right subtrees ≤ 1
Node Structure
Python:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = rightJavaScript:
class TreeNode {
constructor(val = 0, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}---
Tree Traversals
1. Depth-First Search (DFS)
Inorder (Left → Root → Right)
Use: BST gives sorted order
def inorder(root):
result = []
def traverse(node):
if not node:
return
traverse(node.left)
result.append(node.val)
traverse(node.right)
traverse(root)
return resultPreorder (Root → Left → Right)
Use: Copy tree, prefix expressions
def preorder(root):
result = []
def traverse(node):
if not node:
return
result.append(node.val)
traverse(node.left)
traverse(node.right)
traverse(root)
return resultPostorder (Left → Right → Root)
Use: Delete tree, postfix expressions
def postorder(root):
result = []
def traverse(node):
if not node:
return
traverse(node.left)
traverse(node.right)
result.append(node.val)
traverse(root)
return result2. Breadth-First Search (BFS)
Use: Level-order traversal, shortest path in unweighted tree
from collections import deque
def level_order(root):
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
current_level = []
for _ in range(level_size):
node = queue.popleft()
current_level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(current_level)
return resultTime: O(n), Space: O(w) where w is max width
---
Binary Search Tree (BST)
Properties
- Left subtree values < node value
- Right subtree values > node value
- Both subtrees are also BSTs
- Inorder traversal gives sorted sequence
Common Operations
Search
def search_bst(root, val):
if not root or root.val == val:
return root
if val < root.val:
return search_bst(root.left, val)
return search_bst(root.right, val)Time: O(h) where h is height (O(log n) balanced, O(n) worst)
Insert
def insert_bst(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insert_bst(root.left, val)
else:
root.right = insert_bst(root.right, val)
return rootDelete
def delete_bst(root, val):
if not root:
return None
if val < root.val:
root.left = delete_bst(root.left, val)
elif val > root.val:
root.right = delete_bst(root.right, val)
else:
# Node to delete found
# Case 1: No children
if not root.left and not root.right:
return None
# Case 2: One child
if not root.left:
return root.right
if not root.right:
return root.left
# Case 3: Two children
# Find inorder successor (min in right subtree)
min_node = find_min(root.right)
root.val = min_node.val
root.right = delete_bst(root.right, min_node.val)
return root
def find_min(node):
while node.left:
node = node.left
return node---
Common Tree Algorithms
1. Height/Depth of Tree
def max_depth(root):
if not root:
return 0
return 1 + max(max_depth(root.left), max_depth(root.right))2. Balanced Tree Check
def is_balanced(root):
def height(node):
if not node:
return 0
left_height = height(node.left)
if left_height == -1:
return -1
right_height = height(node.right)
if right_height == -1:
return -1
if abs(left_height - right_height) > 1:
return -1
return 1 + max(left_height, right_height)
return height(root) != -13. Lowest Common Ancestor (BST)
def lowest_common_ancestor_bst(root, p, q):
if p.val < root.val and q.val < root.val:
return lowest_common_ancestor_bst(root.left, p, q)
if p.val > root.val and q.val > root.val:
return lowest_common_ancestor_bst(root.right, p, q)
return root4. Diameter of Binary Tree
def diameter_of_binary_tree(root):
diameter = 0
def height(node):
nonlocal diameter
if not node:
return 0
left = height(node.left)
right = height(node.right)
diameter = max(diameter, left + right)
return 1 + max(left, right)
height(root)
return diameter5. Serialize and Deserialize
def serialize(root):
"""Encode tree to string."""
def helper(node):
if not node:
return 'null,'
return str(node.val) + ',' + helper(node.left) + helper(node.right)
return helper(root)
def deserialize(data):
"""Decode string to tree."""
def helper(nodes):
val = next(nodes)
if val == 'null':
return None
node = TreeNode(int(val))
node.left = helper(nodes)
node.right = helper(nodes)
return node
return helper(iter(data.split(',')))---
Graphs
Core Concepts
A graph is a collection of nodes (vertices) connected by edges.
Types:
- Directed vs Undirected: Edges have direction or not
- Weighted vs Unweighted: Edges have weights or not
- Cyclic vs Acyclic: Contains cycles or not
- Connected vs Disconnected: Path exists between all nodes or not
Representations
1. Adjacency List (Most Common)
# Undirected graph
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
# Or using defaultdict
from collections import defaultdict
graph = defaultdict(list)
graph['A'].append('B')
graph['B'].append('A')Space: O(V + E)
2. Adjacency Matrix
# graph[i][j] = 1 if edge from i to j exists
n = 5 # number of vertices
graph = [[0] * n for _ in range(n)]
graph[0][1] = 1 # Edge from 0 to 1
graph[1][0] = 1 # Edge from 1 to 0 (undirected)Space: O(V²)
---
Graph Traversals
1. Depth-First Search (DFS)
Recursive:
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start)
for neighbor in graph[start]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
return visitedIterative (using stack):
def dfs_iterative(graph, start):
visited = set()
stack = [start]
while stack:
node = stack.pop()
if node not in visited:
visited.add(node)
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
stack.append(neighbor)
return visitedTime: O(V + E), Space: O(V)
2. Breadth-First Search (BFS)
from collections import deque
def bfs(graph, start):
visited = set([start])
queue = deque([start])
while queue:
node = queue.popleft()
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return visitedTime: O(V + E), Space: O(V)
---
Common Graph Algorithms
1. Cycle Detection (Undirected Graph)
def has_cycle(graph):
visited = set()
def dfs(node, parent):
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
if dfs(neighbor, node):
return True
elif neighbor != parent:
return True # Cycle found
return False
for node in graph:
if node not in visited:
if dfs(node, None):
return True
return False2. Cycle Detection (Directed Graph)
def has_cycle_directed(graph):
WHITE, GRAY, BLACK = 0, 1, 2
color = {node: WHITE for node in graph}
def dfs(node):
color[node] = GRAY
for neighbor in graph[node]:
if color[neighbor] == GRAY:
return True # Back edge found
if color[neighbor] == WHITE and dfs(neighbor):
return True
color[node] = BLACK
return False
for node in graph:
if color[node] == WHITE:
if dfs(node):
return True
return False3. Topological Sort (DAG)
def topological_sort(graph):
visited = set()
stack = []
def dfs(node):
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs(neighbor)
stack.append(node)
for node in graph:
if node not in visited:
dfs(node)
return stack[::-1] # ReverseTime: O(V + E)
4. Shortest Path (Unweighted - BFS)
from collections import deque
def shortest_path_bfs(graph, start, end):
queue = deque([(start, [start])])
visited = set([start])
while queue:
node, path = queue.popleft()
if node == end:
return path
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
return None # No path found5. Dijkstra's Algorithm (Weighted Graph)
import heapq
def dijkstra(graph, start):
"""Find shortest paths from start to all nodes."""
distances = {node: float('inf') for node in graph}
distances[start] = 0
pq = [(0, start)] # (distance, node)
while pq:
current_dist, current_node = heapq.heappop(pq)
if current_dist > distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance = current_dist + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distancesTime: O((V + E) log V) with min heap
6. Union-Find (Disjoint Set)
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # Path compression
return self.parent[x]
def union(self, x, y):
root_x = self.find(x)
root_y = self.find(y)
if root_x == root_y:
return False
# Union by rank
if self.rank[root_x] < self.rank[root_y]:
self.parent[root_x] = root_y
elif self.rank[root_x] > self.rank[root_y]:
self.parent[root_y] = root_x
else:
self.parent[root_y] = root_x
self.rank[root_x] += 1
return TrueUse: Cycle detection, Kruskal's MST, connected components
---
Common Graph Problems
1. Number of Islands
def num_islands(grid):
if not grid:
return 0
count = 0
rows, cols = len(grid), len(grid[0])
def dfs(r, c):
if (r < 0 or r >= rows or c < 0 or c >= cols or
grid[r][c] == '0'):
return
grid[r][c] = '0' # Mark as visited
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
count += 1
dfs(r, c)
return count2. Course Schedule (Cycle Detection)
def can_finish(num_courses, prerequisites):
graph = defaultdict(list)
for course, prereq in prerequisites:
graph[course].append(prereq)
WHITE, GRAY, BLACK = 0, 1, 2
color = [WHITE] * num_courses
def has_cycle(course):
color[course] = GRAY
for prereq in graph[course]:
if color[prereq] == GRAY:
return True
if color[prereq] == WHITE and has_cycle(prereq):
return True
color[course] = BLACK
return False
for course in range(num_courses):
if color[course] == WHITE:
if has_cycle(course):
return False
return True3. Clone Graph
def clone_graph(node):
if not node:
return None
clones = {}
def dfs(node):
if node in clones:
return clones[node]
clone = Node(node.val)
clones[node] = clone
for neighbor in node.neighbors:
clone.neighbors.append(dfs(neighbor))
return clone
return dfs(node)---
When to Use What
Tree Traversal:
- DFS (Inorder): BST → sorted order
- DFS (Preorder): Copy tree, prefix notation
- DFS (Postorder): Delete tree, postfix notation
- BFS: Level-order, shortest path
Graph Traversal:
- DFS: Cycle detection, topological sort, connected components
- BFS: Shortest path (unweighted), level-wise exploration
Shortest Path:
- BFS: Unweighted graphs
- Dijkstra: Weighted graphs (non-negative weights)
- Bellman-Ford: Weighted graphs (can have negative weights)
- Floyd-Warshall: All-pairs shortest path
Tree/Graph Choice:
- Adjacency List: Sparse graphs (E << V²)
- Adjacency Matrix: Dense graphs, quick edge lookup
Creational Design Patterns
Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.
---
1. Singleton Pattern
Problem
You need exactly one instance of a class (e.g., database connection, configuration manager, logger).
Bad Example
# Multiple instances can be created
class DatabaseConnection:
def __init__(self):
self.connection = self.connect()
def connect(self):
print("Connecting to database...")
return "DB Connection"
# Problem: Multiple connections created
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # False - different instances!Solution
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
class DatabaseConnection(Singleton):
def __init__(self):
if not hasattr(self, 'initialized'):
self.connection = self.connect()
self.initialized = True
def connect(self):
print("Connecting to database...")
return "DB Connection"
# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True - same instance!JavaScript Implementation
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connection = this.connect();
DatabaseConnection.instance = this;
}
connect() {
console.log("Connecting to database...");
return "DB Connection";
}
}
// Usage
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // trueWhen to Use
- Use: Logger, configuration, connection pool, cache
- Don't Use: When you need multiple instances, or for simple utilities (use module instead)
Pros & Cons
✅ Controlled access to single instance ✅ Lazy initialization ❌ Global state (can make testing harder) ❌ Can violate Single Responsibility Principle
---
2. Factory Pattern
Problem
You need to create objects without specifying exact class. Creation logic is complex or depends on conditions.
Bad Example
# Client code knows about all concrete classes
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
# Client has to know which class to instantiate
def get_pet(pet_type):
if pet_type == "dog":
return Dog()
elif pet_type == "cat":
return Cat()
# Adding new pet requires modifying this function!Solution
from abc import ABC, abstractmethod
# Abstract product
class Animal(ABC):
@abstractmethod
def speak(self):
pass
# Concrete products
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Bird(Animal):
def speak(self):
return "Tweet!"
# Factory
class AnimalFactory:
@staticmethod
def create_animal(animal_type):
animals = {
'dog': Dog,
'cat': Cat,
'bird': Bird
}
animal_class = animals.get(animal_type.lower())
if animal_class:
return animal_class()
raise ValueError(f"Unknown animal type: {animal_type}")
# Usage
factory = AnimalFactory()
pet = factory.create_animal('dog')
print(pet.speak()) # Woof!JavaScript Implementation
class Animal {
speak() {
throw new Error("Method must be implemented");
}
}
class Dog extends Animal {
speak() {
return "Woof!";
}
}
class Cat extends Animal {
speak() {
return "Meow!";
}
}
class AnimalFactory {
static createAnimal(animalType) {
const animals = {
dog: Dog,
cat: Cat
};
const AnimalClass = animals[animalType.toLowerCase()];
if (AnimalClass) {
return new AnimalClass();
}
throw new Error(`Unknown animal type: ${animalType}`);
}
}
// Usage
const pet = AnimalFactory.createAnimal('dog');
console.log(pet.speak()); // Woof!When to Use
- Use: When you don't know exact types beforehand, or creation logic is complex
- Don't Use: For simple object creation with no variation
Pros & Cons
✅ Loose coupling between client and products ✅ Easy to add new products (Open/Closed Principle) ✅ Centralized creation logic ❌ Can introduce many classes
---
3. Abstract Factory Pattern
Problem
You need to create families of related objects without specifying concrete classes.
Example: UI Theme Factory
from abc import ABC, abstractmethod
# Abstract products
class Button(ABC):
@abstractmethod
def render(self):
pass
class Checkbox(ABC):
@abstractmethod
def render(self):
pass
# Concrete products - Light theme
class LightButton(Button):
def render(self):
return "Rendering light button"
class LightCheckbox(Checkbox):
def render(self):
return "Rendering light checkbox"
# Concrete products - Dark theme
class DarkButton(Button):
def render(self):
return "Rendering dark button"
class DarkCheckbox(Checkbox):
def render(self):
return "Rendering dark checkbox"
# Abstract factory
class UIFactory(ABC):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_checkbox(self):
pass
# Concrete factories
class LightThemeFactory(UIFactory):
def create_button(self):
return LightButton()
def create_checkbox(self):
return LightCheckbox()
class DarkThemeFactory(UIFactory):
def create_button(self):
return DarkButton()
def create_checkbox(self):
return DarkCheckbox()
# Client code
def create_ui(factory: UIFactory):
button = factory.create_button()
checkbox = factory.create_checkbox()
return button.render(), checkbox.render()
# Usage
light_factory = LightThemeFactory()
print(create_ui(light_factory))
dark_factory = DarkThemeFactory()
print(create_ui(dark_factory))When to Use
- Use: When you need families of related objects to work together
- Don't Use: When you only have one product family
---
4. Builder Pattern
Problem
You need to construct complex objects step by step. Constructor has too many parameters.
Bad Example
# Constructor with too many parameters
class Pizza:
def __init__(self, size, cheese=False, pepperoni=False,
mushrooms=False, onions=False, bacon=False,
ham=False, pineapple=False):
self.size = size
self.cheese = cheese
self.pepperoni = pepperoni
# ... many parameters
# Hard to read, easy to make mistakes
pizza = Pizza(12, True, True, False, True, False, True, False)Solution
class Pizza:
def __init__(self, size):
self.size = size
self.cheese = False
self.pepperoni = False
self.mushrooms = False
self.onions = False
self.bacon = False
def __str__(self):
toppings = []
if self.cheese:
toppings.append("cheese")
if self.pepperoni:
toppings.append("pepperoni")
if self.mushrooms:
toppings.append("mushrooms")
if self.onions:
toppings.append("onions")
if self.bacon:
toppings.append("bacon")
return f"{self.size}\" pizza with {', '.join(toppings)}"
class PizzaBuilder:
def __init__(self, size):
self.pizza = Pizza(size)
def add_cheese(self):
self.pizza.cheese = True
return self
def add_pepperoni(self):
self.pizza.pepperoni = True
return self
def add_mushrooms(self):
self.pizza.mushrooms = True
return self
def add_onions(self):
self.pizza.onions = True
return self
def add_bacon(self):
self.pizza.bacon = True
return self
def build(self):
return self.pizza
# Usage - much more readable!
pizza = (PizzaBuilder(12)
.add_cheese()
.add_pepperoni()
.add_mushrooms()
.build())
print(pizza) # 12" pizza with cheese, pepperoni, mushroomsJavaScript Implementation
class Pizza {
constructor(size) {
this.size = size;
this.toppings = [];
}
toString() {
return `${this.size}" pizza with ${this.toppings.join(', ')}`;
}
}
class PizzaBuilder {
constructor(size) {
this.pizza = new Pizza(size);
}
addCheese() {
this.pizza.toppings.push('cheese');
return this;
}
addPepperoni() {
this.pizza.toppings.push('pepperoni');
return this;
}
addMushrooms() {
this.pizza.toppings.push('mushrooms');
return this;
}
build() {
return this.pizza;
}
}
// Usage
const pizza = new PizzaBuilder(12)
.addCheese()
.addPepperoni()
.addMushrooms()
.build();
console.log(pizza.toString());When to Use
- Use: Many constructor parameters, step-by-step construction, immutable objects
- Don't Use: Simple objects with few parameters
Pros & Cons
✅ Readable, fluent interface ✅ Control over construction process ✅ Can create different representations ❌ More code (requires builder class)
---
5. Prototype Pattern
Problem
You need to copy existing objects without making code dependent on their classes.
Solution
import copy
class Prototype:
def clone(self):
"""Deep copy of the object."""
return copy.deepcopy(self)
class Shape(Prototype):
def __init__(self, shape_type, color):
self.shape_type = shape_type
self.color = color
self.coordinates = []
def __str__(self):
return f"{self.color} {self.shape_type} at {self.coordinates}"
# Usage
original = Shape("Circle", "Red")
original.coordinates = [10, 20]
# Clone
clone = original.clone()
clone.color = "Blue"
clone.coordinates = [30, 40]
print(original) # Red Circle at [10, 20]
print(clone) # Blue Circle at [30, 40]JavaScript Implementation
class Shape {
constructor(shapeType, color) {
this.shapeType = shapeType;
this.color = color;
this.coordinates = [];
}
clone() {
const cloned = Object.create(Object.getPrototypeOf(this));
cloned.shapeType = this.shapeType;
cloned.color = this.color;
cloned.coordinates = [...this.coordinates];
return cloned;
}
toString() {
return `${this.color} ${this.shapeType} at ${this.coordinates}`;
}
}
// Usage
const original = new Shape("Circle", "Red");
original.coordinates = [10, 20];
const clone = original.clone();
clone.color = "Blue";
clone.coordinates = [30, 40];
console.log(original.toString()); // Red Circle at 10,20
console.log(clone.toString()); // Blue Circle at 30,40When to Use
- Use: Expensive object creation, need many similar objects
- Don't Use: Simple objects, shallow copying suffices
---
Pattern Selection Guide
| Pattern | Use When | Example Use Cases |
|---|---|---|
| Singleton | Need exactly one instance | Logger, Config, DB connection pool |
| Factory | Don't know exact class at compile time | Plugin system, document types |
| Abstract Factory | Need families of related objects | UI themes, cross-platform apps |
| Builder | Complex construction with many parameters | Query builders, document builders |
| Prototype | Expensive creation, need copies | Game entities, graphic editors |
---
Anti-Patterns to Avoid
1. Overusing Singleton
# DON'T make everything a singleton
class MathUtils(Singleton): # Bad - just use a module!
@staticmethod
def add(a, b):
return a + b
# DO use module-level functions
def add(a, b):
return a + b2. God Factory
# DON'T create one factory for everything
class GodFactory:
def create_user(self): ...
def create_product(self): ...
def create_order(self): ...
# ... 50 more methods
# DO use separate factories for different concerns
class UserFactory: ...
class ProductFactory: ...
class OrderFactory: ...3. Premature Abstraction
# DON'T create factory for simple cases
class DogFactory:
@staticmethod
def create():
return Dog() # Just one simple class
# DO use direct instantiation
dog = Dog()---
Key Takeaways
1. Singleton: One instance, global access 2. Factory: Decouple object creation from usage 3. Abstract Factory: Families of related objects 4. Builder: Step-by-step complex object construction 5. Prototype: Clone existing objects
Remember: Use patterns when they solve a real problem. Don't force patterns where they don't fit!
JavaScript Quick Reference
Basic Syntax
Variables & Types
// Variable declarations
let x = 5; // Block-scoped, reassignable
const y = 10; // Block-scoped, constant
var z = 15; // Function-scoped (avoid!)
// Types
let num = 42; // Number
let str = "hello"; // String
let bool = true; // Boolean
let arr = [1, 2, 3]; // Array
let obj = {a: 1}; // Object
let nothing = null; // Null
let undef = undefined; // Undefined
// Type checking
typeof num; // "number"
Array.isArray(arr); // trueStrings
// String creation
const s = "hello";
const s2 = 'hello';
const s3 = `hello`; // Template literal
// Template literals (ES6+)
const name = "Alice";
const age = 30;
const message = `${name} is ${age} years old`;
// Common methods
s.toUpperCase(); // "HELLO"
s.toLowerCase(); // "hello"
s.trim(); // Remove whitespace
s.split(','); // Split into array
s.replace('h', 'H'); // "Hello"
s.startsWith('he'); // true
s.endsWith('lo'); // true
s.includes('ll'); // true
s.indexOf('ll'); // 2
// Slicing
s[0]; // 'h'
s.slice(1, 4); // 'ell'
s.slice(-3); // 'llo'Arrays
// Creation
const nums = [1, 2, 3, 4, 5];
const mixed = [1, "hello", true];
const arr = new Array(5); // Array of length 5
// Common operations
nums.push(6); // Add to end
nums.unshift(0); // Add to beginning
nums.pop(); // Remove from end
nums.shift(); // Remove from beginning
nums.splice(2, 1); // Remove at index 2
nums.slice(1, 4); // Subarray [1, 4)
nums.concat([7, 8]); // Merge arrays
// Array methods
nums.length; // 5
nums.indexOf(3); // 2
nums.includes(3); // true
nums.join(', '); // "1, 2, 3, 4, 5"
nums.reverse(); // Reverse in-place
nums.sort(); // Sort in-place (lexicographic)
nums.sort((a, b) => a - b); // Numeric sort
// Higher-order functions
nums.map(x => x * 2); // [2, 4, 6, 8, 10]
nums.filter(x => x % 2 === 0); // [2, 4]
nums.reduce((sum, x) => sum + x, 0); // 15
nums.forEach(x => console.log(x));
nums.find(x => x > 3); // 4
nums.findIndex(x => x > 3); // 3
nums.some(x => x > 3); // true
nums.every(x => x > 0); // trueObjects
// Creation
const person = {
name: "Alice",
age: 30,
city: "NYC"
};
// Or
const person = new Object();
person.name = "Alice";
// Access
person.name; // "Alice"
person['name']; // "Alice"
person.name || 'Unknown'; // Default value
// Modification
person.city = 'SF'; // Update
person.email = 'a@example.com'; // Add
delete person.age; // Remove
// Methods
Object.keys(person); // ['name', 'age', 'city']
Object.values(person); // ['Alice', 30, 'NYC']
Object.entries(person); // [['name', 'Alice'], ...]
// Iteration
for (const key in person) {
console.log(key, person[key]);
}
for (const [key, value] of Object.entries(person)) {
console.log(key, value);
}
// Destructuring
const {name, age} = person;Maps & Sets
// Map (key-value pairs, any type as key)
const map = new Map();
map.set('name', 'Alice');
map.set(1, 'one');
map.get('name'); // "Alice"
map.has('name'); // true
map.delete('name');
map.size; // 1
// Set (unique values)
const set = new Set([1, 2, 3, 3, 3]);
set.add(4);
set.has(3); // true
set.delete(2);
set.size; // 3
// Iteration
for (const value of set) {
console.log(value);
}---
Control Flow
If-Else
const x = 10;
if (x > 0) {
console.log("Positive");
} else if (x < 0) {
console.log("Negative");
} else {
console.log("Zero");
}
// Ternary
const result = x > 0 ? "Positive" : "Non-positive";
// Nullish coalescing (ES2020)
const value = null ?? "default"; // "default"
const value2 = 0 ?? "default"; // 0 (not null/undefined)Loops
// For loop
for (let i = 0; i < 5; i++) {
console.log(i);
}
// For-of (values)
for (const item of [1, 2, 3]) {
console.log(item);
}
// For-in (keys/indices)
for (const key in {a: 1, b: 2}) {
console.log(key);
}
// While
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
// Do-while
let j = 0;
do {
console.log(j);
j++;
} while (j < 5);
// Break and continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // Skip 3
if (i === 8) break; // Stop at 8
console.log(i);
}---
Functions
Function Declarations
// Regular function
function greet(name) {
return `Hello, ${name}`;
}
// Function expression
const greet = function(name) {
return `Hello, ${name}`;
};
// Arrow function (ES6)
const greet = (name) => {
return `Hello, ${name}`;
};
// Concise arrow function
const greet = name => `Hello, ${name}`;
const add = (a, b) => a + b;
// Default parameters
function greet(name = "World") {
return `Hello, ${name}`;
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
// Destructuring parameters
function greet({name, age}) {
return `${name} is ${age}`;
}
greet({name: "Alice", age: 30});Arrow Functions vs Regular
// 'this' binding difference
const obj = {
name: "Alice",
// Regular function - 'this' is obj
greet: function() {
console.log(this.name);
},
// Arrow function - 'this' is lexical
greetArrow: () => {
console.log(this.name); // undefined
}
};---
Object-Oriented Programming
Classes (ES6)
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get birthYear() {
return new Date().getFullYear() - this.age;
}
// Setter
set birthYear(year) {
this.age = new Date().getFullYear() - year;
}
// Static method
static species() {
return "Homo sapiens";
}
}
// Usage
const person = new Person("Alice", 30);
console.log(person.greet());
console.log(person.birthYear);
console.log(Person.species());Inheritance
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}
speak() {
return `${this.name} barks!`;
}
}
const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.speak()); // Buddy barks!Prototypes (Pre-ES6 style)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
const person = new Person("Alice", 30);---
Asynchronous JavaScript
Callbacks
function fetchData(callback) {
setTimeout(() => {
callback('Data loaded');
}, 1000);
}
fetchData((data) => {
console.log(data);
});Promises
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('Success!');
} else {
reject('Error!');
}
}, 1000);
});
// Using promises
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('Done'));
// Promise chaining
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));Async/Await (ES2017)
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}
// Usage
fetchData().then(data => console.log(data));
// Or in async context
const data = await fetchData();---
Error Handling
// Try-catch
try {
const result = riskyOperation();
} catch (error) {
console.error('Error:', error.message);
} finally {
console.log('Cleanup');
}
// Throwing errors
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
}
// Custom errors
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
throw new ValidationError('Invalid input');---
Modern JavaScript Features
Destructuring
// Array destructuring
const [a, b, c] = [1, 2, 3];
const [first, ...rest] = [1, 2, 3, 4, 5];
// Object destructuring
const {name, age} = {name: 'Alice', age: 30};
const {name: userName, age: userAge} = person;
// Function parameters
function greet({name, age = 18}) {
console.log(`${name} is ${age}`);
}Spread Operator
// Array spread
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// Object spread
const obj1 = {a: 1, b: 2};
const obj2 = {...obj1, c: 3}; // {a: 1, b: 2, c: 3}
// Function arguments
const numbers = [1, 2, 3];
Math.max(...numbers); // 3Optional Chaining (ES2020)
const user = {
name: "Alice",
address: {
city: "NYC"
}
};
// Safe access
user.address?.city; // "NYC"
user.contact?.email; // undefined (no error)
user.greet?.(); // undefined (method doesn't exist)Nullish Coalescing (ES2020)
const value = null ?? "default"; // "default"
const value2 = 0 ?? "default"; // 0
const value3 = "" ?? "default"; // ""---
Common Patterns
Array Manipulation
// Remove duplicates
const unique = [...new Set([1, 2, 2, 3, 3, 4])]; // [1, 2, 3, 4]
// Flatten array
const nested = [1, [2, 3], [4, [5, 6]]];
const flat = nested.flat(2); // [1, 2, 3, 4, 5, 6]
// Group by
const people = [
{name: 'Alice', age: 30},
{name: 'Bob', age: 25},
{name: 'Charlie', age: 30}
];
const grouped = people.reduce((acc, person) => {
(acc[person.age] = acc[person.age] || []).push(person);
return acc;
}, {});Object Manipulation
// Merge objects
const merged = {...obj1, ...obj2};
const merged2 = Object.assign({}, obj1, obj2);
// Clone object (shallow)
const clone = {...original};
// Clone object (deep)
const deepClone = JSON.parse(JSON.stringify(original));
// Pick properties
const {name, age, ...rest} = person;Function Composition
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const composed = compose(multiply2, add5);
composed(10); // (10 + 5) * 2 = 30---
Common Gotchas
1. == vs ===
// AVOID ==
0 == false; // true
"" == false; // true
null == undefined; // true
// USE ===
0 === false; // false
"" === false; // false
null === undefined; // false2. var vs let/const
// var has function scope (problem!)
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Prints: 3, 3, 3 (unexpected!)
// let has block scope (correct)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Prints: 0, 1, 23. this Binding
const obj = {
name: "Alice",
greet: function() {
// Regular function - 'this' is obj
console.log(this.name);
setTimeout(function() {
// 'this' is undefined/window!
console.log(this.name); // undefined
}, 100);
// Fix with arrow function
setTimeout(() => {
console.log(this.name); // "Alice"
}, 100);
}
};4. Array/Object Reference
// Arrays and objects are passed by reference
const arr1 = [1, 2, 3];
const arr2 = arr1; // Same reference!
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4]
// Clone to avoid
const arr3 = [...arr1]; // New array---
Best Practices
1. Use const by default
// Good
const PI = 3.14159;
const user = {name: "Alice"};
// Use let only when reassignment needed
let counter = 0;
counter++;2. Use === instead of ==
// Always use strict equality
if (value === 0) { }
if (str === "") { }3. Use arrow functions for callbacks
// Good
arr.map(x => x * 2);
arr.filter(x => x > 0);
// Avoid
arr.map(function(x) { return x * 2; });4. Use template literals
// Good
const message = `Hello, ${name}!`;
// Avoid
const message = "Hello, " + name + "!";5. Use destructuring
// Good
const {name, age} = person;
const [first, second] = arr;
// Avoid
const name = person.name;
const age = person.age;---
ES6+ Features Summary
- let/const: Block-scoped variables
- Arrow functions: Concise syntax, lexical this
- Template literals: String interpolation
- Destructuring: Extract values from arrays/objects
- Spread/Rest: ... operator
- Classes: OOP syntax sugar
- Promises: Async handling
- async/await: Cleaner async code
- Modules: import/export
- Optional chaining: ?. operator
- Nullish coalescing: ?? operator
---
Common Use Cases
Array Methods Chain
const result = users
.filter(user => user.active)
.map(user => user.name)
.sort()
.join(', ');Fetch API
async function getUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('User not found');
return await response.json();
} catch (error) {
console.error(error);
}
}Event Handling
button.addEventListener('click', (event) => {
event.preventDefault();
console.log('Clicked!');
});Python Quick Reference
Basic Syntax
Variables & Types
# Dynamic typing
x = 5 # int
y = 3.14 # float
name = "Alice" # str
is_valid = True # bool
# Type hints (optional, Python 3.5+)
def greet(name: str) -> str:
return f"Hello, {name}"
# Multiple assignment
a, b, c = 1, 2, 3
x = y = z = 0Strings
# String creation
s = "hello"
s = 'hello'
s = """multi
line"""
# F-strings (Python 3.6+)
name = "Alice"
age = 30
message = f"{name} is {age} years old"
# Common methods
s.upper() # "HELLO"
s.lower() # "hello"
s.strip() # Remove whitespace
s.split(',') # Split into list
s.replace('h', 'H') # "Hello"
s.startswith('he') # True
s.endswith('lo') # True
s.find('ll') # 2 (index, -1 if not found)
# Slicing
s[0] # 'h'
s[-1] # 'o'
s[1:4] # 'ell'
s[::-1] # 'olleh' (reverse)Lists
# Creation
nums = [1, 2, 3, 4, 5]
mixed = [1, "hello", True, 3.14]
# Common operations
nums.append(6) # Add to end
nums.insert(0, 0) # Insert at index
nums.remove(3) # Remove first occurrence
nums.pop() # Remove and return last
nums.pop(0) # Remove and return at index
nums.extend([7, 8]) # Add multiple elements
len(nums) # Length
nums.sort() # Sort in-place
sorted(nums) # Return sorted copy
nums.reverse() # Reverse in-place
nums[::-1] # Return reversed copy
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]Dictionaries
# Creation
person = {'name': 'Alice', 'age': 30}
person = dict(name='Alice', age=30)
# Access
name = person['name'] # KeyError if not exists
name = person.get('name') # None if not exists
name = person.get('name', 'Unknown') # Default value
# Modification
person['city'] = 'NYC' # Add/update
del person['age'] # Remove
age = person.pop('age', 0) # Remove and return
# Iteration
for key in person:
print(key, person[key])
for key, value in person.items():
print(key, value)
# Dict comprehension
squares = {x: x**2 for x in range(5)}Sets
# Creation
s = {1, 2, 3, 4, 5}
s = set([1, 2, 3, 3, 3]) # {1, 2, 3}
# Operations
s.add(6) # Add element
s.remove(3) # Remove (KeyError if not exists)
s.discard(3) # Remove (no error)
s.union({4, 5, 6}) # {1, 2, 3, 4, 5, 6}
s.intersection({3, 4}) # {3, 4}
s.difference({3, 4}) # {1, 2, 5}---
Control Flow
If-Elif-Else
x = 10
if x > 0:
print("Positive")
elif x < 0:
print("Negative")
else:
print("Zero")
# Ternary
result = "Positive" if x > 0 else "Non-positive"Loops
# For loop
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(2, 10, 2): # 2, 4, 6, 8
print(i)
for item in [1, 2, 3]:
print(item)
# Enumerate (index + value)
for i, val in enumerate(['a', 'b', 'c']):
print(f"{i}: {val}")
# While loop
i = 0
while i < 5:
print(i)
i += 1
# Break and continue
for i in range(10):
if i == 3:
continue # Skip 3
if i == 8:
break # Stop at 8
print(i)---
Functions
Basic Functions
def greet(name):
return f"Hello, {name}"
# Default arguments
def greet(name="World"):
return f"Hello, {name}"
# Multiple return values
def divide(a, b):
return a // b, a % b # Returns tuple
quotient, remainder = divide(10, 3)
# *args and **kwargs
def print_all(*args):
for arg in args:
print(arg)
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_all(1, 2, 3)
print_info(name="Alice", age=30)Lambda Functions
# Anonymous function
square = lambda x: x ** 2
add = lambda x, y: x + y
# Common with map, filter, sorted
nums = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x**2, nums))
evens = list(filter(lambda x: x % 2 == 0, nums))
sorted_tuples = sorted([(1, 'c'), (2, 'a')], key=lambda x: x[1])---
Object-Oriented Programming
Classes
class Person:
# Class variable
species = "Homo sapiens"
def __init__(self, name, age):
# Instance variables
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}"
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
def __repr__(self):
return f"Person('{self.name}', {self.age})"
# Usage
p = Person("Alice", 30)
print(p.greet())
print(p) # Uses __str__Inheritance
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
print(dog.speak()) # Buddy says Woof!Properties
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
# Usage
c = Circle(5)
print(c.area) # 78.53975
c.radius = 10 # Uses setterSpecial Methods (Dunder Methods)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __len__(self):
return 2
def __getitem__(self, index):
return [self.x, self.y][index]
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # Uses __add__
print(v3) # Uses __str__---
File I/O
# Reading
with open('file.txt', 'r') as f:
content = f.read() # Read entire file
# or
lines = f.readlines() # List of lines
# or
for line in f: # Iterate line by line
print(line.strip())
# Writing
with open('file.txt', 'w') as f:
f.write("Hello\n")
f.writelines(["Line 1\n", "Line 2\n"])
# Appending
with open('file.txt', 'a') as f:
f.write("New line\n")
# JSON
import json
# Write JSON
data = {'name': 'Alice', 'age': 30}
with open('data.json', 'w') as f:
json.dump(data, f, indent=2)
# Read JSON
with open('data.json', 'r') as f:
data = json.load(f)---
Error Handling
# Try-except
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
except Exception as e:
print(f"Error: {e}")
else:
print("No errors") # Runs if no exception
finally:
print("Always runs")
# Raising exceptions
def divide(a, b):
if b == 0:
raise ValueError("Divisor cannot be zero")
return a / b
# Custom exceptions
class InvalidAgeError(Exception):
pass
def set_age(age):
if age < 0:
raise InvalidAgeError("Age cannot be negative")---
Common Libraries
Collections
from collections import Counter, defaultdict, deque
# Counter
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
count = Counter(words)
print(count['apple']) # 3
print(count.most_common(2)) # [('apple', 3), ('banana', 2)]
# defaultdict
d = defaultdict(list)
d['key'].append(1) # No KeyError
# deque (double-ended queue)
q = deque([1, 2, 3])
q.append(4) # Add to right
q.appendleft(0) # Add to left
q.pop() # Remove from right
q.popleft() # Remove from leftItertools
from itertools import combinations, permutations, product
# Combinations
list(combinations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 3)]
# Permutations
list(permutations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 1), ...]
# Cartesian product
list(product([1, 2], ['a', 'b'])) # [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]Functools
from functools import lru_cache, reduce
# Memoization
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Reduce
from functools import reduce
product = reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 24---
List/Dict/Set Comprehensions
# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]
nested = [[i for i in range(3)] for j in range(3)]
# Dict comprehension
squares_dict = {x: x**2 for x in range(5)}
filtered = {k: v for k, v in squares_dict.items() if v > 5}
# Set comprehension
unique_lengths = {len(word) for word in ['apple', 'banana', 'kiwi']}
# Generator expression (memory efficient)
sum_of_squares = sum(x**2 for x in range(1000000))---
Useful Built-in Functions
# any, all
any([False, True, False]) # True (at least one True)
all([True, True, True]) # True (all True)
# zip
names = ['Alice', 'Bob']
ages = [30, 25]
for name, age in zip(names, ages):
print(f"{name}: {age}")
# enumerate
for i, val in enumerate(['a', 'b', 'c']):
print(f"{i}: {val}")
# map, filter
nums = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, nums))
evens = list(filter(lambda x: x % 2 == 0, nums))
# sorted, reversed
sorted([3, 1, 2]) # [1, 2, 3]
sorted([3, 1, 2], reverse=True) # [3, 2, 1]
list(reversed([1, 2, 3])) # [3, 2, 1]
# max, min, sum
max([1, 5, 3]) # 5
min([1, 5, 3]) # 1
sum([1, 2, 3]) # 6---
Common Idioms
Swap Variables
a, b = b, aTernary Operator
result = "Even" if x % 2 == 0 else "Odd"Default Dict Value
value = my_dict.get('key', default_value)Enumerate with Start
for i, val in enumerate(items, start=1):
print(f"{i}. {val}")Unpacking
first, *middle, last = [1, 2, 3, 4, 5]
# first=1, middle=[2,3,4], last=5Context Managers
with open('file.txt') as f:
data = f.read()
# File automatically closed---
Best Practices
1. PEP 8 Style Guide
# Use 4 spaces for indentation
# Use snake_case for variables and functions
# Use PascalCase for classes
# Constants in UPPERCASE
def calculate_total(items):
DISCOUNT_RATE = 0.1
total = sum(items)
return total * (1 - DISCOUNT_RATE)2. List Comprehension vs Loop
# Prefer comprehension for simple transformations
squares = [x**2 for x in range(10)]
# Use loop for complex logic
results = []
for x in range(10):
if x % 2 == 0:
result = process_even(x)
else:
result = process_odd(x)
results.append(result)3. Use is for None, == for Values
if value is None: # Correct
if value == None: # Works but not idiomatic4. EAFP vs LBYL
# Easier to Ask Forgiveness than Permission (Pythonic)
try:
value = my_dict['key']
except KeyError:
value = default
# Look Before You Leap (less Pythonic)
if 'key' in my_dict:
value = my_dict['key']
else:
value = default---
Common Gotchas
1. Mutable Default Arguments
# WRONG
def append_to(element, lst=[]):
lst.append(element)
return lst
# Calls share same list!
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] - unexpected!
# CORRECT
def append_to(element, lst=None):
if lst is None:
lst = []
lst.append(element)
return lst2. Late Binding Closures
# WRONG
funcs = [lambda: i for i in range(5)]
print([f() for f in funcs]) # [4, 4, 4, 4, 4]
# CORRECT
funcs = [lambda i=i: i for i in range(5)]
print([f() for f in funcs]) # [0, 1, 2, 3, 4]3. Modifying List While Iterating
# WRONG
lst = [1, 2, 3, 4, 5]
for item in lst:
if item % 2 == 0:
lst.remove(item) # Can skip elements
# CORRECT
lst = [item for item in lst if item % 2 != 0]---
Python 3.10+ Features
Structural Pattern Matching
def process_command(command):
match command.split():
case ["quit"]:
return "Quitting"
case ["load", filename]:
return f"Loading {filename}"
case ["save", filename]:
return f"Saving {filename}"
case _:
return "Unknown command"Union Types
def greet(name: str | None = None) -> str:
if name is None:
return "Hello, stranger"
return f"Hello, {name}"Learning Log
This file tracks your progress and learning journey with Code Mentor. Your progress is automatically saved after each session.
Session History
Your sessions will be logged below as you learn...
---
Mastered Topics
Topics you've demonstrated proficiency in will appear here...
Areas for Review
Topics that need more practice will be tracked here...
Goals
Your learning goals will be tracked here...
---
Last Updated: Initial setup Total Sessions: 0