diff --git a/README.md b/README.md index 51abd25..e85aea5 100644 --- a/README.md +++ b/README.md @@ -1 +1,199 @@ -# javascript-subway-final \ No newline at end of file +# ๐Ÿš‡ ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ ๊ฒฝ๋กœ ์กฐํšŒ ๋ฏธ์…˜ + +- ๋“ฑ๋ก๋œ ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„์—์„œ ๊ฒฝ๋กœ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค. + +> ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ ๊ฒฝ๋กœ ์กฐํšŒ ๋ฏธ์…˜์€ ์šฐ์•„ํ•œ ํ…Œํฌ์ฝ”์Šค ํ”„๋ฆฌ์ฝ”์Šค ์ฝ”๋”ฉํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. +> ์ง€ํ•˜์ฒ  ๋…ธ์„ ๋„ ๋ฏธ์…˜์—์„œ๋Š” ์œ ์ €๊ฐ€ ์ด๋ฏธ ๋“ฑ๋ก๋œ ์ง€ํ•˜์ฒ  ์—ญ๋“ค์„ ํ†ตํ•ด ๊ฒฝ๋กœ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## โœ”๏ธ ์ฃผ์–ด์ง„ ์š”๊ตฌ์‚ฌํ•ญ + +- [์š”๊ตฌ์‚ฌํ•ญ](#์š”๊ตฌ์‚ฌํ•ญ) +- [๊ธฐ๋Šฅ์š”๊ตฌ์‚ฌํ•ญ](#๊ธฐ๋Šฅ-์š”๊ตฌ์‚ฌํ•ญ) +- [ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์š”๊ตฌ์‚ฌํ•ญ](#ํ”„๋กœ๊ทธ๋ž˜๋ฐ-์š”๊ตฌ์‚ฌํ•ญ) +- [๋ฏธ์…˜ ์ €์žฅ์†Œ ๋ฐ ์ง„ํ–‰ ์š”๊ตฌ์‚ฌํ•ญ](#๋ฏธ์…˜-์ €์žฅ์†Œ-๋ฐ-์ง„ํ–‰-์š”๊ตฌ์‚ฌํ•ญ) + +## โœ”๏ธ ์ฃผ์–ด์ง„ ์ฐธ๊ณ ์‚ฌํ•ญ + +- [ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ๊ฒฐ๊ณผ](#ํ”„๋กœ๊ทธ๋žจ-์‹คํ–‰-๊ฒฐ๊ณผ) +- [์ตœ๋‹จ ๊ฒฝ๋กœ](#์ตœ๋‹จ-๊ฒฝ๋กœ-๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) +- [ํžŒํŠธ](#ํžŒํŠธ) + +## โœ”๏ธ ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ ๋ชฉ๋ก + +### ์ „์ฒด + +- ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ(์—ญ, ๋…ธ์„ , ๊ตฌ๊ฐ„ ๋ฐ์ดํ„ฐ)๋ฅผ ์ดˆ๊ธฐ ์„ค์ •ํ•˜๋Š” ๊ธฐ๋Šฅ + - [์ดˆ๊ธฐ ์„ค์ •](#์ดˆ๊ธฐ-์„ค์ •)์„ ์ฐธ๊ณ  +- ์œ ์ €๋กœ๋ถ€ํ„ฐ ์ง€ํ•˜์ฒ  ์ถœ๋ฐœ์—ญ์„ ์ž…๋ ฅ๋ฐ›๋Š” ๊ธฐ๋Šฅ +- ์œ ์ €๋กœ๋ถ€ํ„ฐ ์ง€ํ•˜์ฒ  ๋„์ฐฉ์—ญ์„ ์ž…๋ ฅ๋ฐ›๋Š” ๊ธฐ๋Šฅ + + - ๐Ÿšฆ ์˜ˆ์™ธ์ƒํ™ฉ + - ์œ ์ €๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์—ญ์„ ์ถœ๋ฐœ/๋„์ฐฉ์—ญ์œผ๋กœ ์ž…๋ ฅํ–ˆ์„ ๋•Œ + - ์œ ์ €๊ฐ€ ์ถœ๋ฐœ/๋„์ฐฉ์—ญ์„ 1๊ธ€์ž ์ดํ•˜๋กœ ์ž…๋ ฅํ–ˆ์„ ๋•Œ + - ์œ ์ €๊ฐ€ ์ถœ๋ฐœ/๋„์ฐฉ์—ญ์„ ๊ฐ™๊ฒŒ ์ž…๋ ฅํ–ˆ์„ ใ„ธ๋Œ€ + - ์œ ์ €๊ฐ€ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์€ ์ถœ๋ฐœ/๋„์ฐฉ์—ญ์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ + - ์œ ์ €๊ฐ€ ์•ž๋’ค๋กœ ๊ณต๋ฐฑ์„ ํฌํ•จํ•ด ์ถœ๋ฐœ/๋„์ฐฉ์—ญ์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ + +- ์œ ์ €๊ฐ€ ์„ ํƒํ•œ ์ตœ๋‹จ๊ฑฐ๋ฆฌ/์ตœ์†Œ์‹œ๊ฐ„ ์˜ต์…˜์„ ๋ฐ›๋Š” ๊ธฐ๋Šฅ +- ๊ฒฐ๊ณผ๋ฅผ ํ‘œ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ + +### ์ง€ํ•˜์ฒ  ์ตœ๋‹จ๊ฑฐ๋ฆฌ ๊ด€๋ จ ๊ธฐ๋Šฅ + +### ์ง€ํ•˜์ฒ  ์ตœ์†Œ์‹œ๊ฐ„ ๊ด€๋ จ ๊ธฐ๋Šฅ + +## ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ + +> ํ”„๋ฆฌ์ฝ”์Šค 3์ฃผ์ฐจ ๋ฏธ์…˜์—์„œ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด๋„ ๋ฌด๊ด€ํ•˜๋‹ค. + +### ์ดˆ๊ธฐ ์„ค์ • + +- ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘ ์‹œ ์—ญ, ๋…ธ์„ , ๊ตฌ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐ ์„ค์ • ํ•ด์•ผ ํ•œ๋‹ค. +- ๊ฑฐ๋ฆฌ์™€ ์†Œ์š” ์‹œ๊ฐ„์€ ์–‘์˜ ์ •์ˆ˜์ด๋ฉฐ ๋‹จ์œ„๋Š” km์™€ ๋ถ„์„ ์˜๋ฏธํ•œ๋‹ค. +- ์•„๋ž˜์˜ ์‚ฌ์ „ ๋“ฑ๋ก ์ •๋ณด๋กœ ๋ฐ˜๋“œ์‹œ ์ดˆ๊ธฐ ์„ค์ •์„ ํ•œ๋‹ค. + +``` +1. ์ง€ํ•˜์ฒ ์—ญ์œผ๋กœ ๊ต๋Œ€, ๊ฐ•๋‚จ, ์—ญ์‚ผ, ๋‚จ๋ถ€ํ„ฐ๋ฏธ๋„, ์–‘์žฌ, ์–‘์žฌ์‹œ๋ฏผ์˜์ˆฒ, ๋งค๋ด‰ ์—ญ ์ •๋ณด๊ฐ€ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค. +2. ์ง€ํ•˜์ฒ  ๋…ธ์„ ์œผ๋กœ 2ํ˜ธ์„ , 3ํ˜ธ์„ , ์‹ ๋ถ„๋‹น์„ ์ด ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค. +3. ๋…ธ์„ ์— ์—ญ์ด ์•„๋ž˜์™€ ๊ฐ™์ด ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค.(์™ผ์ชฝ ๋์ด ์ƒํ–‰ ์ข…์ ) + - 2ํ˜ธ์„ : ๊ต๋Œ€ - ( 2km / 3๋ถ„ ) - ๊ฐ•๋‚จ - ( 2km / 3๋ถ„ ) - ์—ญ์‚ผ + - 3ํ˜ธ์„ : ๊ต๋Œ€ - ( 3km / 2๋ถ„ ) - ๋‚จ๋ถ€ํ„ฐ๋ฏธ๋„ - ( 6km / 5๋ถ„ ) - ์–‘์žฌ - ( 1km / 1๋ถ„ ) - ๋งค๋ด‰ + - ์‹ ๋ถ„๋‹น์„ : ๊ฐ•๋‚จ - ( 2km / 8๋ถ„ ) - ์–‘์žฌ - ( 10km / 3๋ถ„ ) - ์–‘์žฌ์‹œ๋ฏผ์˜์ˆฒ +``` + +### ๊ฒฝ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ + + + +- ์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ์„ ์ž…๋ ฅ๋ฐ›์•„ ๊ฒฝ๋กœ๋ฅผ ์กฐํšŒํ•œ๋‹ค. +- ๊ฒฝ๋กœ ์กฐํšŒ ์‹œ ์ด ๊ฑฐ๋ฆฌ, ์ด ์†Œ์š” ์‹œ๊ฐ„์„ ํ•จ๊ป˜ ์ถœ๋ ฅํ•œ๋‹ค. +- ๊ฒฝ๋กœ ์กฐํšŒ ์‹œ `์ตœ๋‹จ ๊ฑฐ๋ฆฌ` ๋˜๋Š” `์ตœ์†Œ ์‹œ๊ฐ„` ์˜ต์…˜์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค. + +### ์˜ˆ์™ธ ์ฒ˜๋ฆฌ + +- ์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ์€ 2๊ธ€์ž ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค. +- ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์—ญ์„ ์ถœ๋ฐœ์—ญ ๋˜๋Š” ๋„์ฐฉ์—ญ์œผ๋กœ ์ž…๋ ฅํ•  ์ˆ˜ ์—†๋‹ค. +- ๊ฒฝ๋กœ ์กฐํšŒ ์‹œ ์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ์ด ๊ฐ™์„ ์ˆ˜ ์—†๋‹ค. +- ๊ฒฝ๋กœ ์กฐํšŒ ์‹œ ์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ์ด ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์œผ๋ฉด ๊ฒฝ๋กœ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์—†๋‹ค. +- ๊ทธ ์™ธ ์ •์ƒ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ `alert`์œผ๋กœ ์—๋Ÿฌ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. + +
+ +## ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์‹คํ–‰ ๊ฒฐ๊ณผ + +### ๊ฒฝ๋กœ ์กฐํšŒ + + + +## ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์š”๊ตฌ์‚ฌํ•ญ + +### ๊ธธ์ฐพ๊ธฐ ๊ด€๋ จ ๊ธฐ๋Šฅ + +- ์ถœ๋ฐœ์—ญ์„ ์ž…๋ ฅํ•˜๋Š” input ํƒœ๊ทธ๋Š” `departure-station-name-input` id ์†์„ฑ๊ฐ’์„ ๊ฐ€์ง„๋‹ค. +- ๋„์ฐฉ์—ญ์„ ์ž…๋ ฅํ•˜๋Š” input ํƒœ๊ทธ๋Š” `arrival-station-name-input` id ์†์„ฑ๊ฐ’์„ ๊ฐ€์ง„๋‹ค. +- ์ตœ๋‹จ๊ฑฐ๋ฆฌ, ์ตœ์†Œ์‹œ๊ฐ„์„ ์„ ํƒํ•˜๋Š” radio๋Š” `search-type` name ์†์„ฑ๊ฐ’์„ ๊ฐ€์ง„๋‹ค. + - **radio option์˜ default ๊ฐ’์€ ์ตœ๋‹จ๊ฑฐ๋ฆฌ์ด๋‹ค.** +- ๊ธธ์ฐพ๊ธฐ ๋ฒ„ํŠผ์€ `search-button` id ์†์„ฑ๊ฐ’์„ ๊ฐ€์ง„๋‹ค. +- ๐Ÿ“ ๊ฒฐ๊ณผ๋Š” `table`์„ ์ด์šฉํ•˜์—ฌ ๋ณด์—ฌ์ค€๋‹ค. + +## ํžŒํŠธ + +## ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™” + +์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘์— ํ•˜๋‚˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด data๋ฅผ `export`ํ•˜๊ณ , `import`ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. + +```javascript +export const users = [ + { + name: "Alt", + }, + { + name: "Jamie", + }, + { + name: "Sony", + }, +]; + +export const courses = [ + { + name: "frontend", + }, + { + name: "backend", + }, + { + name: "iOS", + }, + { + name: "Android", + }, +]; +``` + +์œ„์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ `export`ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ `import` ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. + +```javascript +import { users, courses } from "./data.js"; + +function App() { + this.users = users; + this.courses = courses; +} +``` + +## ์ตœ๋‹จ ๊ฒฝ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ + +- `utils/Dijkstra.js` ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๊ฐ„ํŽธํ•˜๊ฒŒ ์ตœ๋‹จ๊ฑฐ๋ฆฌ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. +- ์ •์ (Vertex)๊ณผ ๊ฐ„์„ (Edge), ๊ทธ๋ฆฌ๊ณ  ๊ฐ€์ค‘์น˜ ๊ฐœ๋…์„ ์ด์šฉ + - ์ •์ : ์ง€ํ•˜์ฒ ์—ญ + - ๊ฐ„์„ : ์ง€ํ•˜์ฒ ์—ญ ์—ฐ๊ฒฐ์ •๋ณด + - ๊ฐ€์ค‘์น˜: ๊ฑฐ๋ฆฌ or ์†Œ์š” ์‹œ๊ฐ„ +- ์ตœ๋‹จ ๊ฑฐ๋ฆฌ ๊ธฐ์ค€ ์กฐํšŒ ์‹œ ๊ฐ€์ค‘์น˜๋ฅผ ๊ฑฐ๋ฆฌ๋กœ ์„ค์ • +- ์ตœ์†Œ ์‹œ๊ฐ„ ๊ธฐ์ค€ ์กฐํšŒ ์‹œ ๊ฐ€์ค‘์น˜๋ฅผ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ • + +```javascript +import Dijkstra from "./utils/Dijkstra.js"; +const dijkstra = new Dijkstra(); + +//dijkstra.addEdge("์ถœ๋ฐœ์—ญ", "๋„์ฐฉ์—ญ", ๊ฑฐ๋ฆฌ ๋˜๋Š” ์‹œ๊ฐ„); +dijkstra.addEdge("V1", "V2", 2); +dijkstra.addEdge("V2", "V3", 2); +dijkstra.addEdge("V1", "V3", 100); + +const result = dijkstra.findShortestPath("V1", "V3"); +// result = ["V1", "V2", "V3"] +``` + +#### ํ…Œ์ŠคํŠธ์„ค๋ช… + + + +- ์—ญ ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ V1->V3 ๊ฒฝ๋กœ๊ฐ€ ์ตœ๋‹จ ๊ฒฝ๋กœ +- ์—ญ ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ ๋ คํ•  ๊ฒฝ์šฐ V1->V3 ๊ฒฝ๋กœ์˜ ๊ฑฐ๋ฆฌ๋Š” 100km, V1->V2->V3 ๊ฒฝ๋กœ์˜ ๊ฑฐ๋ฆฌ๋Š” 4km์ด๋ฏ€๋กœ ์ตœ๋‹จ ๊ฒฝ๋กœ๋Š” V1->V2->V3 + +
+ +### ์š”๊ตฌ์‚ฌํ•ญ + +- ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป๋œ ์ž…๋ ฅ ๊ฐ’์„ ์ž‘์„ฑํ•œ ๊ฒฝ์šฐ `alert`์„ ์ด์šฉํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ์žฌ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. +- ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(jQuery, Lodash ๋“ฑ)๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์ˆœ์ˆ˜ Vanilla JS๋กœ๋งŒ ๊ตฌํ˜„ํ•œ๋‹ค. +- **์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ์ปจ๋ฒค์…˜์„ ์ง€ํ‚ค๋ฉด์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ** ํ•œ๋‹ค + - [https://google.github.io/styleguide/jsguide.html](https://google.github.io/styleguide/jsguide.html) + - [https://ui.toast.com/fe-guide/ko_CODING-CONVENSION/](https://ui.toast.com/fe-guide/ko_CODING-CONVENTION) +- **indent(์ธ๋ดํŠธ, ๋“ค์—ฌ์“ฐ๊ธฐ) depth๋ฅผ 3์ด ๋„˜์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. 2๊นŒ์ง€๋งŒ ํ—ˆ์šฉ**ํ•œ๋‹ค. + - ์˜ˆ๋ฅผ ๋“ค์–ด while๋ฌธ ์•ˆ์— if๋ฌธ์ด ์žˆ์œผ๋ฉด ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” 2์ด๋‹ค. + - ํžŒํŠธ: indent(์ธ๋ดํŠธ, ๋“ค์—ฌ์“ฐ๊ธฐ) depth๋ฅผ ์ค„์ด๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์€ ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์†Œ๋“œ)๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ๋œ๋‹ค. +- **ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์†Œ๋“œ)์˜ ๊ธธ์ด๊ฐ€ 15๋ผ์ธ์„ ๋„˜์–ด๊ฐ€์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค.** + - ํ•จ์ˆ˜(๋˜๋Š” ๋ฉ”์†Œ๋“œ)๊ฐ€ ํ•œ ๊ฐ€์ง€ ์ผ๋งŒ ์ž˜ ํ•˜๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค. +- ๋ณ€์ˆ˜ ์„ ์–ธ์‹œ [var](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/var) ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. [const](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/const) ์™€ [let](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/let) ์„ ์‚ฌ์šฉํ•œ๋‹ค. +- [import](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import) ๋ฌธ์„ ์ด์šฉํ•ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ชจ๋“ˆํ™”ํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๋‹ค. +- [template literal](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals)์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ์™€ html string์„ ๊ฐ€๋…์„ฑ ์ข‹๊ฒŒ ํ‘œํ˜„ํ•œ๋‹ค. + +
+ +## ๋ฏธ์…˜ ์ €์žฅ์†Œ ๋ฐ ์ง„ํ–‰ ์š”๊ตฌ์‚ฌํ•ญ + +- ๋ฏธ์…˜์€ [https://github.com/woowacourse/javascript-subway-path-precourse](https://github.com/woowacourse/javascript-subway-path-precourse) ์ €์žฅ์†Œ๋ฅผ fork/cloneํ•ด ์‹œ์ž‘ํ•œ๋‹ค. +- **๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์— javascript-subway-path-precourse/docs/README.md ํŒŒ์ผ์— ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ ๋ชฉ๋ก**์„ ์ •๋ฆฌํ•ด ์ถ”๊ฐ€ํ•œ๋‹ค. +- **git์˜ commit ๋‹จ์œ„๋Š” ์•ž ๋‹จ๊ณ„์—์„œ README.md ํŒŒ์ผ์— ์ •๋ฆฌํ•œ ๊ธฐ๋Šฅ ๋ชฉ๋ก ๋‹จ์œ„๋กœ ์ถ”๊ฐ€**ํ•œ๋‹ค. +- [ํ”„๋ฆฌ์ฝ”์Šค ๊ณผ์ œ ์ œ์ถœ](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) ๋ฌธ์„œ ์ ˆ์ฐจ๋ฅผ ๋”ฐ๋ผ ๋ฏธ์…˜์„ ์ œ์ถœํ•œ๋‹ค. diff --git a/images/dijkstra_example.png b/images/dijkstra_example.png new file mode 100644 index 0000000..7c75197 Binary files /dev/null and b/images/dijkstra_example.png differ diff --git a/images/path_result.gif b/images/path_result.gif new file mode 100644 index 0000000..ee394bd Binary files /dev/null and b/images/path_result.gif differ diff --git a/images/path_result.jpg b/images/path_result.jpg new file mode 100644 index 0000000..40a4bed Binary files /dev/null and b/images/path_result.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..43a0efa --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + ์ง€ํ•˜์ฒ  ๊ธธ์ฐพ๊ธฐ + + +
+

