Giải quyết một số vấn đề Collection với Kotlin
Bài đăng này đã không được cập nhật trong 4 năm
Nguồn: https://proandroiddev.com/solving-coding-problems-with-kotlin-collection-functions-3d2b1ef7fe2c
Như chúng ta đã biết thì nhiều nhà phát triển Android đều muốn sử dụng ngôn ngữ tuyệt vời như Kotlin để lập trình và tôi cũng thế khi tôi bắt đầu hoc Koltin thì tôi thường vào https://kotlinglang.org để học và cùng với nhiều chức năng tuyệt vời khác. Tôi đã bị ấn tượng với operations và collection của kotlin. Từ khi đó tới giờ tối vẫn sử dụng Kotlin cho việc lập trình Android, và tôi nhận ra rằng khó mà chúng ta có thể sử dụng hết tiềm năng của ngôn ngữ này mang lại cho chúng ta.
Trong suốt những năm tôi còn code Java để lập trình Android. Tôi vẫn không biết lợi ích mà Kotlin mang lại và thật khó để chuyển 1 ngôn ngữ mới như Kotlin, tôi vừa code Java cho việc làm ứng dụng và học thêm nữa về Kotlin, khi càng đọc và tìm hiểu nhiều về Kotin thì tôi nhận ra rằng Kotlin giảm bớt số lượng code phát sinh và cú phát đơn giản và ngắn gọn hơn rất là nhiều. Nó đã giải quyết khá nhiều vấn đề cho tôi, bài này tôi sẽ chia sẻ một số function hay về Kotlin mà có thể bạn đã gặp và đang làm.
Loops
Chúng ta cùng bắt đầu với Loop. Chúng ta sử dụng IntArray
trong mảng 10 thành phần là 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
và kết quả chúng ta muốn lấy là 123456789
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in (1 until array.size)) {
print(array[index])
}
(1 until array.size)
là IntRange
, class này sẽ đại diện cho một khoảng giá trị kiểu Int
. Giá trị chạy đầu tiên trong khoảng này là 1
và giá trị cuối là 9
chúng ta sử dụng until
để loại giá trị cuối cùng để tránh trường hợp ArrayIndexOutOfBoundsException
.
Còn nếu chúng ta muốn xuất ra chỉ 012346789
bỏ giá trị thứ 5
, thường thì chúng ta sử dụng if
để loại phần tử thứ 5. vậy nếu không dùng if
thì sẽ thế nào?
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in array.indices - 5) {
print(array[index])
}
array.indices
trả về chỉ số hợp lệ cho mảng. Trong trường hợp này array.indices
đại diện cho IntRange
trong khoảng (0..9)
. Nếu dùng (0..9) - 5
kết quả sẽ nhận được là [0, 1, 2, 3, 4, 6, 7, 8, 9]
đó là kết quả chính xác mà chúng ta cần.
Kotlin hỗ trợ chúng ta lặp lại từ số lớn hơn xuống số nhỏ hơn bằng cách sử dụng downTo
. Việc lặp lại này có thể bị thay đổi bằng cách dùng step
:
val array = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
for(index in array.size - 1 downTo 1 step 2) {
print(array[index])
}
Kết quả chúng ta nhận được là 97531
Loại bỏ 1 Vowels trong String
Có bài trong LeetCode về vấn đề này :
Cho 1 mảng String, loại bỏ những Vowels 'a', 'e', 'i', 'o', và 'u' từ mảng đó và trả về 1 mảng mới.
Trong Java chúng ta có thể sử dụng Regex chỉ 1 dòng. Còn nếu không dùng Regex chúng ta có thể sẽ làm như thế này:
public String removeVowels(String S) {
StringBuilder sb = new StringBuilder();
for(char s: S.toCharArray()) {
if(s != 'a' && s != 'e' && s != 'i' && s !='o' && s != 'u') {
sb.append(s);
}
}
return sb.toString();
}
Vậy đối với Kotlin thì thế nào? có phức tạp không ? Nhưng thực tế chỉ cần sử dụng filter()
hay filterNot()
cho vấn đề này:
fun removeVowels(S: String): String {
val vowels = setOf('a', 'e', 'i', 'o', 'u')
return S.filter { it !in vowels }
}
Có phải là rất đơn giản đúng không nè
Tính tổng của một mảng
Đây cũng là một đề bài trong Leetcode số 1480.
Cho một mảng giá trị kiểu Integer. Chúng ta tính tổng của mảng như sau
runningSum[i] = sum(nums[0]…nums[i])
.Đầu vào: nums = [1,2,3,4]
Kết quả: [1,3,6,10]
Mong đợi: [1, 1+2, 1+2+3, 1+2+3+4].
Bình thường chúng ta sẽ dùng vòng lặp để tính giá trị trong mảng đó có thể qua nhiều vòng lặp ( 1 .. n vòng lặp) để lấy được kết quả như mình mong muốn.
Vậy thì Kotlin sẽ hỗ trợ cho chúng ta như thế nào trong bài toán này . Chúng ta có toán tử: fold()
và reduce()
. Chúng ta có thể vào đây đọc để rõ hơn về chức năng đó. Nhưng từ Kotlin 1.4 thì ta có nhiều hơn là: runningFold()
và runningReduce()
sẽ trả về 1 mảng cho bạn.
/**
* Returns a list containing successive accumulation values generated by applying [operation] from left to right
* to each element and current accumulator value that starts with the first element of this array.
*
* @param [operation] function that takes current accumulator value and an element, and calculates the next accumulator value.
*
* @sample samples.collections.Collections.Aggregates.runningReduce
*/
@SinceKotlin("1.4")
@kotlin.internal.InlineOnly
public inline fun IntArray.runningReduce(operation: (acc: Int, Int) -> Int): List<Int>
Nghe có vẻ phức tạp đúng không mọi người nhỉ? Chúng ta thử cùng xem qua ví dụ này thử:
fun runningSum(nums: IntArray): IntArray {
return nums.runningReduce { sum, element -> sum + element }.toIntArray()
}
Chỉ 1 đoạn code đơn giản này chúng ta đã giải quyết được vấn đề bằng cách sử dụng runningReduce()
. sum
được bắt đầu từ giá trị đầu tiên của mảng, element là phần tử kế tiếp. Nhìn ví dụ bên dưới bạn sẽ dễ hiểu hơn nhiều:
sum: 1; element: 2; sum + element: 3
sum: 3; element: 3; sum + element: 6
sum: 6; element: 4; sum + element: 10
sum: 10; element: 5; sum + element: 15
Và kết quả chúng ta nhận được là 1 mảng [1, 3, 6, 10, 15]
Từ xuất hiện nhiều nhất
Một vấn đề được đặt ra là:
Cho 1 đoạn văn và một danh sách từ cấm. Trả về từ mà xuất hiện nhiều nhất trong đoạn văn mà không có trong danh sách cấm.
Ví dụ:
Đầu vào:
Đoạn văn = "Bob hit a ball, the hit BALL flew far after it was hit."
Mảng từ cấp = ["hit"]
kết quả: "ball"
Nếu với tôi có thể tôi sẽ làm thế này :
1- Tạo thành 1 mảng bằng cách sử dụng split
:
[bob, hit, a, ball, the, hit, ball, flew, far, after, it, was, hit]
2- Tạo danh sách bị cấm
[hit]
3- Đếm số lần xuất hiên mà không nằm trong danh sách cấm.
{bob=1, a=1, ball=2, the=1, flew=1, far=1, after=1, it=1, was=1}
4- Trả về kết quả
ball
Đây là đoạn code với Java:
public String mostCommonWord(String paragraph, String[] banned) {
// 1. Covert string to lower case and split by words.
String[] words = paragraph.replaceAll("[^a-zA-Z0-9 ]", " ").toLowerCase().split("\\s+");
// 2. Create a set of banned words.
Set<String> bannedWords = new HashSet();
for (String word : banned)
bannedWords.add(word);
// 3. Create a map of words to their occurrence, excluding the banned words
Map<String, Integer> wordCount = new HashMap();
for (String word : words) {
if (!bannedWords.contains(word))
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
// 4. Return word with the highest number of occurrences from the map.
return Collections.max(wordCount.entrySet(), Map.Entry.comparingByValue()).getKey();
}
Còn với Kotlin thì sao :
fun mostCommonWord(paragraph: String, banned: Array<String>): String {
// 1. Covert string to lower case and split by words.
val words = paragraph.toLowerCase().split("\\W+|\\s+".toRegex())
// 2. Create a set of banned words.
val bannedSet = banned.toHashSet()
// 3. Create a map of words to their occurrence, excluding the banned words
val wordToCount = words.filterNot { it in bannedSet }.groupingBy { it }.eachCount()
// 4. Return word with the highest number of occurrences from the map.
return wordToCount.maxBy { it.value }!!.key
}
Giờ mình chia nhỏ từng bước ra như sau:
-
Chia nhỏ string thành mảng:
words: List<String>
-
Chuyển mảng cấm
banned: Array<String>
thànhHashSet
. -
Bước này là 1 chỗ các function được gọi. Đầu tiên chúng ta sử dụng
filterNot()
để lọc những từ cấm.filterNot { it in banned }
sẽ trả về một chuỗi và không trả về từ cấm. Không nhưgroupBy()
thì trả về map,groupingBy
trả về đối tượng kiểuGrouping
. Có nghĩ là chúng ta gộp những giá trị (Giá trị ở đây là word) hiện tại trong collection. Để đếm số lượng word trongGrouping
chúng ta sử dụngeachCount()
-
Chúng ta sử dụng
maxBy
để lấy phần tử lớn nhất trong map. Kết quả trả về làMap.Entry<String, Int>?
. , Ví du: ball = 2.
Kết thúc
Cuối cùng là mong các bạn comment bên dưới, nếu bài này có sai xót nhé mình sẽ cải thiện bài của mình hơn nữa. Cám ơn các bạn đã đọc qua
Tài liệu tham khảo :
https://proandroiddev.com/solving-coding-problems-with-kotlin-collection-functions-3d2b1ef7fe2c
All rights reserved