프론트엔드 개발자의 기록 공간

[프로그래머스 JavaScript] 신고 결과 받기 본문

알고리즘_JS/프로그래머스_Level1

[프로그래머스 JavaScript] 신고 결과 받기

[리우] 2022. 4. 8. 14:45

🚩 신고 결과 받기 JS

 

📖 문제 설명 : 일반 유저가 악성 유저를 신고할 수 있다. 사용자가 지정한 신고 횟수 이상으로 신고를 당한 악성 유저는 차단을 당하고, 차단 당한 사실을 신고한 유저에게 메일로 알려줘야 한다.  프로그래머스 문제

 

💡 Map, Set 객체를 이용한다. 

 

처음 해결한 코드

function solution(id_list, report, k) {
  let answer = []

  let idMap = new Map()
  // key : id_list,
  // value : {reportList : 유저가 신고한 ID 목록, cnt : 자신이 신고 당한 횟수, mail : 메일을 받을 횟수}
  id_list.forEach((el) => idMap.set(el, { reportList: [], cnt: 0, mail: 0 }))

  report.forEach((el) => {
    let [userId, reportId] = el.split(' ')

    let reportValue = idMap.get(userId)
    let cntValue = idMap.get(reportId)

    // 한 유저가 같은 유저 여러 번 신고하는 경우 처리
    // 유저 ID에 유저가 신고한 ID가 포함되지 않을 경우(아직 신고한 적이 없을 경우)만
    // 신고한 ID를 찾아 자신이 신고 당한 횟수 카운트
    if (![...reportValue.reportList].includes(reportId)) {
      idMap.set(reportId, {
        reportList: cntValue.reportList,
        cnt: cntValue.cnt + 1,
        mail: 0,
      })
    }

    // 유저 ID를 찾아 유저가 신고한 ID 추가
    // Set 객체를 통해 중복 제거
    idMap.set(userId, {
      reportList: new Set([...reportValue.reportList, reportId]),
      cnt: reportValue.cnt,
      mail: 0,
    })
  })

  // 정지 userId 판별
  let blackList = []
  for (let [key, value] of idMap) {
    if (value.cnt >= k) {
      blackList.push(key)
    }
  }

  // 메일 발송 갯수 카운트
  // 유저가 신고한 ID에 정지 ID가 포함되는 경우 유저 mail 값 카운트
  blackList.forEach((id) => {
    for (let [key, value] of idMap) {
      if ([...value.reportList].includes(id)) {
        idMap.set(key, {
          reportList: value.reportList,
          cnt: value.cnt,
          mail: value.mail + 1,
        })
      }
    }
  })

  // 메일 발송
  for (let [key, value] of idMap) {
    answer.push(value.mail)
  }

  return answer
}

const id_list = ['muzi', 'frodo', 'apeach', 'neo']
const report = [
  'muzi frodo',
  'apeach frodo',
  'frodo neo',
  'muzi neo',
  'apeach muzi',
]
// console.log(solution(id_list, report, 2))

const id_list2 = ['con', 'ryan']
const report2 = ['ryan con', 'ryan con', 'ryan con', 'ryan con']
console.log(solution(id_list2, report2, 3))

👨‍💻 코드 설

1. Map 객체를 이용해서(idMap) i 번째 유저가 신고한 ID, i 번째 유저 자기 자신이 신고 당한 횟수, i 번째 유저가 메일을 받을 횟수를 기억할 resportList, cnt, mail 변수를 선언한다.

 

2. 매개변수 report를 순회하면서, 자신이 신고 당한 횟수, 자신이 신고한 유저 정보를 저장한다.

 

3. 1,2번을 수행 후 idMap을 순회하면서 자신이 신고 당한 횟수가 K 횟수 이상일 때, 유저 이름을 블랙리스트 배열에 저장한다.

 

4. idMap을 순회하면서 신고한 유저 정보에 블랙리스트 id가 포함되면 해당 유저에게 메일을 보내야 하므로 메일 횟수를 카운트한다.

 

5. 메일 횟수만 따로 배열에 저장 후 return 해준다.

 

👉 문제는 풀었지만 로직이 너무 복잡하고 이해하기 어렵다.. 리팩토링을 위해 다른 사람의 풀이 참고

 

리팩토링 코드

function solution(id_list, report, k) {
  let answer = []

  // 중복 제거 후, 유저 ID, 유저가 신고한 ID 분리
  let reports = [...new Set(report)].map((el) => el.split(' '))

  // 신고 당한 ID Map
  let reportId = new Map()

  // 신고 당한 ID 카운트
  for ([, report] of reports) {
    reportId.set(report, reportId.get(report) + 1 || 1)
  }

  // 메일 발송 ID Map
  let mailId = new Map()

  // 메일 발송 갯수 카운트
  for ([user, report] of reports) {
    if (reportId.get(report) >= k) {
      mailId.set(user, mailId.get(user) + 1 || 1)
    }
  }

  // 메일 발송 추출 (없는 경우 0)
  answer = id_list.map((id) => mailId.get(id) || 0)

  return answer
}

const id_list = ['muzi', 'frodo', 'apeach', 'neo']
const report = [
  'muzi frodo',
  'apeach frodo',
  'frodo neo',
  'muzi neo',
  'apeach muzi',
]
console.log(solution(id_list, report, 2))

const id_list2 = ['con', 'ryan']
const report2 = ['ryan con', 'ryan con', 'ryan con', 'ryan con']
console.log(solution(id_list2, report2, 3))

👨‍💻 코드 설

1. Set 객체를 이용하여 문자열 자체를 중복 제거한다. 즉 동일한 유저가 동일한 다른 유저를 신고하는 경우를 처리한다. 그 후, 유저 ID, 유저가 신고한 ID를 분리하여 reports 배열에 저장한다.

 

2. reportId Map을 선언하여 신고 당한 ID를 카운트해준다.

 

3. mailId Map을 선언하여 신고 당한 ID가 K 횟수 이상일 때, mailId에 신고를 한 유저 ID 정보를 저장한다.

 

4. 메일 발송을 위해 id_list 순서대로 순회하며 메일 발송 횟수를 저장한 후 return 해준다.

 

👉 Set 객체가 문자열 자체도 중복 제거할 수 있는지 몰랐다. 그래서 중복 제거를 위한 로직이 훨씬 깔끔해졌다. 또한 유저 ID, 신고 당한 유저 ID, 메일 발송을 각각 분리하여 조합하고 비교를 통해 훨씬 깔끔하고 직관적이고 짧은 코드로 해결할 수 있었다. 다른 사람들의 코드를 보면서도 알고리즘 푸는데 많이 도움 되는 것 같다.

 

잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.

728x90
Comments