안녕하세요

우테코 프리코스 1주차 - 문제 7 (리팩토링O) 본문

우테코 프리코스/1주차

우테코 프리코스 1주차 - 문제 7 (리팩토링O)

sakuraop 2022. 11. 1. 23:14

https://github.com/youngentry/javascript-onboarding/blob/main/docs/PROBLEM7.md

 

🚀 기능 요구 사항

레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.

  • 사용자와 함께 아는 친구의 수 = 10점
  • 사용자의 타임 라인에 방문한 횟수 = 1점

사용자 아이디 user와 친구 관계를 담은 이차원 배열 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.

제한사항

  • user는 길이가 1 이상 30 이하인 문자열이다.
  • friends는 길이가 1 이상 10,000 이하인 배열이다.
  • friends의 각 원소는 길이가 2인 배열로 [아이디 A, 아이디 B] 순으로 들어있다.
    • A와 B는 친구라는 의미이다.
    • 아이디는 길이가 1 이상 30 이하인 문자열이다.
  • visitors는 길이가 0 이상 10,000 이하인 배열이다.
  • 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
  • 동일한 친구 관계가 중복해서 주어지지 않는다.
  • 추천할 친구가 없는 경우는 주어지지 않는다.

실행 결과 예시

userfriendsvisitorsresult

"mrko" [ ["donut", "andole"], ["donut", "jun"], ["donut", "mrko"], ["shakevan", "andole"], ["shakevan", "jun"], ["shakevan", "mrko"] ] ["bedi", "bedi", "donut", "bedi", "shakevan"] ["andole", "jun", "bedi"]

 

 


## ✔구현 기능 리스트

-   1단계) user와 친구인 배열만을 담은 myFriends 배열생성
-   2단계) 점수 카운트 조건에 유효한 친구관계만을 남긴 connection 배열 생성하기
    -   배열 구성요소가 모두 친구인 경우 제외
    -   모두 친구가 아닌 경우 제외
-   3단계) 유효한 친구관계 또는 방문자의 점수를 기록할 객체 생성
-   4단계) 점수를 카운트 하기 전 미리 이름 순으로 정렬
-   5단계) 규칙에 따라 점수 기록
-   6단계) 점수가 가장 높은 순으로 정렬
-   7단계) 형식에 맞게 배열로 변환하여 결과를 반환

function problem7(user, friends, visitors) {
    // 1단계) user와 친구인 배열만을 담은 myFriends 배열생성
    const myFriends = [];

    for (let i = 0; i < friends.length; i++) {
        if (friends[i][1].includes(user)) {
            myFriends.push(friends[i][0]);
        }
        if (friends[i][0].includes(user)) {
            myFriends.push(friends[i][1]);
        }
    }

    // 2단계) 점수 카운트 조건에 유효한 친구관계만 남기기
    const connection = friends.filter((el) => el[0] !== user && el[1] !== user);
    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0]) && myFriends.includes(connection[i][1])) {
            connection.splice(i, 1);
        } else if (!myFriends.includes(connection[i][0]) && !myFriends.includes(connection[i][1])) {
            connection.splice(i, 1);
        }
    }

    // 3단계) 조건에 유효한 친구관계 또는 방문자의 점수를 기록할 객체 생성
    const scores = {};
    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0])) {
            scores[connection[i][1]] = 0;
        }
        if (myFriends.includes(connection[i][1])) {
            scores[connection[i][0]] = 0;
        }
    }
    for (let i = 0; i < visitors.length; i++) {
        if (!myFriends.includes(visitors[i])) {
            scores[visitors[i]] = 0;
        }
    }

    // 4단계) 점수를 카운트 하기 전 미리 이름 순으로 정렬
    const orderedScoresArray = Object.entries(scores).sort();
    const orderedScoresObject = Object.fromEntries(orderedScoresArray);

    // 5단계) 규칙에 따라 점수 기록
    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0])) {
            orderedScoresObject[connection[i][1]] += 10;
        }
        if (myFriends.includes(connection[i][1])) {
            orderedScoresObject[connection[i][0]] += 10;
        }
    }
    for (let i = 0; i < visitors.length; i++) {
        if (!myFriends.includes(visitors[i])) {
            orderedScoresObject[visitors[i]] += 1;
        }
    }

    // 6단계) 점수가 가장 높은 순으로 정렬
    const orderedArray = Object.entries(orderedScoresObject).sort((a, b) => b[1] - a[1]);

    // 7단계) 형식에 맞게 배열로 변환하여 결과를 반환
    const result = orderedArray.map((el) => el[0]);
    return result;
}

