All Articles

reduce와 lodash로 최빈값을 계산해보자

어쩌다보니 면접을 진행하는 사람으로 들어가게 되는데, 코딩테스트를 진행하게 된다. 그 때는 보통 배열을 다루는 실력을 보게 된다.

하지만 안타깝게도 쉬운 문제이지만 제대로 해결하지 못하는 경우가 대다수이다. 그 중의 문제 한 가지를 가져와봤다.

최빈값을 계산해보기

최빈값(mode)를 구하는 방법은 여러가지가 있는데 그 중 3가지를 소개하고 한다.

모두 다 O(n)의 시간복잡도를 가지고 있다.

const arr = [100, 200, 300, 200, 200, 500, 500, 600];

배열이 이렇게 주어졌을때 가장 빈도수가 많은 값을 리턴해주면 되는 간단한 문제다. 말만 들으면 정말 간단해보이지만 사실 그렇게만 간단하지는 않을 것이다.

이 문제는 배열을 다루는 것 뿐만 아니라 Object key, value를 다룰 줄 아는지 시험하는 의도 또한 포함되어 있다.

보통 여기에서 빠지는 함정은 모든 것을 한번에 해결하려고 한다는 것이다. 하지만 한번에 하는 방법은 코드만 복잡할 뿐만 아니라 추천할만 하지도 않다.

정석적으로 풀어보기

일단 코드를 타이핑 하기전에 말로 절차를 설명해보자. 말로 매끄럽게 설명할 수 있다면 그걸 그대로 코드로 적으면 된다.

  1. 정리된 결과를 보여줄 새로운 newObject를 선언한다.
  2. 배열을 순회한다. 이미 있던 값이면 value에 count + 1을 해주고 만약 없던 값이면 새로운 키값을 만들어준다.
  3. newObject의 첫번째 키값과 첫번째 value값을 임시로 최빈값으로 지정해준다. (modeValue, modeKey)
  4. newObject의 키값들을 순회하면서 더 큰 value가 있다면 modeValue와 modeKey를 바꾸어준다.
  5. 결과값을 리턴한다.
const arr = [100, 200, 300, 200, 200, 500, 500, 600];

const getMode = (arr) => {
  /**
   * 1. 배열들을 순회하면서 해당하는 value들을 키값, count를 value로 하는 Object를 만든다.
   * 2. 만들어진 Object에 있는 value들을 확인하며 최대값을 확인한다.
   */
  const newObject = {};
  arr.forEach((item) => {
    if (newObject[item]) {
      newObject[item] += 1;
    } else {
      newObject[item] = 1;
    }
  });

  /*
  만들어진 새로운 newObject에서 첫번째 값을 최빈값으로 설정해두고
  Object.keys로 순회하면서 더 큰 value를 가진 값이 있으면 mode를 바꾸어 준다.
  */
  let modeValue = newObject[Object.keys(newObject)[0]];
  let modeKey = Object.keys(newObject)[0];

  for (const item in newObject) {
    if (newObject[item] > mode) {
      mode = newObject[item];
      modeKey = Object.keys(newObject)[mode];
    }
  }
  return modeKey;
};

const result = getMode(arr);
console.log(`Mode is ${result}`);

Reduce로 풀어보기

이렇게 풀면 깔끔하게 푼 것이다. 하지만 만약에 자신이 reduce등, 배열 함수를 잘 사용한다면 아래와 같이 풀면 더 세련되게 풀 수 있을 것이다.

const newObject = arr.reduce((acc, cur) => {
  acc.hasOwnProperty(cur) ? (acc[cur] += 1) : (acc[cur] = 1);
  return acc;
}, {});

const modeKey = Object.keys(newObject).reduce((acc, cur) =>
  newObject[acc] > newObject[cur] ? acc : cur
);
return modeKey;


lodash로 구해보기

const result = _.chain(arr).countBy().pairs().max(_.last).head().value()

코드가 훨씬 더 간단해졌다. 설명해보자면 아래와 같다.

  • chain: lodash wrapper를 만들어서 연속으로 lodash함수를 사용할 수 있게끔 만드는 함수
  • countBy: list에 있는 목록을 그룹으로 정렬해주고, 각각에 대한 카운트를 반환
  • pairs: Object를 (key, value)로 반환
  • max: 말 그대로 최댓값 반환
  • last: 마지막 개체 반환
  • head: Array의 첫번째 값 반환
  • value: Extracts the value of a wrapped object.

lodash 함수가 더 코드가 깔끔하지 않나 싶은데 만약 최빈값을 구해야하는 경우가 많다면 helper 함수를 따로 만들어 추상화시키는 방법이 더 좋을 것이다.