+7

Phép xử lý Map, Filter, Reduce trong Swift

Các phép xử lý map, filter, reduce là các phép xử lý được áp dụng trên các đối tượng dạng collection như Array, Dictionay, Set. Nó cho phép người lập trình dễ dàng thao tác với kiểu dữ liệu collection và làm code trở nên ngắn ngọn và rõ ràng hơn. Trong phạm vi bài viết sẽ lấy một số vi dụ cơ bản để mô tả cho các phép xử lý này trên ngôn ngữ lập trình Swift.

Map

Sử dụng phép xử lý map để lặp duyệt qua từng phần tử trong một collection, đồng thời sử dụng các phép biến đổi để thay đổi giá trị của từng phần tử. Kết quả cuối cùng của phép xử lý map là một mảng các phần tử của collection cũ sau khi áp dụng các phép biến đổi.

Ví dụ: Ta có một mảng A: [Double], và muốn có một mảng B: [Double] có phần tử là luỹ thừa của phần từ trong A [x1, x2, x3, x4] => [x1 * x1, x2 * x2, x3 * x3, x4 * x4]

Với phong cách truyền thống ta sử dụng vòng lặp để duyệt từng phần tử trong mảng và tính giá trị luỹ thừa cả từng phần tử trong A như code dưới đây:

let A = [2.0,4.0,5.0,7.0]
var B: [Double] = []
for value in A {
  B.append(value*value)
}

Ta sẽ có được kết quả tương ứng với code ngắn gọn hơn nếu sử dụng phép xử lý map

let A = [2.0,4.0,5.0,7.0]
let B = A.map {$0 * $0}
// [4.0, 16.0, 25.0, 49.0]

Qua ví dụ trên ta thấy rằng phép xử lý map sử dụng closure làm tham số, và toàn bộ các phép tính toán đối phần tử trong mảng sẽ được định nghĩa trong closure này. Ta có thể sử dụng các cú pháp ngắn gọn hoặc tường minh của closure đối với phép xử lý map. Đoạn xử lý map ở trên có thể được viết với các cách khác nhau như dưới đây.

Cách 1:

let A = [2.0,4.0,5.0,7.0]
let B = A.map {$0 * $0}

Cách 2:

let A = [2.0,4.0,5.0,7.0]
let B = A.map {value in value * value}

Cách 3:

let A = [2.0,4.0,5.0,7.0]
let B = A.map({
  (value: Double) -> Double in 
      return value * value
})

Cách 4:

let A = [2.0,4.0,5.0,7.0]
let B = A.map({
  (value: Double) -> Double in  value * value
})

Ngoài việc áp dụng phép xử lý map đối với Array, ta còn có thể sử dụng map đối với bất kỳ kiểu dữ liệu collection khác như Set hoặc Dictionary

Ví dụ với kiểu dữ liệu Set

let widthInMeters: Set = [4.0,6.2,8.9]
let widthInFeet = widthInMeters.map {meters in meters * 3.2808}

Ví dụ với kiểu dữ liệu Dictionary

let milesToPoint = ["point1":120.0,"point2":50.0,"point3":70.0]
let kmToPoint = milesToPoint.map { name,miles in miles * 1.6093 }

Để biết rõ hơn về kiểu dữ liệu của từng tham số trong closure, ta có thể dựa vào chức năng gợi nhớ của Xcode (như hình dưới đây)

Filter

Sử dụng phép xử lý filter duyệt qua tất cả các phần tử có trong collection và chỉ lấy ra các phần tử thoả mãn điều kiện cho trước và bổ sung vào mảng kết quả. Ví dụ: Ta muốn lấy ra các số chẵn có trong một mảng số nguyên cho trước

Với cách thông thường không sử dụng filter thì sẽ có dạng như sau:

let digits = [1, 4, 10, 15]
var even: [Int] = []
for value in digits {
  if (value % 2) == 0 {
      even.append(value*value)
    }
}

Khi sử dụng filter ta sẽ có:

let digits = [1,4,10,15]
let even = digits.filter { $0 % 2 == 0 }
// [4, 10]

Reduce

Phép xử lý reduce được sử dụng để kết hợp tất cả các phần tử trong một collection thành một kết quả đầu ra. Ví dụ: ta muốn tính tổng tất cả các giá trị có trong một mảng array Phép xử lý reduce gồm 2 tham số

  • Gia trị khởi tạo
  • Closure kết hợp Áp dụng cho ví dụ trên, ta sẽ có đoạn chương trình như sau:
let items = [2.0, 4.0, 5.0, 7.0]
let total = items.reduce(0.0, combine: +)
// 18.0

Phép xử lý combine dùng toán tử + cũng có thể áp dụng với kiểu dữ liệu String

let codes = ["abc","def","ghi"]
let text = codes.reduce("", combine: +)
// "abcdefghi"

Do phép xử lý combine có thể là một closure nên ta có thể viết lại phép reduce để tỉnh tổng một array như sau:

let items = [2.0, 4.0, 5.0, 7.0]
let total = items.reduce(0.0) {retVal, value in retVal + value}
// 18

FlatMap

FlatMap được dùng để chuyển dữ liệu dạng collection của các collection về dạng mảng Ví dụ:

let collections = [[5,2,7],[4,8],[9,1,3]]
let flat = collections.flatMap { $0 }
// [5, 2, 7, 4, 8, 9, 1, 3]

Một số ví dụ kết hợp sử dụng FlatMap một cách hiểu quả để loại các giá trị không hợp có trong các mảng

  • Loại bỏ các phần tử có giá trị nil
let people: [String?] = ["Tom",nil,"Peter",nil,"Harry"]
let valid = people.flatMap {$0}
// ["Tom", "Peter", "Harry"]
  • Loại bỏ các phần tử có giá trị là số lẻ
let collections = [[5, 2, 7], [4, 8], [9, 1, 3]]
let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } }

Chaining

Cho phép sử dụng kết quả của phép xử lý Map, Filter, Reduce trước đó làm đầu vào cho phép Map, Filter, Reduce sau này. Một số ví dụ:

  • Tính tổng các số >= 7 có trong mảng
let marks = [4, 5 ,8, 2, 9, 7]
let totalPass = marks.filter{$0 >= 7}.reduce(0,combine: +)
  • Lấy các giá trị có luỹ thừa chẵn có trong mảng
let numbers = [20,17,35,4,12]
let evenSquares = numbers.map{$0 * $0}.filter{$0 % 2 == 0}
// [400, 16, 144]

Kết luận

Mong bài viết này sẽ giúp mọi người làm quen hơn về các phép xử lý map, filter, reduce đối với kiểu dữ liệu collection.

Nguồn tham khảo

https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/ https://medium.com/@mimicatcodes/simple-higher-order-functions-in-swift-3-0-map-filter-reduce-and-flatmap-984fa00b2532


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí