🚀 기능 요구 사항

레벨 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 이하인 배열이다.
  • 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
  • 동일한 친구 관계가 중복해서 주어지지 않는다.
  • 추천할 친구가 없는 경우는 주어지지 않는다.

실행 결과 예시

user friends visitors result
“mrko” [ [“donut”, “andole”], [“donut”, “jun”], [“donut”, “mrko”], [“shakevan”, “andole”], [“shakevan”, “jun”], [“shakevan”, “mrko”] ] [“bedi”, “bedi”, “donut”, “bedi”, “shakevan”] [“andole”, “jun”, “bedi”]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function problem7(user, friends, visitors) {
if (
typeof user !== "string" ||
!Array.isArray(friends) ||
!Array.isArray(visitors)
)
throw new Error(
"user는 문자열, friends는 배열, visitors는 배열이여야 합니다."
);
if (!user.length || user.length > 30)
throw new RangeError("user는 길이가 1 이상 30 이하인 문자열이여야 합니다.");
if (!friends.length || friends.length > 10000)
throw new RangeError(
"friends는 길이가 1 이상 10,000 이하인 배열이여야 합니다."
);
if (visitors.length < 0 || visitors.length > 10000)
throw new RangeError(
"visitors는 길이가 0 이상 10,000 이하인 배열이여야 합니다."
);

// 유저의 친구들
let userFriends = [];
friends.forEach((friend) => {
if (friend.includes(user)) userFriends = [...userFriends, ...friend];
});
userFriends = userFriends.filter((friend) => friend !== user);

// 유저 친구의 친구들 => 알 수도 있는 친구들
let mayKnowUsers = [];
friends.forEach((friend) => {
userFriends.forEach((userFriend) => {
if (friend.includes(userFriend))
mayKnowUsers = [...mayKnowUsers, ...friend];
});
});

mayKnowUsers = mayKnowUsers.filter((mayKnowUser) => {
return mayKnowUser !== user && !userFriends.includes(mayKnowUser);
});

// 유저 점수 계산
const userCount = {};
mayKnowUsers.forEach((mayKnowUser) => {
userCount[mayKnowUser] = (userCount[mayKnowUser] ?? 0) + 10;
});

visitors.forEach((visitor) => {
if (visitor === user || userFriends.includes(visitor)) return;
userCount[visitor] = (userCount[visitor] ?? 0) + 1;
});

// 점수 기준 내림차순 정렬
const sortFunc = (userA, userB) => {
const numA = userCount[userA];
const numB = userCount[userB];

if (numA - numB > 0) return -1;
else if (numA - numB < 0) return 1;
else return userA < userB ? -1 : userA === userB ? 0 : 1;
};

const userId = Object.keys(userCount);

return userId.sort((userA, userB) => sortFunc(userA, userB)).slice(0, 5);
}
  • 유저의 친구들을 구하였다.
  • 유저의 친구들의 친구들을 구하였다. ⇒ 10점을 매기기 위해
  • 방문객과 유저의 친구의 친구들을 순회하면서 점수를 매겼다.
  • 점수를 기준으로 내림차순 정렬과 동일 점수일 때 알파벳 순으로 정렬을 해주었다.

🏓 소감

이 문제는 처음에 한국말을 이해하기 어려워서 오랫동안 고전했던 문제이다. 친구에게 문제를 차근히 설명해보면서 요구사항에 대해 파악할 수 있었다.

  • 시간이 부족하여 6번문제와 7번문제는 리팩터링을 많이 못한 것이 아쉬웠다.
  • 이전까지 sort() 메서드는 단순히 오름차순, 내림차순 정렬에만 사용되는 것이라고 생각했는데, 이번 기회에 sort() 메서드는 두개의 인자를 받아서 -1, 0, 1 값을 return 하는 것을 기준으로 배열의 index를 직접 변경하는 메서드이라는 것을 제대로 알게되었다.