내 장점이 뭔지 알아 ? 바로 솔직한거야~ - (2) 2021 KAKAO BLIND 모아보긔

yoorabaek·2022년 9월 22일
0

no problem! 

목록 보기
3/3
post-thumbnail

메뉴 리뉴얼

메뉴 리뉴얼 - 프로그래머스

처음 생각한 solution

orders의 각 요소를 돌면서 가능한 2개 이상의 조합을 모두 검사해서 2번 이상 다른 order(주문조합)에 등장하는 경우를 모두 구해서 answer에 push 하고 최종적으로 만들어진 answer을 반환하면 되지 않을까?

function solution(orders, course) {
    var answer = [];
    
    // orders에서 가능한 2개 이상 조합을 모두 검사하여 해당 조합이 다른 order에도 2회 이상
    // 등장하는 경우 answer에 해당 조합을 추가한다
    orders
    .map(val => val.split('').sort().join(''))
    .forEach((order, index) => {
        // order에서 각 조합 검사하기 (이진법 활용)
        for (let i = 1; i <= 2 ** (order.length); i++) {
            let binArr = i.toString(2).split('');
            if (binArr.filter(x => x == 1).length !== 1) {
                let count = 0;
                let selected = order.split('').filter((x, i) => binArr[i] == 1);
                orders.forEach((ord, idx) => {
                    let flag = true;
                    selected.forEach(x => {
                        if (!ord.includes(x)) {
                            flag = false;
                            return;
                        }
                    })
                    if (flag) count++;
                })
                if (count >= 2 && !answer.includes(selected.join(''))) {
                    answer.push(selected.join(''));
                }
            }
        }
        
    })
    
    return answer.sort();
}

오우 벨로그 코드 테마 ... 머선일

어쨌든 자세히 보고 싶은 사람은 코드 참고하시라는 의미에서 실어두긴 하겠슴다

이렇게 해서 코드 돌렸더니 결과는..


물론 정답인 것도 하나 있었으나 보니까 제가 생각한 방식으로 하니까
정답 요소들도 포함이 되어있긴 한데 모든 조합을 다 구해서 정답이 아니더랍니다..
그래서 다시 눈 씻고 문제를 읽어보니 진짜 말도안되는 멍충이 짓을 했다..ㅎㅎ 여기에 쓰기도 좀 부끄럽지만 course라는 몇 개 조합을 구할 건지 주어지는 배열을 놓친 것이다...

function solution(orders, course) {
    let answerCourse = Array.from({ length: course.length }, () => []);
    let courseMax = new Array(course.length).fill(2);
    // orders에서 가능한 2개 이상 조합을 모두 검사하여 해당 조합이 다른 order에도 2회 이상
    // 등장하는 경우 answer에 해당 조합을 추가한다
    orders
    .map(val => val.split('').sort().join(''))
    .forEach((order, index) => {
        // order에서 각 조합 검사하기 (이진법 활용)
        for (let i = 1; i <= 2 ** (order.length); i++) {
            let binArr = i.toString(2).split('');
            if (course.includes(binArr.filter(x => x == 1).length)) {
                let count = 0;
                let selected = order.split('').filter((x, i) => binArr[i] == 1);
                let len = selected.length;
                let curIdx = course.indexOf(len);
                orders.forEach((ord, idx) => {
                    let flag = true;
                    selected.forEach(x => {
                        if (!ord.includes(x)) {
                            flag = false;
                            return;
                        }
                    })
                    if (flag) count++;
                }) 
                if (count > courseMax[curIdx]) {
                    courseMax[curIdx] = count
                    answerCourse[curIdx] = [ selected.join('')]
                } else if (count == courseMax[curIdx] && !answerCourse[curIdx].includes(selected.join(''))) {
                    answerCourse[curIdx].push(selected.join(''))
                }
            }
        }   
    })
    return answerCourse.flat().sort()    
}