๐Ÿš‡ ์ง€ํ•˜์ฒ  ๊ธธ์ฐพ๊ธฐ

+
+ + + diff --git a/src/components/find-path-input-container.js b/src/components/find-path-input-container.js new file mode 100644 index 0000000..7bf4404 --- /dev/null +++ b/src/components/find-path-input-container.js @@ -0,0 +1,46 @@ +import { makeStringToHTML } from "../utils/display/display-utils.js"; + +import { dijkstraTime, dijkstraDistance } from "../utils/make-edges.js"; +import { + validateInput, + checkIfStartAndEndSame, +} from "../utils/input/validator/input-validator.js"; +import { minPath, newResult } from "./find-path-result-container.js"; + +const app = document.getElementById("app"); + +export default function findPath() { + const subwayPathHTML = ` +
+ ์ถœ๋ฐœ์—ญ
+ ๋„์ฐฉ์—ญ
+ ์ตœ๋‹จ๊ฑฐ๋ฆฌ + ์ตœ์†Œ์‹œ๊ฐ„
+ +
+ `; + app.append(makeStringToHTML(subwayPathHTML).firstElementChild); + + const searchButton = document.getElementById("search-button"); + searchButton.addEventListener("click", () => { + const selected = document.querySelector('input[name="search-type"]:checked') + .value; + const startStation = document.getElementById("departure-station-name-input") + .value; + const endStation = document.getElementById("arrival-station-name-input") + .value; + newResult(); + + if (!startStation && !endStation) { + return checkIfStartAndEndSame(startStation, endStation); + } + + if (validateInput(startStation) && validateInput(endStation)) { + if (selected === "distance") { + minPath(dijkstraDistance, startStation, endStation); + } else if (selected === "time") { + minPath(dijkstraTime, startStation, endStation); + } + } + }); +} diff --git a/src/components/find-path-result-container.js b/src/components/find-path-result-container.js new file mode 100644 index 0000000..a7fb475 --- /dev/null +++ b/src/components/find-path-result-container.js @@ -0,0 +1,119 @@ +import { + makeStringToHTML, + clearElement, +} from "../utils/display/display-utils.js"; +import { lines } from "../data.js"; + +export function newResult() { + resultWrapper(); + + const result = document.getElementById("result"); + clearElement(result); + resultPath(result); + resultPathTable(result); +} + +function resultWrapper() { + const resultWrapperHTML = `
`; + app.append(makeStringToHTML(resultWrapperHTML).firstElementChild); +} + +function resultPath(parent) { + const subwayPathTitleHTML = `

๐Ÿ“ ๊ฒฐ๊ณผ

`; + parent.append(makeStringToHTML(subwayPathTitleHTML).firstElementChild); +} + +export function minPath(distanceOrTimeDijkstra, startStation, endStation) { + const tbody = document.getElementsByTagName("tbody")[0]; + const shortestDistance = distanceOrTimeDijkstra.findShortestPath( + startStation, + endStation + ); + + tbody.append( + distanceAndTimeRow( + totalDistance(shortestDistance)[0], + totalDistance(shortestDistance)[1] + ), + + resultPathStations(shortestDistance) + ); + + return shortestDistance; +} + +function resultPathStations(result) { + const td = document.createElement("td"); + let resultStations = ""; + + for (let i = 0; i < result.length; i++) { + resultStations += result[i]; + if (result[i + 1]) { + resultStations += "โžก"; + } + } + + td.textContent = resultStations; + td.colSpan = 2; + + return td; +} + +function textInTd(innerText) { + const td = document.createElement("td"); + td.textContent = innerText; + + return td; +} + +function distanceAndTimeRow(...args) { + const tr = document.createElement("tr"); + + for (const text of [...args]) { + const tdWithText = textInTd(text); + tr.append(tdWithText); + } + + return tr; +} + +function resultPathTable(parent) { + const subwayPathTableHTML = ` + + + + + + + + + +
์ด ๊ฑฐ๋ฆฌ์ด ์†Œ์š” ์‹œ๊ฐ„
+ `; + parent.append(makeStringToHTML(subwayPathTableHTML).firstElementChild); +} + +function totalDistance(result) { + const totalDistanceAndTimeArray = []; + let totalDistances = 0; + let totalTimes = 0; + + for (const line of lines) { + for (const station of line.stations) { + for (let i = 0; i < result.length - 1; i++) { + if ( + (result[i] === station.name && + result[i + 1] === station.nextStation) || + (result[i] === station.nextStation && result[i + 1] === station.name) + ) { + totalDistances += station.distance; + totalTimes += station.time; + } + } + } + } + + totalDistanceAndTimeArray.push(totalDistances + "km", totalTimes + "๋ถ„"); + + return totalDistanceAndTimeArray; +} diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..5dd1a50 --- /dev/null +++ b/src/data.js @@ -0,0 +1,77 @@ +export const lines = [ + { + lineName: "2ํ˜ธ์„ ", + stations: [ + { + name: "๊ต๋Œ€", + nextStation: "๊ฐ•๋‚จ", + distance: 2, + time: 3, + }, + { + name: "๊ฐ•๋‚จ", + nextStation: "์—ญ์‚ผ", + distance: 2, + time: 3, + }, + { + name: "์—ญ์‚ผ", + nextStation: null, + distance: null, + time: null, + }, + ], + }, + { + lineName: "3ํ˜ธ์„ ", + stations: [ + { + name: "๊ต๋Œ€", + nextStation: "๋‚จ๋ถ€ํ„ฐ๋ฏธ๋„", + distance: 3, + time: 2, + }, + { + name: "๋‚จ๋ถ€ํ„ฐ๋ฏธ๋„", + nextStation: "์–‘์žฌ", + distance: 6, + time: 5, + }, + { + name: "์–‘์žฌ", + nextStation: "๋งค๋ด‰", + distance: 1, + time: 1, + }, + { + name: "๋งค๋ด‰", + nextStation: null, + distance: null, + time: null, + }, + ], + }, + { + lineName: "์‹ ๋ถ„๋‹น์„ ", + stations: [ + { + name: "๊ฐ•๋‚จ", + nextStation: "์–‘์žฌ", + distance: 2, + time: 8, + }, + { + name: "์–‘์žฌ", + nextStation: "์–‘์žฌ์‹œ๋ฏผ์˜์ˆฒ", + distance: 10, + time: 3, + }, + { + name: "์–‘์žฌ์‹œ๋ฏผ์˜์ˆฒ", + nextStation: null, + distance: null, + time: null, + }, + ], + }, +]; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..3f8e23d --- /dev/null +++ b/src/index.js @@ -0,0 +1,9 @@ +import findPath from "./components/find-path-input-container.js"; + +function subwayPath() { + findPath(); +} + +new subwayPath(); + +// ์ฒซ ํ™”๋ฉด ๊ทธ๋ฆฌ๊ธฐ diff --git a/src/utils/Dijkstra.js b/src/utils/Dijkstra.js new file mode 100644 index 0000000..7c6cfd5 --- /dev/null +++ b/src/utils/Dijkstra.js @@ -0,0 +1,220 @@ +export default function Dijkstra() { + const Node = { + init: function (val, priority) { + this.val = val; + this.priority = priority; + }, + }; + + const PriorityQueue = { + init: function () { + this.values = []; + }, + enqueue: function (val, priority) { + const newNode = Object.create(Node); + newNode.init(val, priority); + + this.values.push(newNode); + + let idxOfNewNode = this.values.length - 1; + + while (idxOfNewNode > 0) { + const idxOfParentNode = Math.floor((idxOfNewNode - 1) / 2); + + const parentNode = this.values[idxOfParentNode]; + + if (priority < parentNode.priority) { + this.values[idxOfParentNode] = newNode; + this.values[idxOfNewNode] = parentNode; + idxOfNewNode = idxOfParentNode; + continue; + } + break; + } + return this.values; + }, + dequeue: function () { + if (this.values.length == 0) { + return; + } + const dequeued = this.values.shift(); + const lastItem = this.values.pop(); + if (!lastItem) { + return dequeued; + } + this.values.unshift(lastItem); + + let idxOfTarget = 0; + + while (true) { + let idxOfLeftChild = idxOfTarget * 2 + 1; + let idxOfRightChild = idxOfTarget * 2 + 2; + let leftChild = this.values[idxOfLeftChild]; + let rightChild = this.values[idxOfRightChild]; + + function swap(direction) { + const idxOfChild = + direction == "left" ? idxOfLeftChild : idxOfRightChild; + const child = direction == "left" ? leftChild : rightChild; + this.values[idxOfChild] = this.values[idxOfTarget]; + this.values[idxOfTarget] = child; + idxOfTarget = idxOfChild; + } + + if (!leftChild) { + return dequeued; + } + + if (!rightChild) { + if (leftChild.priority < lastItem.priority) { + swap.call(this, "left"); + continue; + } + return dequeued; + } + + if (leftChild.priority == rightChild.priority) { + swap.call(this, "left"); + continue; + } + + if ( + leftChild.priority < rightChild.priority && + leftChild.priority < lastItem.priority + ) { + swap.call(this, "left"); + continue; + } + + if ( + rightChild.priority < leftChild.priority && + rightChild.priority < lastItem.priority + ) { + swap.call(this, "right"); + continue; + } + } + }, + }; + + const WeightedGraph = { + init: function () { + this.adjacencyList = {}; + this.length = 0; + }, + addVertex: function (vertex) { + if (!this.adjacencyList.hasOwnProperty(vertex)) { + this.adjacencyList[vertex] = {}; + this.length++; + } + }, + addEdge: function (vertex1, vertex2, weight) { + this.addVertex(vertex1); + this.addVertex(vertex2); + this.adjacencyList[vertex1][vertex2] = weight; + this.adjacencyList[vertex2][vertex1] = weight; + return this.adjacencyList; + }, + removeEdge: function (vertex1, vertex2) { + if (!this.adjacencyList.hasOwnProperty(vertex1)) { + return `There's no ${vertex1}`; + } + if (!this.adjacencyList.hasOwnProperty(vertex2)) { + return `There's no ${vertex2}`; + } + + function removeHelper(v1, v2) { + if (!this.adjacencyList.hasOwnProperty(v1)) { + return `There's no edge between ${v1} and ${v2}`; + } + delete this.adjacencyList[v1][v2]; + if (Object.keys(this.adjacencyList[v1]).length == 0) { + delete this.adjacencyList[v1]; + } + } + + removeHelper.call(this, vertex1, vertex2); + removeHelper.call(this, vertex2, vertex1); + + return this.adjacencyList; + }, + removeVertex: function (vertex) { + if (!this.adjacencyList.hasOwnProperty(vertex)) { + return `There's no ${vertex}`; + } + const edges = this.adjacencyList[vertex]; + for (const key in edges) { + this.removeEdge(key, vertex); + } + return this.adjacencyList; + }, + findShortestRoute: function (start, end) { + if (!start || !end) { + throw Error("์ถœ๋ฐœ์ง€์™€ ๋„์ฐฉ์ง€๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + const distance = {}; + const previous = {}; + const pq = Object.create(PriorityQueue); + pq.init(); + pq.enqueue(start, 0); + const visited = {}; + + const hashOfVertex = this.adjacencyList; + for (const vertexName in hashOfVertex) { + const priority = vertexName == start ? 0 : Infinity; + distance[vertexName] = priority; + previous[vertexName] = null; + } + + while (true) { + let current = pq.dequeue(); + if (!current?.val) { + return; + } + current = current.val; + if (current == end) { + break; + } + const neighbors = hashOfVertex[current]; + + for (const vertexName in neighbors) { + if (visited.hasOwnProperty(vertexName)) { + continue; + } + const distFromStart = distance[current] + neighbors[vertexName]; + + if (distFromStart < distance[vertexName]) { + pq.enqueue(vertexName, distFromStart); + distance[vertexName] = distFromStart; + previous[vertexName] = current; + } + } + visited[current] = true; + } + + let node = end; + + const route = []; + while (node) { + route.unshift(node); + node = previous[node]; + } + + return route; + }, + }; + + this.addEdge = (source, target, weight) => { + WeightedGraph.addEdge(source, target, weight); + }; + + this.findShortestPath = (source, target) => { + return WeightedGraph.findShortestRoute(source, target); + }; + + this.addVertex = (vertex) => { + WeightedGraph.addVertex(vertex); + }; + + WeightedGraph.init(); +} diff --git a/src/utils/display/display-utils.js b/src/utils/display/display-utils.js new file mode 100644 index 0000000..80cd2a2 --- /dev/null +++ b/src/utils/display/display-utils.js @@ -0,0 +1,11 @@ +const parser = new DOMParser(); + +export function makeStringToHTML(string) { + return parser.parseFromString(string, "text/html").body; +} + +export function clearElement(element) { + if (element) { + element.innerHTML = ""; + } +} diff --git a/src/utils/input/validator/input-validator.js b/src/utils/input/validator/input-validator.js new file mode 100644 index 0000000..05bfe18 --- /dev/null +++ b/src/utils/input/validator/input-validator.js @@ -0,0 +1,47 @@ +import { lines } from "../../../data.js"; + +function isValidStation(input) { + const allStationArray = [...allStations()]; + let isValid = false; + + if (!allStationArray.includes(input)) { + isValid = true; + } + + return isValid; +} + +function allStations() { + const allStations = []; + for (const line of lines) { + for (const station of line.stations) { + allStations.push(station.name); + } + } + const notDuplicatedStations = new Set([...allStations]); + + return notDuplicatedStations; +} + +export function validateInput(input) { + const MINIMUM_INPUT_LENGTH = 2; + const noSpaceInput = input.trim(); + + let isValid = false; + + if (noSpaceInput.length < MINIMUM_INPUT_LENGTH) { + alert("2๊ธ€์ž ์ด์ƒ์˜ ์—ญ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค!"); + } else if (isValidStation(noSpaceInput)) { + alert("์กด์žฌํ•˜์ง€ ์•Š๋Š” ์—ญ์ž…๋‹ˆ๋‹ค."); + } else { + isValid = true; + } + + return isValid; +} + +export function checkIfStartAndEndSame(startStation, endStation) { + if (startStation === endStation) { + alert("์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ์€ ๊ฐ™์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } +} diff --git a/src/utils/make-edges.js b/src/utils/make-edges.js new file mode 100644 index 0000000..62fa870 --- /dev/null +++ b/src/utils/make-edges.js @@ -0,0 +1,21 @@ +import Dijkstra from "../utils/Dijkstra.js"; +import { lines } from "../data.js"; +export const dijkstraTime = new Dijkstra(); +export const dijkstraDistance = new Dijkstra(); + +for (const line of lines) { + for (const station of line.stations) { + addEdge(station); + } +} + +function addEdge(station) { + if (station.nextStation !== null) { + dijkstraDistance.addEdge( + station.name, + station.nextStation, + station.distance + ); + dijkstraTime.addEdge(station.name, station.nextStation, station.time); + } +}