문제 설명
- 유저들이 들어오고 나간 것의 로그를 찍어주는 것
- 중간에 닉네임이 변경되면 result에서도 처음부터 반영되도록하기
- 닉네임과 uid를 잘 구분
문제 접근
- result에 마지막에 찍히는 거니까 uid로 딕셔너리로 관리해주다가 마지막에 출력만 하면 될 것 같은느낌?
- 들어오고 나간거는 이제 함수로 따로 관리해주고
문제풀이
import Foundation
func solution(_ record: [String]) -> [String] {
var user_data = [String: String]()
var command_array = [String]()
// 처음돌릴때는 로그안찍고 ㄱ
func printLog(
_ type: String,
_ name: String,
_ nickname: String,
_ log: Bool = false
) {
switch type {
case "Enter":
user_data[name] = nickname
if log { command_array.append("\(user_data[name])님이 들어왔습니다.") }
case "Leave":
if log { command_array.append("\(user_data[name])님이 나갔습니다.") }
default:
if !log {
user_data[name] = nickname
}
}
}
for log in record {
let logArray = log.split(separator: " ")
let type = logArray[0]
let name = logArray[1]
let nickname = logArray.count > 2 ? logArray[2] : ""
printLog(String(type), String(name), String(nickname))
}
for log in record {
let logArray = log.split(separator: " ")
let type = logArray[0]
let name = logArray[1]
let nickname = logArray.count > 2 ? logArray[2] : ""
printLog(String(type), String(name), String(nickname), true)
}
return command_array
}
print(solution(["Enter uid1234 Muzi", "Enter uid4567 Prodo", "Leave uid1234", "Enter uid1234 Prodo", "Change uid4567 Ryan"]))
command_array
라는 정답배열에 담아주는 것인데, 닉네임이 enter
, change
를 통해 변경될 수 있으므로, 로그를 안찍은채 한번 쭉돌려주고, 이제 최신화된 친구들로한 번 더 돌려주는 것이었다.["Optional(\"Muzi\")님이 들어왔습니다.",
"Optional(\"Prodo\")님이 들어왔습니다.",
"Optional(\"Muzi\")님이 나갔습니다.",
"Optional(\"Prodo\")님이 들어왔습니다."]
이런식으로 옵셔널로찍혀서 이게 하나의 이쁜 문자열처럼 되지가 않는 것이었다.
case "Enter":
if !log {
user_data[name] = nickname
}
if log { command_array.append("\(user_data[name]!)님이 들어왔습니다.") }
case "Leave":
if log { command_array.append("\(user_data[name]!)님이 나갔습니다.") }
이 부분을 강제 언래핑을 해줬다 어처피 log
가 false
일때 이미 딕셔너리에 담겨주었기 때문에 문제는 없었다.
채점 결과
정확성: 100.0
합계: 100.0 / 100.0뭔가 그대로 두번씩 돌려주는게 더 효율적인 방법이 있을 것 같았는데, 일단 구현이 중요한 문제였는지 정답처리를 받을 수 있었다.
코드개선
똑같은 반복문 두개는 너무 원시인 코드 같아서 한 번으로 바꿔주고, 그 과정에서 튜플로 바꿔주었다.
command_array
에 반영해주면서uid
를 넣어주고, 이제 마지막에string배열
로 만드는 과정에서map
을 통해서 구조분해해주면 된다 !
import Foundation
func solution(_ record: [String]) -> [String] {
var user_data = [String: String]()
// 튜플로 바꾸기
var command_array = [(String, String)]()
// 처음돌릴때는 로그안찍고 ㄱ
for log in record {
let logArray = log.split(separator: " ")
let type = String(logArray[0])
let name = String(logArray[1])
let nickname = logArray.count > 2 ? String(logArray[2]) : ""
switch type {
case "Enter":
user_data[name] = nickname
command_array.append((type, name))
case "Leave":
command_array.append((type, name))
case "Change":
user_data[name] = nickname
default:
break
}
}
return command_array.map { command in
let (type, name) = command
let nickname = user_data[name]!
return type == "Enter" ? "\(nickname)님이 들어왔습니다." : "\(nickname)님이 나갔습니다."
}
}
타인의 코드
import Foundation
func solution(_ record:[String]) -> [String] {
let actions = ["Enter":"님이 들어왔습니다.", "Leave":"님이 나갔습니다."]
var a = [String:String]()
record.forEach {
let separated = $0.components(separatedBy: " ")
if separated.count > 2 {
a[separated[1]] = separated[2]
}
}
return record.filter { !$0.contains("Change") }.map { (value:String) -> String in
let separated = value.components(separatedBy: " ")
let newString = a[separated[1]]! + actions[separated[0]]!
return newString
}
}
Q :
components
는 무엇인가 ? split이랑 뭐가 다르지 ??A : 얘는 반환값이
Array<Substring>
이 아니라Array<String>
인 것만으로도 사용할만하다고 생각했다. 정리된 표는 다음과 같다.
또한 파라미터를 여러개 해줄 수 있어서, 좀 더 복잡한 구분이 필요할 때는 components를 사용해주는게 좋다! (다만 import Foundation 필수!! )
특징 | components(separatedBy:) | split(separator:) |
---|---|---|
구분 기준 | 문자열(String) | 문자(Character) |
반환 타입 | [String] | [Substring] |
빈 문자열 처리 | 빈 문자열 포함 | 기본적으로 빈 문자열 제거 |
구현 위치 | Foundation (NSString 기반) | Swift 표준 라이브러리 |
속도 | 다소 느림 | 더 빠름 |
구분자 복잡도 | 여러 문자로 나누기 가능 | 단일 문자 기준 |
다시 코드 설명으로 돌아가면..
separated.count>2
일때.. 즉, Leave가 아닐때로 규정해서 a(user_database)를 최신화 시켜주고, Change
를 제외하고, 값을 array에 넣어주는 것이다. 코드의 로직은 나랑 엄청 유사했는데 양은 3배 차이가 날 정도로 차이가 좀 났던 것 같다.
이번 문제풀이에서는 맨날 split만 쓰던 내가 components
활용을 얻어간다고 생각을 하면 좋을 것 같다.