KumarM
KumarM

Reputation: 1699

Comparing an Array with another using NSPredicate

I've the following structure:

TxnSummary * t1 = [[TxnSummary alloc] init];
t1.txnId = @"1";
t1.shortDesc = @"First one";
t1.filters = [[NSArray alloc] initWithObjects:@"F1", @"F2", nil];

TxnSummary * t2 = [[TxnSummary alloc] init];
t2.txnId = @"2";
t2.shortDesc = @"Second one";
t2.filters = [[NSArray alloc] initWithObjects:@"F1",@"F2", @"F3", nil];

TxnSummary * t3 = [[TxnSummary alloc] init];
t3.txnId = @"3";
t3.shortDesc = @"Third one";
t3.filters = [[NSArray alloc] initWithObjects:@"F1", @"F3", nil];

TxnSummary * t4 = [[TxnSummary alloc] init];
t4.txnId = @"4";
t4.shortDesc = @"Fourth one";
t4.filters = [[NSArray alloc] initWithObjects:@"F4", nil];

NSArray * xnArray = [[NSArray alloc] initWithObjects:t1,t2,t3,t4, nil];

Now if I want to find out which of the txn summaries have filters F1, then I could do this:

NSPredicate * predicate = [NSPredicate predicateWithFormat:@"filters CONTAINS[cd] %@", @"F1"];
NSArray * filteredArray = [xnArray filteredArrayUsingPredicate:predicate];

This works well if I'm comparing for only one string, but if want to find out which all txn summaries have filters "F1", or "F2", then if I have to follow the above mechanism I'll have to create two predicates - each for F1 and F2 and then run it against the xnArray (which seems to be inefficient). I want to be able to create a list of filters strings and use that to fetch the matching txs from the xn array.

NSArray * filterStrings = [[NSArray alloc] initWithObjects:@"F1",@"F2", nil];

Does NSPredicate have functionality to achieve this or should I resort to some other method of filtering?

Appreciate your help.

Thanks, Kumar

Upvotes: 2

Views: 3954

Answers (4)

ggrana
ggrana

Reputation: 2335

You can do something like:

NSPredicate * predicate = [NSPredicate predicateWithFormat:@"filters CONTAINS[cd] %@ || filters CONTAINS[cd] %@", @"F1", @"F4"];

If you want to add all the keys that are in a array you can do something like that:

NSArray * filterStrings = [[NSArray alloc] initWithObjects:@"F1",@"F4", nil];

NSString* predicateString = [filterStrings componentsJoinedByString:@"'|| filters CONTAINS[cd] '"];
predicateString = [NSString stringWithFormat:@"filters CONTAINS[cd] '%@'",predicateString];


NSPredicate * predicate = [NSPredicate predicateWithFormat:predicateString];
NSArray * filteredArray = [xnArray filteredArrayUsingPredicate:predicate];

Upvotes: 4

Magic Bullet Dave
Magic Bullet Dave

Reputation: 9076

If I understand you correctly, you can achieve this by creating a compound predicate from an array of predicates, for example:

 NSPredicate *newFilterPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:selectedItemIDs];

EDIT: added more detailed explanation:

Compound predicates combine predicates into one predicate. For example, if you want to filter for items that contain "F1" or "F2" you do this:

// Normally build this in some kind of loop
NSPredicate *firstPredicate = [NSPredicate predicateWithFormat:@"filter =%@", @"F1"];
NSPredicate *secondPredicate = [NSPredicate predicateWithFormat:@"filter =%@", @"F1"];

// Create the array of predicates
NSArray *arrayOfPredicates = [NSArray arrayWithObjects:firstPredicate, secondPredicate, nil];

// Create the compound predicate
NSPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:arrayOfPredicates];

There are also methods for "and" instead of "or" as well as other boolean conditions. Full reference can be found here: NSCompoundPredicate Class Reference

Hope this helps,

Dave

Upvotes: 4

Greg
Greg

Reputation: 8340

I wouldn't use NSArray to store the filters. This is a perfect book-like example for using NSSet/NSMutableSet instead. You can initialize similarly to the array:

t1.filters = [[NSSet alloc] initWithObjects:@"F1", @"F2", nil];

Then you check if that particular string exists simply by calling:

BOOL contains = [t1.filter containsObject:@"F1"];

You can now also filter the set with methods like filteredSetUsingPredicate, objectsPassingTest (to use with blocks) or even create intersections or unions with other sets (isSubsetOfSet, intersectsSet, etc). So for example you could create a new set with the searched elements and check if the set contains them:

NSSet* toFind = [[NSSet alloc] initWithObjects:@"F1", @"F3", nil];
[toFind isSubsetOfSet:t1.filters];

Searching a set is much quicker than an array because set is backed up by a Hash table, whereas an array has to be searched linearly.

Upvotes: 1

gregheo
gregheo

Reputation: 4270

If exact matching is OK, you could use the IN predicate like so:

NSArray *filterStrings = [NSArray arrayWithObjects:@"F1", @"F2", nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"filters IN %@", filterStrings];

NSArray *filteredArray = [xnArray filteredArrayUsingPredicate:predicate];

Upvotes: 0

Related Questions