Bartłomiej Semańczyk
Bartłomiej Semańczyk

Reputation: 61774

How to create NSPredicate with array contains element

I simply have Address entity with property favourites of type [String]

Address

@objc(Address)
class Address: NSManagedObject, Observer {
    @NSManaged var favourites: [String]
}

Suppose I have some addresses:

1: favourites: ["a", "b", "c"]
2: favourites: ["b", "c"]
3: favourites: ["a", "c"]
4: favourites: ["a"]
5: favourites: ["b"]

Now I need to fetch all addresses with a inside favourites. SO the result should be: 1, 3, 4

In other words I need to check if ANY element in array is equal to specific string then object should be returned.

The following doesn't work:

NSPredicate(format: "favourites CONTAINS %@", "a")

This is how it is defined in xcdatamodel:

enter image description here

Upvotes: 4

Views: 4094

Answers (3)

vadian
vadian

Reputation: 285059

As already mentioned you cannot search in a transformable attribute.

An alternative to an extra entity is to save the string array joined with an unique separator as one string and declare a computed property to convert the string to array and vice versa

@NSManaged var favourites: String

private let uniqueSeparator = "®¥ð"

var favs : [String] {
    get { return favourites.components(separatedBy: uniqueSeparator) }
    set { favourites = newValue.joined(separator: uniqueSeparator)}
}

Then you can simply write

favs.contains("a")

Upvotes: 0

Martin R
Martin R

Reputation: 539685

You cannot search in an array defined as transformable attribute with a predicate. Better define another entity

@objc(Favourite)
class Favourite: NSManagedObject {
    @NSManaged var name: String
}

and a one-to-many relationship from "Address" to "Favourite".

The you can fetch all addresses which have any (i.e.: at least one) favorite equal to the string "a" with the predicate

NSPredicate(format: "ANY favourites.name == %@", "a")

or better with a compiler-checked key path literal:

NSPredicate(format: "ANY %K == %@", #keyPath(Address.favourites.name), "a")

For a case-insensitive comparison you can use

NSPredicate(format: "ANY %K ==[c] %@", #keyPath(Address.favourites.name), "a")

Upvotes: 10

Sweeper
Sweeper

Reputation: 270790

According to the cheatsheet, CONTAINS is for comparing strings. e.g. "Hello" CONTAINS "e".

For collections, you should use IN:

NSPredicate(format: "%@ IN favourites", "a")

Upvotes: 4

Related Questions