프로그래머스 Lv.1 - 92334 신고 결과 받기
프로그래머스 Lv.1 - 92334 신고 결과 받기 문제 풀이
문제 설명
신입사원 무지는 게시판 불량 이용자를 신고하고 처리 결과를 메일로 발송하는 시스템을 개발하려 합니다. 무지가 개발하려는 시스템은 다음과 같습니다.
- 각 유저는 한 번에 한 명의 유저를 신고할 수 있습니다.
- 신고 횟수에 제한은 없습니다. 서로 다른 유저를 계속해서 신고할 수 있습니다.
- 한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 1회로 처리됩니다.
- k번 이상 신고된 유저는 게시판 이용이 정지되며, 해당 유저를 신고한 모든 유저에게 정지 사실을 메일로 발송합니다.
- 유저가 신고한 모든 내용을 취합하여 마지막에 한꺼번에 게시판 이용 정지를 시키면서 정지 메일을 발송합니다.
다음은 전체 유저 목록이 ["muzi", "frodo", "apeach", "neo"]이고, k = 2(즉, 2번 이상 신고당하면 이용 정지)인 경우의 예시입니다.
유저 ID유저가 신고한 ID설명
"muzi" | "frodo" | "muzi"가 "frodo"를 신고했습니다. |
"apeach" | "frodo" | "apeach"가 "frodo"를 신고했습니다. |
"frodo" | "neo" | "frodo"가 "neo"를 신고했습니다. |
"muzi" | "neo" | "muzi"가 "neo"를 신고했습니다. |
"apeach" | "muzi" | "apeach"가 "muzi"를 신고했습니다. |
각 유저별로 신고당한 횟수는 다음과 같습니다.
유저 ID신고당한 횟수
"muzi" | 1 |
"frodo" | 2 |
"apeach" | 0 |
"neo" | 2 |
위 예시에서는 2번 이상 신고당한 "frodo"와 "neo"의 게시판 이용이 정지됩니다. 이때, 각 유저별로 신고한 아이디와 정지된 아이디를 정리하면 다음과 같습니다.
유저 ID유저가 신고한 ID정지된 ID
"muzi" | ["frodo", "neo"] | ["frodo", "neo"] |
"frodo" | ["neo"] | ["neo"] |
"apeach" | ["muzi", "frodo"] | ["frodo"] |
"neo" | 없음 | 없음 |
따라서 "muzi"는 처리 결과 메일을 2회, "frodo"와 "apeach"는 각각 처리 결과 메일을 1회 받게 됩니다.
이용자의 ID가 담긴 문자열 배열 id_list, 각 이용자가 신고한 이용자의 ID 정보가 담긴 문자열 배열 report, 정지 기준이 되는 신고 횟수 k가 매개변수로 주어질 때, 각 유저별로 처리 결과 메일을 받은 횟수를 배열에 담아 return 하도록 solution 함수를 완성해주세요.
문제의 자세한 내용은 해당 링크를 통해 확인 : 문제 링크
풀이 코드
import java.util.*;
class Solution {
public int[] solution(String[] id_list, String[] report, int k) {
// <ID, 신고한 유저 리스트> 형태로 저장하는 List 생성
Map<String, ArrayList<String>> reportUserMap = new LinkedHashMap<>();
// <ID, 이메일 전송 횟수> 형태로 저장하는 List 생성
Map<String, Integer> reportCountMap = new LinkedHashMap<>();
report = Arrays.stream(report).distinct().toArray(String[]::new); // 중복된 신고 제거
for(String s : id_list) { // id_list 순서로 초기화
reportUserMap.put(s, new ArrayList<>());
reportCountMap.put(s, 0);
}
for(String s : report) { // 신고 내역을 순회하며 동작
String[] tempStringArr = s.split(" ");
String key = tempStringArr[1];
String value = tempStringArr[0];
reportUserMap.get(key).add(value); // <ID, 신고한 유저 리스트> 형태로 저장
}
for(String s : id_list) { // id_list를 순회하며 동작
if(reportUserMap.get(s).size() >= k) { // 만약 해당 ID의 신고자가 k 이상인 경우
// 신고한 유저들의 이메일 전송 횟수 1 증가
reportUserMap.get(s)
.forEach(K -> reportCountMap.put(K, reportCountMap.getOrDefault(K, 0) + 1));
}
}
// 이메일 전송 횟수를 int[]로 변환하여 반환
return Arrays.stream(reportCountMap.values().toArray(new Integer[0]))
.mapToInt(i -> i)
.toArray();
}
}
문제 해결 전략
이라 쓰고 문제를 보고서 어떻게 풀면 될까?에 대한 생각을 정리한 항목
아이디별로 신고 당한 횟수와 이메일 전송 횟수를 기록하면서 풀면 될 것 같다고 생각하여 Map을 생각
이후 중복 신고인 경우 1회로 처리한다는 제한사항을 보고서 report의 중복을 제거하여 해결하기로 생각
위 생각으로 처음 구현을 시작했을 땐 위 풀이 코드와 조금 다르게 시작
reportUserMap을 Map<String, List<String>> 형태로 구현하여 사용하려고 했으나 List 부분에 문제 발생
List 부분을 초기화할 때, List.of() 및 Arrays.asList() 메서드를 사용해 진행했는데 이후 add() 부분에서 에러 발생
List.of() 메서드를 사용하여 List 생성 후 add()를 하려고 하는 경우, "ImmutableCollections"에서 "UnsupportedOperationException" 예외 발생
Arrays.asList() 메서드를 사용하여 List 생성 후 add()를 하려고 하는 경우, "AbstractList"에서 "UnsupportedOperationException" 예외 발생
List.of()의 경우 불변 컬렉션(ImmutableCollections)을 반환하기 때문에 add()가 불가능하여 예외가 발생하는 상태였고
Arrays.asList()의 경우 일반적인 ArrayList를 반환하는 것이 아니라 Arrays class의 inner class인 ArrayList를 반환하는데 해당 ArrayList의 add() 메서드는 미구현된 상태여서 예외가 발생하던 것
불변 컬렉션이란 요소의 추가, 제거, 수정이 불가능한 컬렉션인데 add()를 사용하여 요소를 추가하려고 했기에 예외가 발생한 것이고, List의 구현 시 AbstractList<E>를 상속(extends) 받아서 구현하게 되는데 Arrays class의 ArrayList inner class에서는 add() 메서드를 구현하지 않았기에 예외가 발생한 것
위 문제들로 인하여 List가 아닌 ArrayList로 수정하여 초기화 후 add() 메서드 동작이 가능하도록 해결
그리고 처음에는 HashMap을 사용했으나 반환시 id_list로 받은 id 순서로 결과를 반환해야 하기에 순서를 갖는 LinkedHashMap으로 수정하여 해결
마지막으로 최종 결과를 int[]을 반환해야 하는데 Map을 Integer[]로의 변환은 성공했으나 int[]로는 어떻게 변환하는지 까먹어서 구글링
stream을 통해 mapToInt로 요소를 int로 변환 후 toArray() 메서드를 통해 배열로 반환하면 됨을 확인하고 사용하여 해결
해당 문제를 풀면서 List.of()와 Arrays.asList() 메서드로 생성하는 List들의 특성에 대해 알게 되는 계기가 되었습니다.
사실 위 코드는 for문 내부에서 forEach()를 사용하기 때문에 2중 for문이 되어 그렇게 좋은 코드는 아니라고 생각하지만..
일단 생각한 대로 동작을 하였고, 통과를 한 코드이기에 이러한 풀이도 있다는 것을 공유하기 위해 작성하였습니다.
코드를 업로드해 둔 깃 허브