neetcode-gh / leetcode

Leetcode solutions
MIT License
5.65k stars 2.32k forks source link

Feature: Filter problems based on difficulty #787

Open ankurchaudhary627 opened 2 years ago

ankurchaudhary627 commented 2 years ago

It would be a nice feature to have where user is able to filter the problems based on difficulties - Easy, Medium and Hard.

I can work on this. Let me know your thoughts @neetcode-gh

ankurchaudhary627 commented 2 years ago

@neetcode-gh where is the website code? Is it public?

vfonic commented 2 years ago

I'm also interested in expanding the functionalities of the website and willing to put some work into it. I already added two small scripts through a browser extension: 1) Open last/previously opened accordion on page load 2) Add two buttons: "Open random task", "Open random unsolved task"

Next up I'm planning on adding sorting by difficulty and hiding solved tasks.

nimpadjio commented 2 years ago

i'm interested in solving this issue

vfonic commented 2 years ago

Here's the full script that I wrote to make the site easier to use for me:

I have too much free time ```js // ==UserScript== // @name Neetcode // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @match https://neetcode.io/ // @icon https://www.google.com/s2/favicons?sz=64&domain=neetcode.io // @grant none // ==/UserScript== (function() { 'use strict'; const insertFilter = (filterEl) => { const filterContainerEl = document.querySelector('.filter-container.button.is-rounded') filterContainerEl.insertBefore(filterEl, filterContainerEl.querySelector('* > .spacer')) } const vfOpenAccordionSelectorKey = 'vfOpenAccordionSelector' const initUI = () => { const openAccordionSelector = localStorage.getItem(vfOpenAccordionSelectorKey) || 'app-pattern-table .accordion' const openAccordionEl = document.querySelector(openAccordionSelector) // wait until the UI is loaded const isListView = document.getElementById('switchListView').checked const isTaskListLoaded = document.querySelector('.ng-star-inserted td:nth-child(2) a, .table.is-bordered.is-fullwidth tr td:nth-child(2) a') if (!openAccordionEl && !isListView && !isTaskListLoaded) { setTimeout(initUI, 100); return; } const initOpenLastOpenedAccordion = () => { // open previously last opened accordion (if we're in accordion view) openAccordionEl?.click() // track opening accordions and store the value of last opened in localStorage for restoring later ;[...document.querySelectorAll('app-pattern-table .accordion')].forEach(el => { el.addEventListener('click', (e) => { if (el.classList.contains('active')) { const appPatternTableEl = el.parentNode.parentNode.parentNode const elIndex = [...appPatternTableEl.parentNode.children].indexOf(appPatternTableEl) localStorage.setItem(vfOpenAccordionSelectorKey, `app-pattern-table:nth-child(${elIndex+1}) .accordion`) } }) }) } initOpenLastOpenedAccordion() const addRandomAndRandomUnsolvedTaskButtons = () => { const divEl = document.createElement('div') divEl.innerHTML = ` ` insertFilter(divEl) const clickOn = selector => { const taskEls = [...document.querySelectorAll(selector)] if (taskEls.length > 0) { const idx = Math.floor(Math.random() * taskEls.length) taskEls[idx].click() } } divEl.querySelector('.js-randomTask').addEventListener('click', () => clickOn('.ng-star-inserted td:nth-child(2) a, .table.is-bordered.is-fullwidth tr td:nth-child(2) a')) divEl.querySelector('.js-randomUnsolvedTask').addEventListener('click', () => clickOn('.ng-star-inserted:not(.completed) td:nth-child(2) a, .table.is-bordered.is-fullwidth tr:not(.completed) td:nth-child(2) a')) } addRandomAndRandomUnsolvedTaskButtons() const initSortByDifficulty = () => { const diffEls = [...document.querySelectorAll('th')].filter(el => el.textContent == 'Difficulty') diffEls.forEach(diffEl => { diffEl.addEventListener('click', () => { const tableEl = diffEl.closest('table') const rowEls = tableEl.querySelectorAll('tbody tr') const easyRows = [] const mediumRows = [] const hardRows = [] rowEls.forEach(rowEl => { switch (rowEl.querySelector('td:nth-child(3)').textContent) { case 'Easy': easyRows.push(rowEl) break case 'Medium': mediumRows.push(rowEl) break case 'Hard': hardRows.push(rowEl) break } }) const tbodyEl = tableEl.querySelector('tbody') if (diffEl.classList.contains('sorted-ascending')) { hardRows.reverse().forEach(el => tbodyEl.appendChild(el)) mediumRows.reverse().forEach(el => tbodyEl.appendChild(el)) easyRows.reverse().forEach(el => tbodyEl.appendChild(el)) } else { easyRows.reverse().forEach(el => tbodyEl.appendChild(el)) mediumRows.reverse().forEach(el => tbodyEl.appendChild(el)) hardRows.reverse().forEach(el => tbodyEl.appendChild(el)) } diffEl.classList.toggle('sorted-ascending') }) }) } initSortByDifficulty() const initEscToCloseVideoModal = () => { document.body.addEventListener('keydown', (e) => { const modalEl = document.querySelector('.modal.is-active.dialog-open') if (!modalEl) return; if (e.key !== "Escape") return; modalEl.querySelector('.modal-background').click() }) } initEscToCloseVideoModal() const initHideSolved = () => { const divEl = document.createElement('div') divEl.innerHTML = `
` insertFilter(divEl) divEl.querySelector('input').addEventListener('change', () => document.querySelectorAll('tr.completed').forEach(el => el.hidden = !el.hidden)) } initHideSolved() } document.addEventListener('DOMContentLoaded', () => setTimeout(initUI, 2000)) })(); ```

I hope someone finds this useful.