deleteNth

Codewars 문제 풀이

deleteNth 문제

목록 arr와 숫자 N이 주어지면 순서를 변경하지 않고 최대 N번까지 각 arr 수를 포함하는 새 배열을 만듭니다. 예를 들어, N = 2이고 입력이 [1,2,1,1,2,3]인 경우, 반복횟수가 최대 2번이기 때문에 [1,2,1,2,3]이 됩니다.

예시

console.log(deleteNth([1, 1, 1, 1], 2)); // return [1,1]
console.log(deleteNth([20, 37, 20, 21], 1)); // return [20,37,21]

나의 풀이

function deleteNth(arr, n) {
  const result = [];
  const countNum = {};
  arr.forEach((num) => {
    countNum[num] ? countNum[num]++ : (countNum[num] = 1);
    if (countNum[num] <= n) result.push(num);
  });
  return result;
}
  1. 조건에 따라 원본 배열에서 허용되는 중복 횟수의 숫자만 추출해 새 배열을 만들어야 하므로 result 변수를 선언하고 빈 배열을 할당한다.
  2. 요소의 중복 횟수를 세기 위해 countNum변수를 생성하고 빈 객체를 할당한다.
    (빈 객체를 생성한 이유는 주어진 요소들을 프로퍼티 키로 지정하고 각 요소의 반복 횟수를 프로퍼티 값으로 정리하기 위함이다.)
  3. 매핑을 통해 새로운 배열을 바로 반환하는 목적이 아닌 주어진 배열을 순회하면서 조건에 맞는 값을 찾아 resultpush 하기 위한 목적이므로 map()이 아닌 forEach()메서드를 사용했다.
  4. 삼항연산자를 이용해 만약 countNum 객체에 중복되는 num있다면 +1 을 통해 카운트시키고 없다면 1로 카운트를 시작한다.
  5. 카운트가 끝나면 바로 다시 순회시키지 않고 if문을 사용한다. 만약 해당 숫자의 카운트가 최대횟수n을 넘지 않는다면 중복을 허용하기 때문에 result 배열에 숫자 numpush()한다. 만약 최대횟수를 넘어버리면 조건이 불충족되므로 push를 하지 않는다.(push() 메서드는 뒤에서부터 요소를 넣기 때문에 원본배열의 순서 그대로 유지할 수 있다.)
  6. 모든 순회가 끝나면 result 값을 반환한다.

만약 새로운 배열의 요소 값이 기존의 순서가 아닌 오름차순으로 반환해야 한다면 마지막 코드 줄만 수정하면 된다!

 return result.sort((a,b) => a-b);

다른 사람 풀이

Balkoth (plus 159 more warriors)

function deleteNth(arr,x) {
  var cache = {};
  return arr.filter(function(n) {
    cache[n] = (cache[n]||0) + 1;
    return cache[n] <= x;
  });
}
  1. 반복되는 요소의 수를 세기 위해 cache 변수를 생성하고 빈 객체를 할당한다.
  2. 전달받은 arrfilter()메서드를 이용해 조건에 맞는 요소만 반환시킨다.
  3. cache 객체의 프로퍼티 생성과 중복 횟수 카운트는 논리합 단축 평가 방식을 사용했다.
    cache 객체 내 filter()메서드에서 순회하는 숫자가 프로퍼티로 있다면 cache[n]||0는 기존cache[n]값에 +1 증가한다. 하지만 없다면 (undefined || 0)으로 값은 0으로 할당되고 +1 증가한다.
    (객체에 존재하지 않는 프로퍼티에 접근하면 ReferenceError가 발생하지 않고 undefined를 반환한다.)
  4. cache[n] 즉, 해당 숫자의 중복 카운트가 x보다 작거나 같으면 true이므로 반환할 새 배열에 저장하고 만약 중복 횟수가 넘어버리면 false로 저장하지 않는다.

위 풀이를 보고 든 생각
1. 조건에 따라 걸러내는 작업이 필요하기 때문에 filter() 메서드를 떠올렸지만 filter() 메서드를 사용하면 카운트 정보를 담은 객체에 대한 if문을 못사용한다는 생각을 했었다. 하지만 filter()메서드에 핵심은 조건에 맞는 요소를 뽑아 새 배열을 만들 수 있다는 점이다. if문을 작성하려 하기 보다 filter의 콜백함수에 조건을 작성할 생각을 하자!
2. 객체 프로퍼티 생성에 무조건 삼항 연산자만 생각했는데 논리 단축 평가를 사용해 값을 할당할 수 있다는 것을 배웠다. 논리합, 논리곱으로 if문을 대체할 수 있고 if...else문은 삼항연산자로 대체할 수 있다고 공부해도 역시 실제로 문제를 풀어야 깨닫고 기억할 수 있다. 개념을 눈으로만 보고 그것에 대해 안다고 절!대! 생각하지 말자!!

jhoffner

function deleteNth(arr,x){
  var count = {};
  return arr.filter(function(a){
    count[a] = ~~count[a]+1;
    return count[a]<=x;
  })
}

위 방법과 거의 흡사하지만 count[a] = ~~count[a]+1 이 다르다.

~는 무엇인가?
~은 비트 NOT 연산자다. 피연산자의 비트를 반전한다. 피연산자가 숫자가 아닌 경우 32비트 부호 있는 정수로 변환한다.

~0; // -1
~-1; // 0
~1; // -2
  • count 객체에 a 프로퍼티키가 없다면 값은 undefined 다. undefined는 연산자에 의해 0값으로 변환과 동시에 비트 NOT이 적용되어 값 -1이 된다. 여기서 ~이 한 번 더 적용되어 -1의 비트 반전인 0값을 가지게 된다. 뒤이은 +1로 카운트가 시작된다.
  • count객체에 a 프로퍼티키가 있다면 값은 기존의 값 그대로다. 왜냐하면 ~이 2번 적용되면서 비트 NOT의 비트 NOT 즉, 원래값으로 다시 돌아온다.

따라서 만약 undefined를 숫자 0으로 변환하고 싶다면 ~~ 연산자를 사용하면 좋을 것 같다.

연산자에서 숫자가 아닌 +undefined, -undefined는 값이 0이 아닌 NaN이다.
논리연산자에서 부울이 아닌 falsy값을 가진 undefinedfalse로 고려된다.
비트연산자에서 숫자가 아닌 undefined는 32비트 정수로 변환되어 값이 0이다.

좋은 웹페이지 즐겨찾기