주어진 조건 그대로 정렬하는 함수를 구현하면 된다. String을 그대로 사용하면서 별도의 함수를 구현하는 방법도 있겠으나 그러면 String을 id와 log 내용으로 나누는 작업을 매번 해야한다. Swift는 String을 SubString으로 변경하는 작업의 Cost가 크다. 따라서 Log라는 자료형을 만들어서 처음에 모두 분리하고 시작한다.
마찬가지로 String을 다시 합치는 것도 비용이 크다. 다시 합칠 필요가 없도록 originalString 역시 해당 자료형에 저장해둔다.
좀 더 Swift 답게 풀기 위해서 별도의 함수가 아닌 Comparable 프로토콜을 활용한다. 정렬하는 함수를 “static func <”에 구현하면 된다. 이 때 주의할 점이 하나 있다.
문제의 조건 중에 log의 내용이 숫자인 경우에는 입력된 순서대로 저장 하라는 조건이 있다. 해당 조건을 구현하기 위해서는 “return true”가 아니라 “return false”를 해야 한다. 아래 예시에서 보듯이 Array의 순서대로 lhs, rhs가 아니라 뒤의 것이 lhs, 앞의 것이 rhs가 되기 때문이다. Swift 정렬 알고리즘의 내부는 Timsort로 되어 있는데 그와 연관이 있다고 추측한다.
struct Number: Comparable {
let num: Int
static func < (lhs: Number, rhs: Number) -> Bool {
print(lhs.num, rhs.num)
return false
}
}
print([1, 2, 3, 4, 5].map { Number(num: $0) }.sorted(by: <).map { $0.num })
/*
🖨️ 출력 결과
2 1
3 2
4 3
5 4
[1, 2, 3, 4, 5]
*/
나는 문자 로그인지 숫자 로그인지 판단하기 위해서 Int의 initializer를 사용했다. Int(”문자열”)을 하면 “문자열”이 숫자로만 이루어져 있다면 해당 Int 값을 아니라면 nil을 반환하기 때문이다.
✋ 하지만 여기서 주의할 점이 있다. 문자열을 Int로 바꾸었을 때 너무 큰 숫자여서 Int 자료형이 담을 수 없는 경우에도 nil이 나온다는 것이다. Int 자료형이 담을 수 있는 가장 큰 수는 “9223372036854775807”이다.
문제의 조건을 보면 로그의 길이는 최대 100인데 극단적인 예시로 아래 처럼 9가 100번 반복된 문자열이 나온다면 Int로 변환해도 nil이 나오게 된다.
따라서 나는 log의 첫번째 글자만 따와서 문자 로그인지 숫자 로그인지 판단하는 기준으로 사용했다.
print(Int("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"))
//🖨️ 출력 결과 -> nil
이외 설명은 주석으로 갈음한다.
// 로그 자료형
struct Log: Comparable {
let originalString: String
let id: String
let logs: String
let isLetter: Bool
init(_ s: String) {
self.originalString = s
// " "를 기준으로 split
let array = s.split(separator: " ").map { String($0) }
self.id = array[0]
self.logs = array.dropFirst().joined(separator: " ")
//✋ 그냥 Int로 하면 너무 커서 nil됨
self.isLetter = Int(String(array[1].first!)) == nil
}
static func < (lhs: Log, rhs: Log) -> Bool {
// 둘의 로그 타입이 일치하지 않는 경우
guard lhs.isLetter == rhs.isLetter else {
return lhs.isLetter // 문자 로그가 앞에 온다
}
// 둘 다 숫자 로그인 경우
guard lhs.isLetter else {
return false // 입력 순서대로
}
// 둘 다 문자 로그이고 둘이 로그가 일치하지 않는 경우
guard lhs.logs == rhs.logs else {
return lhs.logs < rhs.logs // 로그의 사전순
}
// 둘 다 문자 로그이고 둘의 로그가 일치하는 경우
return lhs.id < rhs.id // id의 사전순
}
}
class Solution {
func reorderLogFiles(_ logs: [String]) -> [String] {
return logs.map { Log($0) }.sorted(by: <).map { $0.originalString }
}
}