NSPredicate not filtering as desired - ios

Consider the following array:
NSArray *dataValues = #[#"Foo[0]", #"Foo[1].bar"];
And the following regex pattern, predicate and expected output:
NSString *pattern = #"Foo[0]";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF BEGINSWITH[cd] %#", pattern];
NSArray *results = [dataValues filteredArrayUsingPredicate: predicate];
NSLog(#"matches = %ld", (long)results.count);
This prints 1 in the console as expected. If we change the pattern to:
NSString *pattern = #"Foo\\[[0-9]\\]";
I would expect this to print 2 in the console, but it prints 0. I have double escaped the outer square brackets to allow them to be parsed and expect to find strings that have the numbers 0 to 9 inside the brackets to match this expression.
I have checked the regex against the following site, which does work correctly:
http://regexr.com/3bcut
I have no warnings/errors in Xcode (6.4, 6E35b) running against the iOS 8.4 iPhone 6 Plus simulator, but why does my regex not filter as expected?

You could try this depending on what your needs are:
NSArray *dataValues = #[#"Foo[0]", #"Foo[1].bar"];
NSString *pattern = #"Foo[*]*";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF LIKE %#", pattern];
NSArray *results = [dataValues filteredArrayUsingPredicate: predicate];
NSLog(#"matches = %ld", (long)results.count);
You could go a little more basic and use
NSMutableArray *results = [NSMutableArray array];
for (NSString *str in dataValues) {
if ([str rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet]].location != NSNotFound) {
if ([str hasPrefix:#"Foo["]) {
[results addObject:str];
}
}
}
NSLog(#"matches = %ld", (long)results.count);

After raising a TSI with Apple (well, who uses those things anyway?) they said I simply needed to use MATCHES instead of BEGINSWITH, which is only used for string matching - whereas I am trying to match on a regex.
My predicate should have therefore read:
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF MATCHES[cd] %#", pattern];

Related

NSPredicate for filtering array with ( ' ) character issue

Here is my test for filtering the array by a string. It work well if my string doesn't contain (') character
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"b'en", #"Adam", #"Melissa", #"arbind", nil];
//NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'b'"]; -> it work
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'b''"]; -> it crash
NSArray *beginWithB = [array filteredArrayUsingPredicate:sPredicate];
NSLog(#"beginwithB = %#",beginWithB);
I also try to change my string to 'b\'' or 'b''' but it still crash
Here is the crash log
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "SELF contains[c] 'b'''"'
How to resolve it? Any help would be great appreciated.
Please try to filter result as follows:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"b'en", #"Adam", #"Melissa", #"arbind", nil];
NSString *strToBeSearched = #"b'";
//NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'b'"]; -> it work
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",strToBeSearched]; //-> it also work
//OR
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'b\\''"];
NSArray *beginWithB = [array filteredArrayUsingPredicate:sPredicate];
NSLog(#"containB = %#",beginWithB);
try this
NSString *searchword = #"b";
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",searchword];
you get output of
You were pretty close when you tried backslash. This is the character that NSPredicate uses to escape special characters. However, you need two, not one, backslash:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"b'en", #"Adam", #"Melissa", #"arbind", nil];
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'b\\''"];
// ^^
NSArray *beginWithB = [array filteredArrayUsingPredicate:sPredicate];
NSLog(#"beginwithB = %#",beginWithB);
The reason you need two is Objective-C compiler. It processes all string literals in your code, and replaces escape sequences it encounters. If you would like NSPredicate to see a single backslash, your string literal needs to have two backslashes, because backslash itself is encoded as \\ in Objective-C string literals.
If your name is b'en then,
NSString *name = #"b'en";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == \"%#\"", name];
Hope this will help :)

Filtering NSMutableArray with NSPredicate and regex

I have a NSMutableArray containing NSString.
I need to filter only the objects that begins with (^WS|WV)[A-Z]{2}[0-9]{2}
How can I do it with NSPredicate?
Assuming that the array is called myArray, how can I write it?
Just google it to see a lot of answers like this:
NSArray *array = #[#"WSPS01", #"WLP05", #"1112"];
NSString *regex = #"(^WS|WV)[A-Z]{2}[0-9]{2}";
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", regex];
NSArray *filteredArray = [array filteredArrayUsingPredicate:pred];
// filteredArray contains only WSPS01
array - its your NSMutableArray
Solved.
this is the correct regex:
NSString *regex = #"(^WS|WV|WC)[A-Z]{2}[0-9]{2}.*"
The only question that I still have is why the MATCHES operator works and the BEGINSWITH not...

How to use a compound NSPredicate?

I would like to know how if at all to use a compound NSPredicate?
I have made an attempt as follows however the currentInstall array is exactly the same at the start as it is after the predicate has been applied.
NSArray *currentInstall = [coreDataController filterReadInstalls:selectedInstallID];
NSArray *tempArray = [currentInstalls filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"cHR == 0"]];
currentInstalls = [tempArray copy];
NSPredicate *predicateAreaString = [NSPredicate predicateWithFormat:#"area == %#", [myFilter objectForKey:#"area"]];
NSPredicate *predicateBString = [NSPredicate predicateWithFormat:#"stage == %#", [myFilter objectForKey:#"area2"]];
NSPredicate *predicateCString = [NSPredicate predicateWithFormat:#"partCode == %#", [myFilter objectForKey:#"area3"]];
NSPredicate *predicateDString = [NSPredicate predicateWithFormat:#"doorNo CONTAINS[cd] %#", [myFilter objectForKey:#"door"]];
NSPredicate *predicateEString = [NSPredicate predicateWithFormat:#"doorDesc CONTAINS[cd] %#", [myFilter objectForKey:#"doorDesc"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[predicateAreaString, predicateBString, predicateCString, predicateDString, predicateEString]];
NSMutableArray *filteredArray = [NSMutableArray arrayWithArray:[currentInstalls filteredArrayUsingPredicate:compoundPredicate]];
currentInstalls = [filteredArray mutableCopy];
There doesn't seem to be anything obviously wrong with the way you have implemented NSCompundPredicate. If you are not trying to And or Not predicates then I would say it is something wrong with your predicate formats and how they match the array you are filtering.
I would try to use just 2 of the predicates to create an NSCompundPredicate then get that working or see what is causing your issue. NSHipster also has some good info about NSPredicates.

NSPredicate predicateWithFormat for NSMetaDataQuery in iOS

I need to append my custom string into NSPredicate.
So I wrote following codes.
self.query = [[NSMetadataQuery alloc] init];
[self.query setSearchScopes: [NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K like '%#*'",NSMetadataItemFSNameKey,whereAreYou];
NSLog(#"%#",pred.description);
[self.query setPredicate:pred];
However when I test it , it only return following value.
kMDItemFSName LIKE "%#*"
the placeholder %# is not append correctly. It only showing %# sign.
How can I do that?
Format arguments inside quotation marks are not expanded by predicateWithFormat,
therefore #"%K like '%#*'" does not work.
This should work:
NSString *pattern = [NSString stringWithFormat:#"%#*", whereAreYou];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, pattern];

NSPredicate search LIKE

i'm trying to filter a string array with this predicate:
[NSPredicate predicateWithFormat:#"SELF LIKE[c] '#*!%d'", aNumber]
Every string which is like #WILDCARD!ANY_NUMBER is valid.
But it doesn't work :(
Can you help me?
EDIT:
NSString *pattern = [#"#*!" stringByAppendingFormat:#"%d", numberVariable];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", pattern];
NSArray *filteredArray = [anArray filteredArrayUsingPredicate:pred];
The array anArray contains Strings like #0!-1 (numberVariable is -1) but the array filterdArray is empty. So the regex doesn't work.
EDIT:
My Solution:
NSString *pattern = [#"#.*!\\" stringByAppendingFormat:#"%d", numberVariable];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", pattern];
NSArray *filteredArray = [anArray filteredArrayUsingPredicate:pred];
To find all strings that look like "#ANY_CHARACTERS!ANY_NUMBER" with an arbitrary number, you need the "MATCHES" operator with a regular expression:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", #"#.*!\\d+"];
NSArray *filtered = [yourArray filteredArrayUsingPredicate:pred];
If you have a specific number aNumber and want to find all strings of the form
"#ANY_CHARACTERS!<aNumber>", then the following should work:
NSString *pattern = [#"#*!" stringByAppendingFormat:#"%d", aNumber];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF LIKE %#", pattern];
NSArray *filtered = [yourArray filteredArrayUsingPredicate:pred];
The problem with your
[NSPredicate predicateWithFormat:#"SELF LIKE[c] '#*!%d'", aNumber]
is that %d inside quotation marks is not replaced by aNumber.

Resources