[SwiftUI] Sort, Filter, Map

Junyoung Park·2022년 8월 19일
0

SwiftUI

목록 보기
13/136
post-thumbnail
post-custom-banner

Sort, Filter, and Map data arrays in Swift | Continued Learning #13

Sort, Filter, Map

  • 생각보다 매우 자주 사용하게 되는 고차 함수. 기본적인 for 문 등으로도 적용 가능하지만, 코드 복잡성을 줄이고 깔끔하게 한 줄로 작성할 수 있다는 점에서 매우 강력.

구현 목표

  1. 고차 함수 정렬
  2. 고차 함수 필터
  3. 고차 함수 맵

구현 태스크

  1. 고차 함수 타입에 따른 이넘을 파라미터로 받아 필터링한다.
  2. 주어진 데이터를 필터링한 결과를 보여준다.

핵심 코드

func updateFilterArray(_ filterType: FilterType) {
        switch filterType {
        case .Sort:
            let sortedArray = dataArray.sorted(by: {$0.point >= $1.point})
            filteredArray = sortedArray
        case .Filter:
            let filteredArray = dataArray.filter{$0.point > 50}
            self.filteredArray = filteredArray
        case .Map:
            let mappedArray = dataArray.compactMap({$0.name})
            // compactMap -> String? excludes all of nil things out
            self.mappedArray = mappedArray
        }
    }
    
    func updateComplexFilterArray() {
        let filteredArray = dataArray.filter{$0.isVerfied}.sorted(by: {$0.point > $1.point}).compactMap({$0.name})
        self.mappedArray = filteredArray
    }
  • compactMap 메소드는 옵셔널까지 자동으로 처리해주기 때문에 nil 핸들링을 따로 할 필요가 없다.
  • 필터링 조건을 이넘으로 준 뒤 파라미터로 받았다. 보다 편리하지만, 이 경우 정렬 조건 및 직접적인 핸들링이 필요하다면 파라미터를 더 구체적으로 받거나, 직접 이 함수 내에서 코드를 작성하면 될 것 같다.

소스 코드

import SwiftUI

struct UserModel: Identifiable {
    let id = UUID().uuidString
    let name: String?
    let point: Int
    var isVerfied: Bool
}

class ArrayModificationViewModel: ObservableObject {
    @Published var dataArray: [UserModel] = []
    @Published var filteredArray: [UserModel] = []
    @Published var mappedArray: [String] = []
    
    init() {
        getUsers()
    }
    
    func getUsers() {
        let user1 = UserModel(name: "Junyeong Park", point: 100, isVerfied: true)
        let user2 = UserModel(name: nil, point: 90, isVerfied: true)
        let user3 = UserModel(name: "Noah", point: 70, isVerfied: false)
        let user4 = UserModel(name: nil, point: 80, isVerfied: true)
        let user5 = UserModel(name: "Noel", point: 56, isVerfied: true)
        let user6 = UserModel(name: "Joseph", point: 30, isVerfied: false)
        let user7 = UserModel(name: "Daniel", point: 0, isVerfied: true)
        let user8 = UserModel(name: "Dune", point: 20, isVerfied: false)
        let user9 = UserModel(name: "Mark", point: 10, isVerfied: true)
        let user10 = UserModel(name: "Tony", point: 1, isVerfied: true)
        dataArray.append(contentsOf: [user1, user2, user3, user4, user5, user6, user7, user8, user9, user10])
    }
    
    enum FilterType: String {
        case Sort = "sort"
        case Filter = "filter"
        case Map = "map"
        
        
    }
    
    func updateFilterArray(_ filterType: FilterType) {
        // sort
        switch filterType {
        case .Sort:
            let sortedArray = dataArray.sorted(by: {$0.point >= $1.point})
            filteredArray = sortedArray
        case .Filter:
            let filteredArray = dataArray.filter{$0.point > 50}
            self.filteredArray = filteredArray
        case .Map:
            let mappedArray = dataArray.compactMap({$0.name})
            // compactMap -> String? excludes all of nil things out
            self.mappedArray = mappedArray
        }
    }
    
    func updateComplexFilterArray() {
        let filteredArray = dataArray.filter{$0.isVerfied}.sorted(by: {$0.point > $1.point}).compactMap({$0.name})
        self.mappedArray = filteredArray
    }
}

struct ArraysBootCamp: View {
    @StateObject private var viewModel = ArrayModificationViewModel()
    @State private var dataArray: [UserModel] = []
    @State private var mappedArray: [String] = []
    @State private var isMapped: Bool = false
    var body: some View {
        
        VStack {
            HStack {
                Button("Total") {
                    isMapped = false
                    dataArray = viewModel.dataArray
                }
                Button("Sorted") {
                    isMapped = false
                    viewModel.updateFilterArray(.Sort)
                    dataArray = viewModel.filteredArray
                }
                Button("Filtered") {
                    isMapped = false
                    viewModel.updateFilterArray(.Filter)
                    dataArray = viewModel.filteredArray
                }
                Button("Mapped") {
                    isMapped = true
                    viewModel.updateFilterArray(.Map)
                    mappedArray = viewModel.mappedArray
                }
                Button("Complex Mapped") {
                    isMapped = true
                    viewModel.updateComplexFilterArray()
                    mappedArray = viewModel.mappedArray
                }
            }
            ScrollView {
                VStack {
                    if isMapped {
                        ForEach(mappedArray, id:\.self) { name in
                            Text(name)
                                .font(.headline)
                        }
                    } else {
                        ForEach(dataArray) { user in
                            VStack {
                                Text(user.name ?? "")
                                    .font(.headline)
                                HStack {
                                    Text("Points : \(user.point)")
                                    Spacer()
                                    if user.isVerfied {
                                        Image(systemName: "flame.fill")
                                    }
                                }
                            }
                            .foregroundColor(.white)
                            .padding()
                            .padding(.horizontal)
                            .background(Color.blue.cornerRadius(10))
                        }
                    }
                }
            }
        }
    }
}
  • 이름만 보여주는 mappedArray와 전체 구조체가 들어오는 dataArray를 따로 선언해서 보여주었는데, 이것보다는 UI를 그릴 때 플래그 비트를 두고(즉 이름 매핑 배열만 보여줄지 결정하는 비트) 조건화하는 게 낫지 않나 싶다. 해당 뷰 안에 별도의 데이터를 선언해야 하니 낭비인 것 같다.

구현 화면

profile
JUST DO IT
post-custom-banner

0개의 댓글