module.exports = problem7;

리팩토링 전 코드 (문제 이해를 잘못하여 테스트케이스에서 실패하여 로직만 약간 겹치고 리팩토링 후와는 거의 다른 코드입니다.)

function problem5(user, friends, visitors) {
    const myFriends = [];
    for (let i = 0; i < friends.length; i++) {
        if (friends[i][1].includes(user)) {
            myFriends.push(friends[i][0]);
        }
        if (friends[i][0].includes(user)) {
            myFriends.push(friends[i][1]);
        }
    }
    //  ===> ["donut", "shakevan"]

    const possibleVisitors = visitors.filter((el) => !myFriends.includes(el));
    const possibleFriends0 = friends.filter((el) => !el[0].includes(user));
    const possibleFriends1 = possibleFriends0.filter((el) => !el[1].includes(user));
    const recommendationScore = {};

    for (let i = 0; i < possibleFriends1.length; i++) {
        for (let j = 0; j < myFriends.length; j++) {
            if (possibleFriends1[i].includes(myFriends[j]) && possibleFriends1[i][1] !== user && possibleFriends1[i][0] !== user) {
                recommendationScore[possibleFriends1[i][1]] = 0;
            }
        }
    }

    for (let i = 0; i < possibleVisitors.length; i++) {
        recommendationScore[possibleVisitors[i]] = 0;
    }

    const orderedScoreArray = Object.entries(recommendationScore).sort();
    const orderedScoreObject = Object.fromEntries(orderedScoreArray);

    for (let i = 0; i < possibleFriends1.length; i++) {
        for (let j = 0; j < myFriends.length; j++) {
            if (possibleFriends1[i].includes(myFriends[j]) && possibleFriends1[i][1] !== user) {
                orderedScoreObject[possibleFriends1[i][1]] += 10;
            }
        }
    }

    for (let i = 0; i < possibleVisitors.length; i++) {
        orderedScoreObject[possibleVisitors[i]] += 1;
    }

    const orderedArray = Object.entries(orderedScoreObject).sort((a, b) => b[1] - a[1]);
    return orderedArray;
}

problem5(user, friends, visitors);

리팩토링 후 코드

  • 불필요한 2중 for문의 사용을 깨닫고 모두 제거했습니다.
function problem7(user, friends, visitors) {
    const myFriends = [];

    for (let i = 0; i < friends.length; i++) {
        if (friends[i][1].includes(user)) {
            myFriends.push(friends[i][0]);
        }
        if (friends[i][0].includes(user)) {
            myFriends.push(friends[i][1]);
        }
    }

    const connection = friends.filter((el) => el[0] !== user && el[1] !== user);
    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0]) && myFriends.includes(connection[i][1])) {
            connection.splice(i, 1);
        } else if (!myFriends.includes(connection[i][0]) && !myFriends.includes(connection[i][1])) {
            connection.splice(i, 1);
        }
    }

    const scores = {};
    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0])) {
            scores[connection[i][1]] = 0;
        }
        if (myFriends.includes(connection[i][1])) {
            scores[connection[i][0]] = 0;
        }
    }
    for (let i = 0; i < visitors.length; i++) {
        if (!myFriends.includes(visitors[i])) {
            scores[visitors[i]] = 0;
        }
    }

    const orderedScoresArray = Object.entries(scores).sort();
    const orderedScoresObject = Object.fromEntries(orderedScoresArray);

    for (let i = 0; i < connection.length; i++) {
        if (myFriends.includes(connection[i][0])) {
            orderedScoresObject[connection[i][1]] += 10;
        }
        if (myFriends.includes(connection[i][1])) {
            orderedScoresObject[connection[i][0]] += 10;
        }
    }
    for (let i = 0; i < visitors.length; i++) {
        if (!myFriends.includes(visitors[i])) {
            orderedScoresObject[visitors[i]] += 1;
        }
    }

    const orderedArray = Object.entries(orderedScoresObject).sort((a, b) => b[1] - a[1]);

    const result = orderedArray.map((el) => el[0]);
    return result;
}

module.exports = problem7;