Home > database >  Using a computed property to filter an array of objects based on a nested array property
Using a computed property to filter an array of objects based on a nested array property

Time:11-20

I'm creating a search field to allow users to search for tags associated with a photo, and then only show photos containing that tag in my List. I'm using a computed property to check if my array of Photo contains the tag but the tags are in a nested array several properties deep in my Photo object. I need some help filtering the photos array from the computed property so my List uses the correct photos.

I'm trying to use this computed property to filter my photos:

struct PhotoListView: View {
    let photos: [Photo]
    
    @State private var searchText: String = ""
    
    var filteredPhotos: [Photo] {
        if searchText.count == 0 {
            return photos
        } else {
            return photos.filter { photo in
                return photo.info?.tags.tagContent.filter { $0._content.contains(searchText) }
            }
        }
    }
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(filteredPhotos) { photo in
                    NavigationLink {
                        PhotoDetailView(photo: photo)
                    } label: {
                        PhotoRow(photo: photo)
                    }
                }
            }
            .navigationTitle("Recent Photos")
            .searchable(text: $searchText)
        }
    }
}

The attempt above results in an error - Cannot convert value of type '[TagContent]?' to closure result type 'Bool'

class Photo: Decodable, Identifiable {
    let id: String
    let owner: String
    let secret: String
    let title: String
    let server: String
    let farm: Int
    var imageURL: URL?
    var info: PhotoInfo?
}

struct PhotoInfo: Decodable {
    let id: String
    let dateuploaded: String
    let tags: PhotoTags
}

struct PhotoTags: Decodable {
    let tagContent: [TagContent]

    enum CodingKeys: String, CodingKey {
        case tagContent = "tag"
    }
}
struct TagContent: Decodable, Hashable {
    let id: String
    let _content: String
}

Using the above model structure can anyone help me filter the tags by _content from my computed property?

CodePudding user response:

The filter function needs a boolean to determine if the value should be included. But for now you filter function returns only the inner filtered collection. Check if this collection contains any values.

return photos.filter { photo in
    !(photo.info?.tags.tagContent.filter { $0._content.contains(searchText) }.isEmpty ?? true)
}

CodePudding user response:

Currently your return [TagContent] rather than Bool

Replace filteredPhotos with:

var filteredPhotos: [Photo] {
    if searchText.count == 0 {
        return photos
    } else {
        return photos.filter { photo in
            let tagContents = photo.info?.tags.tagContent.filter{ $0._content.contains(searchText) } ?? []
            
            return !tagContents.isEmpty
        }
    }
}
  • Related