본문 바로가기

1일1알고리즘

Simple Fun #136: Missing Values, arr.reduce(), &&로 if 대체, find() filter() | Codewars [자바스크립트]

Codewars 7kyu 문제 Simple Fun #136: Missing Values.

 

Codewars: Achieve mastery through challenge

Codewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.

www.codewars.com

숫자로 이뤄진 배열이 주어짐. 배열을 구성하는 숫자들은 기본적으로 세 번씩 등장하지만 x는 두 개 y는 한 개만 들어있음. x와 y를 각각 찾아서 x*x*y 값을 구하는 문제.

1. 최초

function missingValues(arr) {
  let x, y;
  
  for (let i = 0; i < arr.length; i++) {
    const indexesOfValue = arr.reduce((result, currentNumber, index) => {
      currentNumber === arr[i] && result.push(index);
      return result;
    }, []);

    if (indexesOfValue.length === 1) x = arr[i];
    else if (indexesOfValue.length === 2) y = arr[i];
  }
  
  return x * x * y;  
}

특정 값의 인덱스들을 구하고 길이가 1이면 해당 값을 x로, 길이가 2면 y로 저장.

 

특정 값의 인덱스를 찾는 방법은 스택오버플로우에서 찾음.

 

How to get multiple indexes of array by a value in pure JavaScript (value exact matching)

I'm trying to make indexOf return me multiple indexes of array's items which value equal "1" (exactly matching). Here's what I'm doing: var arr = [1, 11, 1, 111, 1111, 11, 1, 1111, 11]; for (i = ...

stackoverflow.com

1) arr.reduce()

이 방법을 따오면서 arr.reduce()도 다시 공부함.

 

Array.prototype.reduce()

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.

developer.mozilla.org

 

메소드 이름이 reduce인 이유는 배열 요소들을 한 값으로 reduce 해주기 때문. for와 비슷한 일을 하지만 변수를 변환하지 않음.

array.reduce(callback(accumulator, currentValue, index), initialValue);

index와 initialValue는 필수 아님.

accumulator는 콜백함수의 리턴값을 저장함. currentValue는 현재 함수에 들어간 값. index는 currentValue의 인덱스. initialValue를 넣었을 땐 index가 0에서 시작, 아니면 1부터 올라감.

2) &&와 if

그런데

currentNumber === arr[i] && result.push(index);

이 줄이 이해가 안 돼서 찾아봄.

알고보니 && 특성을 이용해서 if 기능을 구현한 것.

 

&&나 ||는 불리언이 아닌 피연산자의 값을 내보냄.

&&는 왼쪽부터 falsey 값을 찾음. falsey 값이 나오면 연산 중지. 끝까지 falsey 값이 안 나오면 마지막 값을 찍어냄.

 

따라서 위 코드에서 currentNumber가 arr[i]와 다르면 거기서 멈출 테고 같으면 result.push(index)를 처리. 즉

if (currentNumber === value) result.push(index);

이렇게 쓰는 것과 같음. 나중에 써먹어봐야지..

 

Javascript && operator versus nested if statements: what is faster?

Now, before you all jump on me and say "you're over concerned about performance," let it hereby stand that I ask this more out of curiosity than rather an overzealous nature. That said... I am cu...

stackoverflow.com

속도는 거의 차이가 나지 않는다고 함.

2. 효율화와 단순화

코드를 조금 더 깔끔하게 쓰고 싶다는 생각이 듦. 그리고

const indexesOfValue = arr.reduce((result, currentNumber, index) => {
  currentNumber === arr[i] && result.push(index);
  return result;
}, []);

이 부분을 밖으로 빼내고 싶었음.

1) arr.reduce()를 별도 함수로

function missingValues(arr) {
  let x, y;
  
  for (let i = 0; i < arr.length; i++) {      
    if (findIndexes(arr, arr[i]).length === 1) x = arr[i];
    else if (findIndexes(arr, arr[i]).length === 2) y = arr[i];
  }
  
  return x * x * y;  
}

function findIndexes(array, value) {
  const indexesOfValue = array.reduce((result, currentNumber, index) => {
    if (currentNumber === value) result.push(index);
    return result;
  }, []);  

  return indexesOfValue;
}

findIndexes라는 함수를 만듦. 배열, 찾는 값을 매개변수로.

2) 조건 추가

x와 y 값을 찾아도 배열 끝까지 for로 도는 문제가 있다고 판단. 그래서 x 또는 y가 undefined일 때만 조건문을 돌릴 방법을 생각해봄.

function missingValues(arr) {
  let x, y;
  
  for (let i = 0; i < arr.length; i++) {
    if (!!x || !!y) {      
      if (findIndexes(arr, arr[i]).length === 1) x = arr[i];
      else if (findIndexes(arr, arr[i]).length === 2) y = arr[i];
    }
  }
  
  return x * x * y;  
}

(!!x || !!y)라는 조건을 추가했으나 실패(NaN).

||는 왼쪽부터 truthy 값을 찾다가 끝까지 못 찾으면 마지막 값을 내보내는데 !!y가 애초에 false이기 때문에 안쪽 조건문을 아예 실행하지 않기 때문.

 

그래서 그냥 직접적으로 씀. 최종 답변은

function missingValues(arr) {
  let x, y;
  
  for (let i = 0; i < arr.length; i++) {
    if (x === undefined || y === undefined) {      
      if (findIndexes(arr, arr[i]).length === 1) x = arr[i];
      else if (findIndexes(arr, arr[i]).length === 2) y = arr[i];
    }
  }
  
  return x * x * y;  
}

function findIndexes(array, value) {
  const indexesOfValue = array.reduce((result, currentNumber, index) => {
    if (currentNumber === value) result.push(index);
    
    return result;
  }, []);  

  return indexesOfValue;
}

3. 다른 답변

코드를 제출하고 다른 답변들을 살펴보는데 아래 답변이 마음에 들었음.

function missingValues(arr) {
 const x = arr.find(item => arr.filter(element => element === item).length === 1);
 const y = arr.find(item => arr.filter(element => element === item).length === 2);
 return x * x * y
}

find()는 배열에서 조건을 만족하는 값을 내보냄. 

 

Array.prototype.find()

The find() method returns the value of the first element in the provided array that satisfies the provided testing function.

developer.mozilla.org

그리고 filter()는 조건을 만족하는 요소를 모두 담아서 새로운 배열로 내보냄.

 

Array.prototype.filter()

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

developer.mozilla.org