Skip to content

Commit 46423f6

Browse files
committed
interactive mode, random from list, detailed info
1 parent 01d38f2 commit 46423f6

File tree

3 files changed

+105
-36
lines changed

3 files changed

+105
-36
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
# leetcode-problem-picker
22

33
## Purpose:
4-
This project was a response to the reoccurring question "Which leetcode problem should I do next?"
4+
This project was a response to my reoccurring question *"Which leetcode problem should I do next?"*
55

66
Over time I found that the answer varies, depending on progress, needs, and goals:
7-
1. Topic Focus: Narrow down to one or a few subjects e.g. "only problems related to graphs, tries, and DP".
8-
2. Filtered Lists: Which problems were frequently asked by companies I’m interested in? Also lists like Blind 75
9-
3. Level Up (WIP): Deduces user's "skill range" for each topic in order to challenge (but not discourage).
10-
4. Weighted random (TODO): Weighted towards high like count, high like-dislike ratio, etc.
7+
1. **Topic Focus**: Narrow down to 1+ subjects e.g. "only do problems related to graphs or DP".
8+
2. **Filtered Lists**: Honing in on questions asked by desired companies. Applies to lists like Blind's Curated 75.
9+
3. **Level Up** (WIP): Deduces user's "skill range" for each topic in order to challenge (but not discourage).
10+
4. **Weighted random** (TODO): Weighted towards questions with high like count, greater like/dislike ratio, etc.
1111

