Sudhin Davis
Sudhin Davis

Reputation: 2020

NSPredicate to check whether a string of numbers separated by comma contains a number

I have a string say, "0,1,2,3,4,5,6,7,8,9,10,11" (Assumed to be months index string) in my Core Data. I want to use a predicate to fetch whether that field contains a number say, string contains '0'.

We cannot use 'CONTAINS' since '0' is also present in '10'. I need to fetch the object using NSPredicate to avoid loops from Core Data.

Update: I just want to test whether '0,1,2,3,4,5,6,7,8,9,10,11' contains '0' or not using NSPredicate.

Solved

The "MATCHES" operator of NSPredicate can be used to compare against a regular expression:

NSString *searchTerm = @"0";
NSString *regex = [NSString stringWithFormat:@"(.*,)?%@(,.*)?", searchTerm];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"strIndex MATCHES %@", regex];

This worked fine for me. Thank you @skagedal for the suggestion. I got this answer from Form NSPredicate from string that contains id's.

Upvotes: 1

Views: 1261

Answers (3)

bunty
bunty

Reputation: 629

You can check numbers separated by comma by using regular expression as below:

NSString *str = @"0,1,2,3,4,5,6,7,8,9,10,11";
NSString *regex = @"(\\d+)(,\\s*\\d+)*";
NSPredicate *myTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];

if ([myTest evaluateWithObject: str])
    NSLog(@"matched");
else
    NSLog(@"not matched");

So use this predicate while fetching data from core data with field name as below:

NSError* error = nil;
NSString *regex = @"(\\d+)(,\\s*\\d+)*";
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TableName" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"MonthFieldName MATCHES %@", regex];
[fetchRequest setPredicate:predicate];
NSArray* arrRecords = [context executeFetchRequest:fetchRequest error:&error];

Upvotes: 0

skagedal
skagedal

Reputation: 2411

As bunty writes, you can use regular expressions. If I understand the question, you want a predicate that looks for a specific string of digits – but only if not directly preceded or followed by other digits. This can be achieved with what in regular expressions is called look-behind and look-ahead assertions. More specifically, negative look-behind/look-ahead assertions.

Take a look at the syntax for regular expressions used by NSPredicate. To search for the number 1, without also finding 11 and 10, use the regular expression:

(?<!\d)1(?!\d)

So you can build your Core Data predicate with something like:

NSString *month = @"11"; // the number you're looking for
NSString *regex = [NSString stringWithFormat:@"(?<!\d)%@(?!\d)", month];
NSPredicate *myTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];

This will not be a very effective search, but if that's what your Core Data model looks like and you can't easily change it, it just might work fine. If you can, I'd go with bbum's suggestion and reorganize your model.

Upvotes: 0

bbum
bbum

Reputation: 162712

@bunty's answer is directly correct, but there is a meta answer.

You should probably have a Months entity that contains the 12 months of the year in the data store. Then, have a one-to-many (ordered or not depending on needs) relationship from the thing that contains that string to the Months entity.

Then, you simply follow the relationship (i.e. myThing.months) to fetch the months that the entity needs.

For large databases, fetches using string matching predicates are going to be quite slow and it really is a bit of anti-pattern.

Upvotes: 1

Related Questions