diff --git "a/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/README.md" "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/README.md" new file mode 100644 index 0000000..bb78317 --- /dev/null +++ "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/README.md" @@ -0,0 +1,38 @@ +## HOW TO CHECK + +루트 디렉토리를 기준으로 다음을 순차적으로 입력해주세요. + +### 현재 과제 디렉토리로 이동 + +```bash +cd JavaScript +cd 07주차 +cd assignment +cd 재영 +``` + +### 커맨드 실행 + +```bash +npm test +``` + +`pnpm`을 쓰신다면 `pnpm`으로 테스트 가능합니다. +```bash +pnpm test +``` + +`yarn`을 쓰신다면 `yarn`으로 테스트 가능합니다. +```bash +yarn test +``` + +### 테스트 코드 작성 + +`test.js`에서 테스트가 가능합니다. + +#### 작성 방법 + +`expect`가 같은 것임을 확인하는 함수라면, `describe`는 일련의 `expect`를 단위로 묶어주는 grouping을 위한 함수입니다. + +`describe`에 첫 번째 인자는 `"테스트 그룹명"`을, 두 번째 인자에는 `expect들의 배열`을 넣어주세요. \ No newline at end of file diff --git "a/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/index.js" "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/index.js" new file mode 100644 index 0000000..3630fe9 --- /dev/null +++ "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/index.js" @@ -0,0 +1,127 @@ +// [x] 양방향 연결리스트는 각 노드의 prev와 next로 이동할 수 있어야 합니다. +class Node { + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +export class DoubleLinkedList { + constructor(val) { + this.node = val ? new Node(val) : null; + this.head = val ? this.node : null; + this.tail = val ? this.node : null; + + this.cnt = val ? 1 : 0; + } + + get prevNode() { + return this.node.prev ?? this.node; + } + + get nextNode() { + return this.node.next ?? this.node; + } + + // [x] insert메서드를 통해 특정 인덱스의 노드에 삽입할 수 있어야 해요. + insert(val) { + if (!this.cnt) { + const nowNode = new Node(val); + this.node = nowNode; + this.head = nowNode; + this.tail = nowNode; + } else { + const nowNode = new Node(val, this.node, this.node.next ?? this.head); + this.nextNode.prev = nowNode; + this.node.next = nowNode; + this.node = nowNode; + + if (this.node.next === this.head) this.tail = nowNode; + } + + this.cnt += 1; + + return this; + } + + // [x] get를 통해 원하는 인덱스의 노드를 가져올 수 있어야 해요. + getNthNodeFromHead(order) { + let i = 0; + let now = this.head; + while (i < order) { + i += 1; + now = now.next; + } + + return now; + } + + getNthNode(order) { + let i = 0; + let now = this.node; + while (i < order) { + i += 1; + now = now.next; + } + + return now; + } + + get nowNode() { + return this.node; + } + + // [x] update를 통해 해당 인덱스의 노드를 수정할 수 있어야 해요. + update(val) { + this.node.val = val; + + return this; + } + + // [x] remove를 통해 해당 인덱스의 노드를 삭제할 수 있어야 해요. + remove() { + if (!this.cnt) return this; + + if (this.cnt === 1) { + this.node = null; + + this.head = null; + this.tail = null; + } else { + this.nextNode.prev = this.node.prev; + this.prevNode.next = this.node.next; + + if (this.head === this.node) this.head = this.node.next; + this.node = this.node.next; + } + + this.cnt -= 1; + + return this; + } + + goPrev() { + if (this.cnt === 0) return this; + if (this.cnt === 1) return this.node; + + this.node = this.node.prev; + + return this; + } + + goNext() { + if (this.cnt === 0) return this; + if (this.cnt === 1) return this.node; + + this.node = this.node.next; + + return this; + } + + get length() { + return this.cnt; + } +} + +const doubleLinkedList = new DoubleLinkedList(); diff --git "a/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/package.json" "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/package.json" new file mode 100644 index 0000000..9ef4e93 --- /dev/null +++ "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/package.json" @@ -0,0 +1,7 @@ +{ + "main": "index.js", + "type": "module", + "scripts": { + "test": "node ./test.js" + } +} \ No newline at end of file diff --git "a/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/test.js" "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/test.js" new file mode 100644 index 0000000..92a2fed --- /dev/null +++ "b/JavaScript/07\354\243\274\354\260\250/assignment/\354\236\254\354\230\201/test.js" @@ -0,0 +1,77 @@ +import { DoubleLinkedList } from "./index.js"; + +function expect(string = "", cb) { + if (!new.target) { + return new expect(string, cb); + } + + this.isNot = false; + + Object.defineProperty(this, 'not', { + get() { + this.isNot = !this.isNot; + return this; + }, + }); + + this.toBeEqual = (val) => { + const res = cb(); + const result = this.isNot ? res !== val : res === val; + + const nowIsNot = this.isNot; + + this.isNot = false; + + console.log({ command: string, result, ...(!result ? { expectedValue: `${nowIsNot ? 'not ' : ''}${val}`, actualValue: res } : {}) }); + return result; + } +} + +function describe(title, tests) { + try { + console.log(`${title} 결과: `) + const res = tests.filter(v => v).length; + if (res === tests.length) { + console.log(`✅ CHECKED ${tests.length} TESTS SUCCEED!`) + } else { + throw new Error(`🔥 ${res}/${tests.length} TESTS SUCCEED. FAILED 😖`) + } + } catch(e) { + console.error("🤯 FAILED TEST!\n") + console.error(e) + } +} + +describe("양방향 연결리스트", [ + expect("3개를 추가한 후 현재를 반환하면, 길이가 3개가 나와야 한다.", () => { + const doubleLinkedList = new DoubleLinkedList(); + return doubleLinkedList.insert(1).insert(2).insert(3).length; + }).toBeEqual(3), + expect("마지막을 삭제하면, 마지막은 첫번째 머리 노드가 나와야 한다.", () => { + const doubleLinkedList = new DoubleLinkedList(); + return doubleLinkedList.insert(1).insert(2).insert(3).remove().nowNode.val; + }).toBeEqual(1), + expect("첫번째 머리 노드를 삭제하면, 추출 시 다음 노드의 앞은 꼬리 노드가 나와야 한다.", () => { + const doubleLinkedList = new DoubleLinkedList(); + return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().nowNode.val; + }).toBeEqual(2), + expect("첫번째 머리 노드를 삭제하면, 다음 머리는 다음 노드가 되어야 한다.", () => { + const doubleLinkedList = new DoubleLinkedList(); + return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().head.val; + }).toBeEqual(2), + expect("getNthNodeFromHead 메서드로 순회를 한다면 머리 노드를 기준으로 나와야 한다", () => { + const doubleLinkedList = new DoubleLinkedList(); + return doubleLinkedList + .insert(1) + .insert(2) + .insert(3) + .insert(4) + .insert(5) + .insert(6) + .insert(7) + .insert(8) + .insert(9) + .getNthNode(8) + .val + }).toBeEqual(8) +]); \ No newline at end of file