If you need to use a predicate to filter structs, you can do it without going into @objc
territory or converting your data into NSArray
:
func filter(actors: [Actor], named: String) -> [Actor] {
let predicate = NSPredicate(format: "SELF contains[cd] %@", named)
return actors.filter {
predicate.evaluate(with: $0.name)
}
}
Example:
struct Actor {
let name: String
let imdb: String
}
let actors = [
Actor(name: "John Travolta", imdb: "http://www.imdb.com/name/nm0000237"),
Actor(name: "Uma Thurman", imdb: "http://www.imdb.com/name/nm0000235"),
Actor(name: "Samuel L. Jackson", imdb: "http://www.imdb.com/name/nm0000 168"),
Actor(name: "Penélope Cruz", imdb: "http://www.imdb.com/name/nm0004851"),
Actor(name: "Penelope Ann Miller", imdb: "http://www.imdb.com/name/nm0000542")
]
print(filter(actors: actors, named: "Uma").map { $0.name })
// ["Uma Thurman"]
print(filter(actors: actors, named: "J").map { $0.name })
// ["John Travolta", "Samuel L. Jackson"]
Filtering using a predicate is useful, for example, to take into account diacritics. Let's search an actor name with an accented character:
print(filter(actors: actors, named: "Penelope").map { $0.name })
// ["Penélope Cruz", "Penelope Ann Miller"]
It finds both "Penelope", with and without accented "e".
Using String.contains
won't work in that case:
let text = "Penelope"
let hits = actors.filter { ( $0.name.lowercased().contains(text.lowercased()) )}
print(hits.map { $0.name })
// ["Penelope Ann Miller"]
There are better alternatives (String
localized search, regular expressions, String.range(of:options:range:locale)
, ...). Using predicates is not faster (not much slower either). It may be easier to reason about complex queries using predicate syntax if you already know it.