
문자열 paragraph 와 금지단어들이 담긴 Array<String> banned가 주어지면, paragraph에서 banned에 포함된 문자를 제외하고 가장 많이 등장한 단어를 반환하는 문제이다.
대소문자와 특수문자는 모두 무시하고 오로지 단어만 비교하면 되는 문제이다.
class Solution {
fun mostCommonWord(paragraph: String, banned: Array<String>): String {
var result = paragraph.split("[^a-zA-Z0-9]".toRegex())
.filter { it.isNotEmpty() }
.map { word -> word.lowercase().filter { it.isLetterOrDigit() } }
.filterNot { it in banned }
.groupingBy { it }.eachCount()
return result.filterValues { it == result.values.max() }.keys.first()
}
}
전달받은 문자열을 모두 분할한 뒤 특수문자와 공백을 제거한다. 그리고 금지단어를 제거한 다음 각 단어별로 카운트해 가장 높은 값을 반환하도록 만들었다.
지난번 문제에서 substringAfter을 썻다면 이번에는 사용된 모든 단어를 비교하기 위해 split()을 사용했다.
split()은 정규식이나 기준이되는 Char혹은 String값을 전달받아 해당 값을 제거하면서, 그 값을 기준으로 String을 분할해 List를 만들어 반환한다.
예를들어 다음과 같은 문자열을 split()으로 분할하면 이런 결과가 나온다.
val str = "사건은 다가와 Ah Oh Ay 거세게 커져가 Ah Oh Ay"
println(str.split("Ah")) // "Ah"라는 String값을 기준으로 분할
// 결과 : ["사건은 다가와 ", " Oh Ay 거세게 커져가 ", " Oh Ay"]
Char값이나 String값을 사용하면 해당 값을 기준으로 분할되지만 정규식을 사용할 경우 해당 정규식에 해당하는 모든 값을 기준으로 분할된다.
Regex("[^a-zA-Z0-9]")일 경우 영문자와 숫자를 제외한 모든 기준으로 분할된다.
filter()와 filterNot()은 모두 Boolean값을 반환하는 함수를 전달받아 해당 값을 기준으로 List를 리턴한다.
filter()의 경우 조건식에 해당되는 값을 가지고 있는 List를 filterNot()은 조건식에 해당되는 값을 제거한 List를 반환한다.
val arr = arrayOf("a", "b", "a", "d", "a", "f", "a", "g")
println(arr.filter { it == "a" })
// 결과 : ["a", "a", "a", "a"]
println(arr.filterNot { it == "a" })
// 결과 : ["b", "d", "f", "g"]
map연산자는 Collection에 있는 각 요소들을 절달된 함수를 사용해 변환한뒤 새로운 Collection을 생성해 반환하는 함수이다. 따라서 전달되는 함수는 반드시 무언가를 반환 하도록 작성해야한다.
val n = listOf(1, 2, 3, 4, 5)
val result = n.map { it * 2 } // 각 원소들에 2를 곱해 반환
println(result) // [2, 4, 6, 8, 10]
groupingBy{}는 전달받은 함수를 통해 각 요소별로 Key를 생성한다. 그 후 해당 Key별로 값들을 그룹화 해서 반환하는데, groupBy()의 경우 그룹화된 Map<K, List<V>>을 반환하는 반면 groupingBy{}는 Grouping<T, K>를 반환한다.
반환된 Grouping객체의 경우 eachCount나 fold같은 그룹에 대한 연산을 추가적으로 지원한다.
map()연산자와 마찬가지로 groupingBy{}의 경우에도 Key값으로 반환되는 값이 있어야 사용이 가능하다.
Grouping객체에 지원되는 함수로 그룹화 된 각 Key별로 포함된 원소가 몇개인지 카운트 해준다. groupingBy{}의 경우 eachCount()나 fold()메서드를 통해 추가 연산을 해야 객체로 구성된 값을 map타입으로 받아 사용할 수 있다.
val s = listOf("one", "two", "three", "four")
// 각 원소의 첫째글자를 key로 지정
val groupBy = s.groupBy { it.first() }
val groupingBy = s.groupingBy { it.first() }
println(groupBy)
// groupBy의 경우 map이 반환됨 {o=[one], t=[two, three], f=[four]}
println(groupingBy)
// 추가적인 연산이 없을 경우 객체 주소 반환 (groupingBy$1@1efbd816)
println(groupingBy.eachCount())
// 각 키 별로 몇개의 원소가 있는지 카운트해서 반환 {o=1, t=2, f=1}
Map컬렉션에 지원되는 함수로 Value값을 기준으로 필터링한 Map을 반환한다. Array나 List의 filter()와 비슷하며 Value값 말고 Key값을 기준으로도 필터링이 가능하다.