Skip to content

Commit 633dadc

Browse files
sounmindclaude
andcommitted
feat: reduce 문제 풀이 현황 to Blind Top 75's 10 categories
LeetCode 기준 20+개 카테고리가 나열되며 표가 길어지던 문제를 Blind Top 75 큐레이션의 10개 카테고리로 축소한다. - utils/blindCategories.js: 75개 문제별 Blind 카테고리 매핑 테이블 - handlers/learning-status.js: buildCategoryProgress 가 Blind 10개만 집계 Closes #34 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d5fa7a4 commit 633dadc

2 files changed

Lines changed: 156 additions & 7 deletions

File tree

handlers/learning-status.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,37 @@ import {
1515
formatLearningStatusComment,
1616
upsertLearningStatusComment,
1717
} from "../utils/learningComment.js";
18+
import {
19+
BLIND_CATEGORY_ORDER,
20+
getBlindCategories,
21+
} from "../utils/blindCategories.js";
1822

1923
const MAX_FILE_SIZE = 15000; // 15K 문자 제한 (OpenAI 토큰 안전장치)
2024

2125
/**
22-
* 카테고리별로 누적 풀이 진행도를 계산한다.
26+
* Blind Top 75 카테고리별로 누적 풀이 진행도를 계산한다.
27+
*
28+
* `problem-categories.json` 의 LeetCode 세부 카테고리(20+개) 대신
29+
* Blind 10 카테고리만 표로 노출한다 (이슈 #34).
2330
*
2431
* @param {object} categories - problem-categories.json 전체 오브젝트
2532
* @param {string[]} solvedProblems - 사용자가 풀이한 문제 이름 배열
2633
* @returns {Array<{ category: string, solved: number, total: number, difficulties: string }>}
2734
*/
2835
function buildCategoryProgress(categories, solvedProblems) {
2936
const solvedSet = new Set(solvedProblems);
30-
const categoryMap = new Map();
37+
const categoryMap = new Map(
38+
BLIND_CATEGORY_ORDER.map((cat) => [
39+
cat,
40+
{ total: 0, solved: 0, solvedDifficulties: [] },
41+
])
42+
);
3143

3244
for (const [problemName, info] of Object.entries(categories)) {
33-
for (const cat of info.categories) {
34-
if (!categoryMap.has(cat)) {
35-
categoryMap.set(cat, { total: 0, solved: 0, solvedDifficulties: [] });
36-
}
45+
const blindCategories = getBlindCategories(problemName);
46+
for (const cat of blindCategories) {
3747
const entry = categoryMap.get(cat);
48+
if (!entry) continue;
3849
entry.total++;
3950
if (solvedSet.has(problemName)) {
4051
entry.solved++;
@@ -48,7 +59,9 @@ function buildCategoryProgress(categories, solvedProblems) {
4859
const ratioA = a[1].total > 0 ? a[1].solved / a[1].total : 0;
4960
const ratioB = b[1].total > 0 ? b[1].solved / b[1].total : 0;
5061
if (ratioB !== ratioA) return ratioB - ratioA;
51-
return a[0].localeCompare(b[0]);
62+
return (
63+
BLIND_CATEGORY_ORDER.indexOf(a[0]) - BLIND_CATEGORY_ORDER.indexOf(b[0])
64+
);
5265
})
5366
.map(([cat, data]) => {
5467
const diffCounts = {};

utils/blindCategories.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Blind Top 75 카테고리 매핑
3+
*
4+
* "문제 풀이 현황" 테이블에는 LeetCode 홈페이지 기준 20+개 카테고리 대신
5+
* Blind Top 75 큐레이션의 10개 카테고리만 노출한다.
6+
*
7+
* 출처: https://www.teamblind.com/post/new-year-gift-curated-list-of-top-75-leetcode-questions-to-save-your-time-oam1oreu
8+
* 관련 이슈: https://github.com/DaleStudy/github/issues/34
9+
*/
10+
11+
export const BLIND_CATEGORY_ORDER = [
12+
"Array",
13+
"Binary",
14+
"Dynamic Programming",
15+
"Graph",
16+
"Interval",
17+
"Linked List",
18+
"Matrix",
19+
"String",
20+
"Tree",
21+
"Heap",
22+
];
23+
24+
/**
25+
* 문제 슬러그 → Blind 카테고리 배열.
26+
*
27+
* 한 문제가 여러 Blind 카테고리에 속할 수 있다 (예: merge-k-sorted-lists → Linked List + Heap).
28+
* `problem-categories.json`에 있지만 이 맵에 없는 문제는 Blind 75 밖이므로 집계에서 제외된다.
29+
*/
30+
export const BLIND_PROBLEM_MAP = {
31+
// Array (10)
32+
"two-sum": ["Array"],
33+
"best-time-to-buy-and-sell-stock": ["Array"],
34+
"contains-duplicate": ["Array"],
35+
"product-of-array-except-self": ["Array"],
36+
"maximum-subarray": ["Array"],
37+
"maximum-product-subarray": ["Array"],
38+
"find-minimum-in-rotated-sorted-array": ["Array"],
39+
"search-in-rotated-sorted-array": ["Array"],
40+
"3sum": ["Array"],
41+
"container-with-most-water": ["Array"],
42+
43+
// Binary (5)
44+
"sum-of-two-integers": ["Binary"],
45+
"number-of-1-bits": ["Binary"],
46+
"counting-bits": ["Binary"],
47+
"missing-number": ["Binary"],
48+
"reverse-bits": ["Binary"],
49+
50+
// Dynamic Programming (11)
51+
"climbing-stairs": ["Dynamic Programming"],
52+
"coin-change": ["Dynamic Programming"],
53+
"longest-increasing-subsequence": ["Dynamic Programming"],
54+
"longest-common-subsequence": ["Dynamic Programming"],
55+
"word-break": ["Dynamic Programming"],
56+
"combination-sum": ["Dynamic Programming"],
57+
"house-robber": ["Dynamic Programming"],
58+
"house-robber-ii": ["Dynamic Programming"],
59+
"decode-ways": ["Dynamic Programming"],
60+
"unique-paths": ["Dynamic Programming"],
61+
"jump-game": ["Dynamic Programming"],
62+
63+
// Graph (8)
64+
"clone-graph": ["Graph"],
65+
"course-schedule": ["Graph"],
66+
"pacific-atlantic-water-flow": ["Graph"],
67+
"number-of-islands": ["Graph"],
68+
"longest-consecutive-sequence": ["Graph"],
69+
"alien-dictionary": ["Graph"],
70+
"graph-valid-tree": ["Graph"],
71+
"number-of-connected-components-in-an-undirected-graph": ["Graph"],
72+
73+
// Interval (5)
74+
"insert-interval": ["Interval"],
75+
"merge-intervals": ["Interval"],
76+
"non-overlapping-intervals": ["Interval"],
77+
"meeting-rooms": ["Interval"],
78+
"meeting-rooms-ii": ["Interval"],
79+
80+
// Linked List (6; merge-k-sorted-lists 는 Heap 과 중복)
81+
"reverse-linked-list": ["Linked List"],
82+
"linked-list-cycle": ["Linked List"],
83+
"merge-two-sorted-lists": ["Linked List"],
84+
"merge-k-sorted-lists": ["Linked List", "Heap"],
85+
"remove-nth-node-from-end-of-list": ["Linked List"],
86+
"reorder-list": ["Linked List"],
87+
88+
// Matrix (4)
89+
"set-matrix-zeroes": ["Matrix"],
90+
"spiral-matrix": ["Matrix"],
91+
"rotate-image": ["Matrix"],
92+
"word-search": ["Matrix"],
93+
94+
// String (10)
95+
"longest-substring-without-repeating-characters": ["String"],
96+
"longest-repeating-character-replacement": ["String"],
97+
"minimum-window-substring": ["String"],
98+
"valid-anagram": ["String"],
99+
"group-anagrams": ["String"],
100+
"valid-parentheses": ["String"],
101+
"valid-palindrome": ["String"],
102+
"longest-palindromic-substring": ["String"],
103+
"palindromic-substrings": ["String"],
104+
"encode-and-decode-strings": ["String"],
105+
106+
// Tree (14)
107+
"maximum-depth-of-binary-tree": ["Tree"],
108+
"same-tree": ["Tree"],
109+
"invert-binary-tree": ["Tree"],
110+
"binary-tree-maximum-path-sum": ["Tree"],
111+
"binary-tree-level-order-traversal": ["Tree"],
112+
"serialize-and-deserialize-binary-tree": ["Tree"],
113+
"subtree-of-another-tree": ["Tree"],
114+
"construct-binary-tree-from-preorder-and-inorder-traversal": ["Tree"],
115+
"validate-binary-search-tree": ["Tree"],
116+
"kth-smallest-element-in-a-bst": ["Tree"],
117+
"lowest-common-ancestor-of-a-binary-search-tree": ["Tree"],
118+
"implement-trie-prefix-tree": ["Tree"],
119+
"design-add-and-search-words-data-structure": ["Tree"],
120+
"word-search-ii": ["Tree"],
121+
122+
// Heap (3; merge-k-sorted-lists 는 Linked List 와 중복)
123+
"top-k-frequent-elements": ["Heap"],
124+
"find-median-from-data-stream": ["Heap"],
125+
};
126+
127+
/**
128+
* 문제 슬러그에 해당하는 Blind 카테고리 배열을 반환한다.
129+
* Blind 75 밖의 문제는 빈 배열을 반환한다.
130+
*
131+
* @param {string} problemName
132+
* @returns {string[]}
133+
*/
134+
export function getBlindCategories(problemName) {
135+
return BLIND_PROBLEM_MAP[problemName] ?? [];
136+
}

0 commit comments

Comments
 (0)