1212
# State of leetcode-problem-picker
1313
![WIP](https://i.ibb.co/BcSX334/WIP.png)
1414

15-
I wish to share and help others, but what I currently have and use is a product of months of ideas. It is neither presentable nor ready for public consumption. What you see now is just a bunch of stubs.
15+
I wish to share and help others, but what I currently have and use is a product of months of ideas. It is neither presentable nor ready for public consumption. In fact, what you see now is just a bunch of stubs.
1616

17-
**If you are interested in this idea, please star this repo** and give me a week or so to clean it up.
17+
**If you are interested in this idea, please star this repo** and allow me a week or so to clean up the code and get the first two parts ready.
1818

1919
## Setup:
2020
```git clone https://github.com/evliang/leetcode-problem-picker.git```

completed.csv

Whitespace-only changes.

lcpp.py

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,40 @@
33
import os
44
from enum import Enum
55
import random
6-
from constants import topics
6+
from constants import topics, blind75
7+
import csv
8+
from collections import defaultdict
9+
from timeit import default_timer as timer
10+
import datetime
711

812
ProblemType = Enum('ProblemType', 'Top Freq Easiest Hardest Common Random')
913

10-
def load_completed():
11-
# completed.csv
14+
def load_completed_list():
15+
completed1 = set()
1216
with open('completed.csv', 'r') as f:
13-
completed1 = f.read().splitlines()
14-
# completed.txt
15-
with open('completed.txt', 'r') as f:
16-
completed2 = f.read().splitlines()
17-
# merge
17+
for line in f.read().splitlines():
18+
try:
19+
completed1.add(int(line.split(',')[0].strip()))
20+
except ValueError:
21+
continue
22+
completed2 = load_problem_nums('completed.txt')
23+
# TODO handle skipped, revisit, refresh lists
24+
return completed1.union(completed2)
1825

19-
# handle skipped, revisit, refresh lists
20-
return set()
26+
def load_problem_nums(file_name):
27+
ret = set()
28+
with open(file_name, 'r') as f:
29+
for num_string in f.read().split(','):
30+
try:
31+
ret.add(int(num_string.strip()))
32+
except ValueError:
33+
continue
34+
return ret
2135

2236
def pick_problems(problem_type=ProblemType.Random, categories=[], companies=[], k=5):
37+
completed = load_completed_list()
38+
skipped_hard = load_problem_nums('skipped.txt')
39+
revisit = load_problem_nums('revisit_later.txt')
2340
problem_set = set(companies) - completed - skipped_hard - revisit
2441
if problem_type==ProblemType.Random:
2542
return random.sample(list(problem_set), k)
@@ -31,27 +48,79 @@ def record_problems():
3148
None
3249

3350
if __name__ == "__main__":
51+
faang_companies = ['amazon', 'apple', 'google', 'netflix', 'facebook']
52+
my_companies = ['adobe', 'microsoft', 'airbnb', 'linkedin', 'tesla', 'twitter', 'hulu', 'redfin', 'snapchat',
53+
'paypal', 'pinterest', 'audible', 'atlassian', 'lyft', 'uber', 'twitch', 'twilio', 'robinhood',
54+
'cruise', 'reddit', 'valve', 'walmart', 'dropbox']
55+
56+
all_problems = {}
57+
easy_set, medium_set, hard_set = set(), set(), set()
58+
# also store in sorted list for binsearch range lookup: https://stackoverflow.com/a/2899190
59+
60+
companies = defaultdict(dict)
61+
problem_to_company = defaultdict(set)
62+
63+
for fi in glob.glob('companies//*.csv'):
64+
(company,duration) = fi[10:-4].rsplit('_')
65+
companies[company][duration] = dict()
66+
with open(fi, 'r') as f:
67+
for line in csv.DictReader(f, fieldnames=('ID', 'Name', 'Acceptance', 'Difficulty', 'Frequency', 'Link')):
68+
problem_num = int(line.pop('ID'))
69+
all_problems[problem_num] = line
70+
companies[company][duration][problem_num] = line.pop('Frequency')
71+
problem_to_company[problem_num].add(company)
72+
73+
#populate company file w/ maximum of 4 lines (sorted). each line is a comma separated list of problem numbers.
74+
# question: does 1yr,2yr and alltime contain 6mo? does 2yr contain 1yr? I think not?
75+
# 6mo
76+
# 1yr
77+
# 2yr
78+
# alltime
3479
if len(sys.argv) == 1:
35-
print(pick_problems(companies=blind_list, k=5))
80+
None
81+
#print(pick_problems(companies=blind_list, k=5))
3682
elif sys.argv[1] == 'interactive':
37-
probs = pick_problems(companies=blind_list, k=5)
38-
print(probs)
83+
problems = pick_problems(companies=blind75, k=5)
3984

4085
companies = faang_companies + my_companies # all companies?
4186
d = {}
42-
for company in companies:
43-
d[company] = get_the_question_set(get_frequencies([company]))
44-
45-
while True:
46-
inp = input('leetcode ID? ')
47-
if inp == '' or inp.startswith('q'):
48-
break
49-
if inp.isdigit():
50-
ret = []
51-
# problem # =>
52-
# details: name, difficulty, accept rate. need to set up dict. can just grab one from company.
53-
# [company (%)] dict from problem# => companyname => %
54-
for company in d:
55-
if int(inp) in d[company]:
56-
ret.append(company)
57-
print(f'\t {len(ret)}: ' + ', '.join(ret))
87+
#for company in companies:
88+
# d[company] = get_the_question_set(get_frequencies([company]))
89+
90+
print("Other valid inputs: info, hint, easy, hard, quit, help")
91+
92+
for (idx,leetcode_id) in enumerate(problems):
93+
problem = all_problems[leetcode_id]
94+
msg = "First problem" if idx == 0 else "Last problem" if idx == len(problems)-1 else "Next up"
95+
print(f"\n\n{msg}:\n{leetcode_id}: {problem['Name']} {problem['Link']}")
96+
start_time = timer()
97+
while True:
98+
inp = input('When completed, enter: y/n,[num_errs],[time]\n')
99+
if inp == '' or inp.startswith('q'):
100+
break # TODO
101+
if inp == 'hint':
102+
ret = []
103+
# problem # =>
104+
# details: name, difficulty, accept rate. need to set up dict. can just grab one from company.
105+
# [company (%)] dict from problem# => companyname => %
106+
for company in d:
107+
if int(inp) in d[company]:
108+
ret.append(company)
109+
print(f'\t {len(ret)}: ' + ', '.join(ret))
110+
elif inp == 'info':
111+
company_list = problem_to_company[leetcode_id]
112+
difficulty_string = "medium difficulty" if problem['Difficulty'] == "Medium" else "considered easy" if problem['Difficulty'] == 'Easy' else problem['Difficulty']
113+
print(f"{leetcode_id} {problem['Name']} is {difficulty_string}: {problem['Acceptance']} of submissions pass")
114+
print(f"{len(company_list)} have asked this question: {', '.join(company_list)}")
115+
elif inp.startswith('y') or inp.startswith('n'):
116+
end_time = timer()
117+
entry = inp.split(',')
118+
was_solved = 'yes' if entry[0].startswith('y') else 'no'
119+
num_errs = entry[1] if len(entry) > 1 else '0'
120+
time = entry[2] if len(entry) > 2 else (start_time - end_time)//60
121+
with open('completed.csv', 'a') as f:
122+
f.write(f'{leetcode_id},{was_solved},{num_errs},{time},{datetime.datetime.now():%Y-%m-%d}')
123+
# log entry into csv
124+
break
125+
else:
126+
print("Invalid input. Type help for options")

0 commit comments

Comments
 (0)