+2

Giải quyết một số vấn đề Collection với Kotlin

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)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()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()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:

  1. Chia nhỏ string thành mảng: words: List<String>

  2. Chuyển mảng cấm banned: Array<String>thành HashSet.

  3. 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ểu Grouping. 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 trong Grouping chúng ta sử dụng eachCount()

  4. 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

Viblo
Let's register a Viblo Account to get more interesting posts.