수정해서 제출한 결과 일단 샘플 코드는 모두 통과했으나 정확성 테스트의 40%만 통과할 수 있었다 ㅠ

내 코드 어디가 문제니...??


문제점 발견

일단 내가 작성한 solution은 만약에 남이 작성한 코드다 생각하고 읽어보니 일단 가독성이 💩이다.

왜 만들었는지 생각하기 복잡한 초기에 선언한 배열 변수들이며

orders에 중첩 사용되는 map, forEach 내부에서 마구 돌리는 for문들의 향연이다... 이렇게 되니 지금처럼 일부 테스트가 통과되지 않는 상황에서도 대체 뭣이 문제인지 코드 유지보수가 너무 힘들어진다 (몸소 느껴버림).

일단 정확성을 통과하는 건 둘째치고 코드를 좀 더 간결하게 작성해 봐야겠다는 생각이 들었다.
그래서 배우자는 생각으로 다수가 제출한 코드를 확인해봤는데,


🤓 가독성이 좋은 solution !?

function solution(orders, course) {
  const ordered = {};
  const candidates = {};
  const maxNum = Array(10 + 1).fill(0);
  const createSet = (arr, start, len, foods) => {
    if (len === 0) {
      ordered[foods] = (ordered[foods] || 0) + 1;
      if (ordered[foods] > 1) candidates[foods] = ordered[foods];
      maxNum[foods.length] = Math.max(maxNum[foods.length], ordered[foods]);
      return;
    }

    for (let i = start; i < arr.length; i++) {
      createSet(arr, i + 1, len - 1, foods + arr[i]);
    }
  };

  orders.forEach((od) => {
    // arr.sort는 기본적으로 사전식 배열
    const sorted = od.split('').sort();
    // 주문한 음식을 사전순으로 배열해서 문자열을 만든다.
    // course에 있는 길이만 만들면 된다.
    course.forEach((len) => {
      createSet(sorted, 0, len, '');
    });
  });

  const launched = Object.keys(candidates).filter(
    (food) => maxNum[food.length] === candidates[food]
  );

  return launched.sort();
}

(출처 : 프로그래머스 - 다른 사람의 풀이)

What I learn !

(A) 변수 선언이 명확하고 문제에서 설명한 해결방법이 보이게 설정했다
문제 설명부분에서 2번 이상 함께 주문된 단품 메뉴들의 조합부터 선택될 수 있는 후보라고 한 점과 일단 이 후보를 모아둔 조합 외에 기본적으로 주문한 조합에 대한 횟수 정보를 담아서 조건식을 통해 후보 조합으로 보낼지 검사하는 과정이 그리고 maxNum이라는 누가봐도 최대값을 업데이트하기 위해 만든 배열 등 해당 부분이 내가 어떻게 해야 객체 하나라도 덜 쓸까 고민해서 변수 한개 줄이고 대신 로직이 길고 복잡해진 케이스보단 이게 더 깔끔할 수 있겠다는 생각이 들었다.. 남들이 봤을 때 이해도 쉽고!?

(B) 순열&조합은 재귀함수로 해결이 가능한 대표적인 예다
예전에 공부하면서 정리했던 문제 유형별로 어떻게 풀 수 있는지 샘플 코드를 모아둔 깃허브 저장소 자료구조 알고리즘 with javascript를 확인해 보니 순열 / 조합이 문제 해결에 필요한 case가 Recursion function의 대표적인 예시였던 것..
물론 재귀함수와 반복문은 재귀함수가 좀 더 반복되는 기능을 모듈화 했을 뿐 로직이 더 간단하거나 한 것은 아니지만 코드의 가독성 면에서 좀 더 쉽게 읽히는 것 같고 그럼으로 인해 유지보수가 쉬워진다. 지금 내가 반복문만을 사용해서 작성한 코드는 기능의 가장 핵심인 최소 작업 단위가 어떤 것인지 파악하기 어렵다.



순위 검색

순위 검색 - 프로그래머스


작성중..

0개의 댓글