Using #min,#max and etc inside a predicate? - iphone

Is it possible to use aggregate operator such as #min inside a predicate?
BTW The predicate filters an array of objects.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF.score == #min.score"];
I know you can retrieve the min value using the key-value operators eg:
NSNumber *minScore = [myArray valueForKeyPath:#"#min.score"];
So far I only get errors about the objects not being key value compliant for "#min".
Thanks

The reason that you're getting that error is that the predicate is being applied to each object in myArray, and those objects apparently don't support the key path #min.score. NSPredicate does have support for at least some of the collection operators, though. Here's a simple example that works:
NSDictionary *d1 = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:35] forKey:#"score"];
NSDictionary *d2 = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:52] forKey:#"score"];
NSDictionary *d3 = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:13] forKey:#"score"];
NSDictionary *d4 = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:19] forKey:#"score"];
NSArray *array = [NSArray arrayWithObjects:d1, d2, d3, d4, nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.score == %#.#min.score", array];
NSLog(#"Min dictionaries: %#", [array filteredArrayUsingPredicate:predicate]);
You can see that in this case, the #min.score key path is applied to the array, which makes sense. The output is an array containing the one dictionary that contains the minimum value:
Min dictionaries: (
{
score = 13;
}
)

Related

Sort array of dictionary objects

the problem im having is that i cant sort an NSMutableArray of NSMutableDictionary Objects, I want to sort the objects by rating, the rating is a NSNumber, what am i missing?
My current code that sums all the ratings from "arrayMealRating" and sorts the resulting array:
[arrayMealRating addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
[eachObject objectForKey:#"Airline"], #"Airline"
,[eachObject objectForKey:#"setMealRating"], #"setMealRating"
, nil]];
}
NSArray *airlineNames = [arrayMealRating valueForKeyPath:#"#distinctUnionOfObjects.Airline"];
// Loop through all the airlines
for (NSString *airline in airlineNames) {
// Get an array of all the dictionaries for the current airline
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(Airline == %#)", airline];
NSArray *airlineMealRating = [arrayMealRating filteredArrayUsingPredicate:predicate];
// Get the sum of all the ratings using KVC #sum collection operator
NSNumber *rating = [airlineMealRating valueForKeyPath:#"#sum.setMealRating"];
//NSLog(#"%#: %#", airline, rating);
[sortedMealArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
airline, #"Airline"
,[rating stringValue], #"setMealRating"
, nil]];
}
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"setMealRating" ascending:YES];
[sortedMealArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObjects:descriptor,nil]];
auxMealRating = [sortedMealArray copy];
Any doubt, please dont down vote, just ask and i will edit the question.
Best Regards and sorry for my poor english.
This should do what you want:
NSArray *sortedArray = [arrayMealRating sortedArrayUsingComparator:^(id obj1, id obj2) {
NSNumber *rating1 = [(NSDictionary *)obj1 objectForKey:#"setMealRating"];
NSNumber *rating2 = [(NSDictionary *)obj2 objectForKey:#"setMealRating"];
return [rating1 compare:rating2];
}];

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
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
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];
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.
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];

Search NSArray of NSDictionaries

I have an array with dictionaries,
and need to search trough the array
and modify a specific dictionary in the array found by an object name inside the dictionary.
So , create the mutable array, dictionary , and add many dictionaries to the array
...{ self.bloquesArray = [[[NSMutableArray alloc] init]autorelease];
[self createBloqueDicto];
[self.unBloqueDicto setObject:#"easySprite" forKey:#"Name"];
[self.unBloqueDicto setObject:#"290" forKey:#"X"];
[self.unBloqueDicto setObject:#"300" forKey:#"Y"];
[self.bloquesArray addObject:self.unBloqueDicto];
}
- (void)createBloqueDicto {
self.unBloqueDicto = [[[NSMutableDictionary alloc] init] autorelease];
}
so now I need to change the value for the key X and Y in the dictionary with
key: Name = easySprite
so need to find that specific dictionary [other dictionaries have different values for Name]
how can I do this please?
thanks!
Do the following to get the matched dictionaries,
NSPredicate *p = [NSPredicate predicateWithFormat:#"Name = %#", #"easySprite"];
NSArray *matchedDicts = [bloquesArray filteredArrayUsingPredicate:p];
Now the matchedDicts contains the dictionaries with the value #"easySprite" for the key #"Name". Do the rest(changing the X and Y) from there.

Search NSArray for value matching value

I have an NSArray of objects, which has a particular property called name (type NSString).
I have a second NSArray of NSStrings which are names.
I'd like to get an NSArray of all the objects whose .name property matches one of the names in the second NSArray.
How do I go about this, fast and efficiently as this will be required quite often.
Why not just to use predicates to do that for you?:
// For number kind of values:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF = %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
// For string kind of values:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
// For any object kind of value (yes, you can search objects also):
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", value];
NSArray *results = [array_to_search filteredArrayUsingPredicate:predicate];
Here's a simple way:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", nameToFind];
[listOfItems filteredArrayUsingPredicate:predicate];
With your current data structures, you can only do it in O(n^2) time by looping over the first array once for each member of the second array:
NSMutableArray * array = [NSMutableArray array];
for (NSString * name in names) {
for (MyObject * object in objects) {
if ([[myObject name] isEqualToString:name]) {
[array addObject:object];
}
}
}
(Alternate as suggested by Stefan: loop over the objects array and ask the names array if it containsObject: for the name of each object.)
But if this really needs to be faster (really depends on the size of the arrays as much as how often you do it), you can improve this by introducing an NSDictionary that maps the names in the first array to their objects. Then each of those lookups is O(1) and the overall time is O(n). (You'd have to keep this dictionary always in sync with the array of objects, which isn't hard with reasonable accessors. This technique also has the constraint that the same name can't appear on more than one object.)
An alternate way of getting this result (and which doesn't have that last constraint) is to use an NSSet for your second collection, then walk through the objects array calling containsObject: with each one on the set of names. Whether this technique is better depends on whether your two collections are roughly the same size, or if one is much larger than the other.
I like to use this method:
NSIndexSet *indexes = [_items indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return ((MyObject *)obj).name isEqualToString:name];
}];
if (indexes.count != 0) {
//extract your objects from the indexSet, and do what you like...
}
NSMutableArray * foundNames = [NSMutableArray array];
for (MyObject * objectWithName in objectCollection) {
if ([names containsObject:objectWithName.name]) {
[foundNames objectWithName];
}
}
The methods most helpful will be:
filteredArrayUsingPredicate:
and
indexesOfObjectsPassingTest:
The second one uses a code block, not available on iOS before 4.0
Both of these will be more efficient than iterating directly.
There's a good example here:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxUsing.html
NSMutableArray* solutions = [NSMutableArray array];
for (Object* object in objects){
for (NSString* name in names){
if ([object.name isEqualToString:name]){
[solutions addObject:object];
break; // If this doesnt work remove this
}
}
}
int count=0;
if (range.location!=NSNotFound)
{
[searchindex addObject:[NSString stringWithFormat:#"%d",count]];
}

Grab all values in NSDictionary inside an NSArray

I have an NSArray full of NSDictionary objects, and each NSDictionary has a unique ID inside. I want to do lookups of particular dictionaries based on the ID, and get all the information for that dictionary in my own dictionary.
myArray contains:
[index 0] myDictionary object
name = apple,
weight = 1 pound,
number = 294,
[index 1] myDictionary object
name = pear,
weight = .5 pound,
number = 149,
[index 3] myDictionary object (etc...)
I want to get the name and weight for the second dictionary object (I won't know the index of the object... if there were only two dicts, I could just make a dictionary from [myArray objectAtIndex:1])
So, say I know the number 149. How would I be able to get the second myDictionary object out of myArray into a new NSDictionary?
As an alternative to Jacob's answer, you could also just ask the dictionary to find the object:
NSPredicate *finder = [NSPredicate predicateWithFormat:#"number = 149"];
NSDictionary *targetDictionary = [[array filteredArrayUsingPredicate:finder] lastObject];
You'd need to iterate through every NSDictionary object in your NSArray:
- (NSDictionary *) findDictByNumber:(NSInteger) num {
for(NSDictionary *dict in myArray) {
if([[dict objectForKey:#"number"] intValue] == num)
return [NSDictionary dictionaryWithObjectsAndKeys:[dict objectForKey:#"weight"], #"weight", [dict objectForKey:#"name"], #"name", nil];
}
return nil;
}