문제 4. fizzbuzz 기능을 배열 프로토타입으로 만들어라.
(3의 배수이면 fizz로 변환, 5의 배수이면 buzz로 변환, 3과 5의 배수이면 fizzbuzz로 변환)
const arr = [9, 80, 12, 2, 30]
arr.fizzbuzz()
// arr is ["fizz", "buzz", "fizz", 2, "fizzbuzz"]
풀이 전 생각)
- 3과 5의 배수는 3의 배수와 5의 배수와 겹치니, 이것의 구성 순서를 잘 생각해보아야 한다.
- 해당 배열을 모두 순회해야하고, 배열을 바꾸어야하므로, forEach를 사용할 수 있다.
풀이)
Array.prototype.fizzbuzz = function() {
this.forEach((e, i, arr) => {
if (e % 3 === 0 && e % 5 === 0) {
console.log(e, i, arr)
return arr[i] = 'fizzbuzz'
}
if (e % 3 === 0) {
console.log(e, i, arr)
return arr[i] = 'fizz'
}
if (e % 5 === 0) {
console.log(e, i, arr)
return arr[i] = 'buzz'
}
})
}
- 배열을 순회하며 바꿔야하는 기능 -> forEach로 구현
- 이 때, fizzbuzz의 순서를 잘 고려해야한다.
- fizzbuzz는 3과 5의 배수이기 때문에 중첩적으로 연산이 될 가능성이 있다.
- 위 함수에서는 return 기능을 통해 중첩연산을 방지하였다.
forEach의 인수를 log를 통해 하나씩 출력해보았다.
(배열의 요소, 요소의 인덱스, 사용하는 배열) 순이다.
연산 결과에 따라 기존의 배열이 계속 바뀌고 있음을 볼 수 있다.
답지 풀이)
Array.prototype.fizzbuzz = function (i = 0) {
if (i === this.length) {
return this
}
if (this[i] % 5 === 0 && this[i] % 3 === 0) {
this[i] = 'fizzbuzz'
}
if (this[i] % 3 === 0) {
this[i] = 'fizz'
}
if (this[i] % 5 === 0) {
this[i] = 'buzz'
}
return this.fizzbuzz(i + 1)
}
- forEach를 사용하지 않고 배열의 인덱스를 갱신했다.
- 여기서, this[i]를 한번 수정하면 배열의 요소가 바뀌면서 중복 연산이 일어나지 않으므로,
- fizzbuzz가 먼저 연산되게끔 짜는 것이 안전하다.
문제 5. removeEvens이라는 프로토타입 기능을 만들어라.
이 기능은 배열 내 짝수들을 삭제한다.
const arr = [9, 80, 12, 2]
arr.removeEvens()
// arr becomes [9]
풀이 전 생각)
- 배열의 일부를 잘라내는 연산 -> splice를 이용한다.
- splice의 파라미터는 (해당 인덱스, 잘라낼 갯수, 대체할 요소) 이다.
- 배열의 길이와 내부를 순환하는 인덱스 카운터의 크기가 같아지면 함수를 종료한다.
풀이)
Array.prototype.removeEvens = function(i = 0) {
if (this.length === i) return this
if (this[i] % 2 === 0) {
console.log('변경전', this);
this.splice(i, 1)
console.log('변경후', this);
return this.removeEvens(i)
}
return this.removeEvens(i + 1)
}
배열을 하나씩 돌면서 조건에 맞는 위치에서 splice가 작동된다.
이 때, 짝수인 요소를 삭제한 조건문에서 함수를 재귀시킬 때, 인덱스 카운터를 1올린다면
그 다음 요소를 아예 건너뛰게 되므로, 짝수를 제거했을 때는 인덱스를 추가하지 않아야 한다.
답지와 정답이 같다.
문제 6. getIterator라는 프로토타입 기능을 만들어라.
해당 프로토타입은 함수를 리턴한다.
리턴된 함수를 호출했을 때, 배열의 다음 요소가 리턴된다.
const arr = [9, 80, 12, 2]
const get = arr.getIterator()
get() // 9
get() // 80
get() // 12
get() // 2
get() // 9 ...
풀이 전 생각)
- 클로저를 이용한다.
- 함수를 호출할 때마다 반복적으로 순환될 수 있는 모델을 만든다.
풀이)
Array.prototype.getIterator = function() {
let count = 0
return () => {
if (this.length === 0) {
return undefined
}
if (this.length <= count) {
count = 0
}
count = count + 1
return this[count - 1]
}
}
- let count 리턴함수 밖에 선언하여 클로저를 이용할 수 있는 환경을 만들어준다.
- 빈 배열이 들어왔을 경우를 대비한 undefined를 코드해준다.
- count가 배열의 길이를 초과했다면 초기화 시킨다.
- 카운트를 하나씩 올리면서, 배열의 count - 1 인덱스 요소를 리턴한다.
답지 정답)
Array.prototype.getIterator = function () {
let i = -1
return () => {
i = i + 1
return this[i % this.length]
}
}
- 아.. 이렇게도 할 수 있구나 싶었다.
- i % 배열의 길이... 배열 길이를 4로 고정한다면
- 0 % 4 = 0
- 1 % 4 = 1
- 2 % 4 = 2 ...
이런식으로 진행하게되어 인덱스를 하나씩 순회할 수 있다.
하나 배웠다!
'Javascript' 카테고리의 다른 글
addEventListener (0) | 2022.08.24 |
---|---|
Async + 배열 helper 기능 (0) | 2022.08.18 |
Prototype Inheritance 문제 연습 1 (0) | 2022.08.17 |
Prototype Inheritance (0) | 2022.08.17 |
크롬 브라우저 사용해서 Object 찍어보기 (0) | 2022.08.15 |