I have an array of strings within a struct. This populates a list, and I use a .searchable to filter elements there. I can filter them by text, date, or tags within each item. All done without regard for the case.
Now, for the tags, I'm struggling to find the right way to filter them:
let dateFormatter = DateFormatter()
struct LibItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
}
final class DataModel: ObservableObject {
@AppStorage("myapp") public var collectables: [LibItem] = []
init() {
self.collectables = self.collectables.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
func sortList() {
self.collectables = self.collectables.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
}
struct ContentView: View {
@EnvironmentObject private var data: DataModel
@State var searchText: String = ""
var body: some View {
NavigationView {
List(filteredItems) { collectable in
VStack(alignment: .leading) {
Spacer()
Text(collectable.text).font(.body).padding(.leading).padding(.bottom, 1)
Spacer()
}
}
.listStyle(.plain)
.searchable(
text: $searchText,
placement: .navigationBarDrawer(displayMode: .always),
prompt: "Search..."
)
}
}
var filteredItems: [LibItem] {
switch selectedItem {
case 1: // date
return data.collectables.filter {
searchText.isEmpty ? true : $0.dateText.localizedCaseInsensitiveContains(searchText)
}
case 2: //tags <--- here how do I search and filter by, case insensitive?
return data.collectables.filter {
searchText.isEmpty ? true : $0.tags.contains(searchText)
}
default:
return data.collectables.filter {
searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText)
}
}
}
}
CodePudding user response:
Compare two lowercased strings, by momentarily turning both sides of the comparison lowercased.
An example you can use in your code:
$0.tags.compactMap { $0.lowercased() }.contains(searchText.lowercased())
If you need to check whether each single item of tags
contains part of the string, you need to iterate:
var isTagFound = false
$0.tags.compactMap { $0.lowercased() }.forEach {
if $0.contains(searchText.lowercased()) {
isTagFound = true
}
}
CodePudding user response:
For strict equality, case insensitive:
return data.collectables.filter {
searchText.isEmpty
? true
: $0.tags.contains { tag in tag.caseInsensitiveCompare(searchText) == .orderedSame }
}
for substrings, you can use the range(of:)
method:
return data.collectables.filter {
searchText.isEmpty
? true
: $0.tags.contains { tag in tag.range(of: searchText, options: .caseInsensitive) != nil }
}
or, use localizedCaseInsensitiveContains
which you have already used before:
return data.collectables.filter {
searchText.isEmpty
? true
: $0.tags.contains { tag in tag.localizedCaseInsensitiveContains(searchText) }
}