NSPredicate on NSDictionary - iphone

I'm trying to make sections in a table view based on the alphabet and sort my entries alphabetically under these sections.
I have collected the first letter in every entry of bandsArray in bandsArrayIndex and I'm now trying to use NSPredicate to count how many of each letter there are.
I'm doing this by following this guide.
They are using an NSArray and I'm using an NSDictionary and can't seem to get it to work. Can anyone help me out?
The application crashes when trying to show the view with the table view in it.
In Debugger Console the following error is shown:
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'Can't do a substring operation with
something that isn't a string
This is my code
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *alphabet = [bandsArrayIndex objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *letters = [bandsArray filteredArrayUsingPredicate:predicate];
return [letters count];
}
EDIT: This is my bandsArray.
The header
NSMutableArray *bandsArray;
#property (nonatomic, retain) NSMutableArray* bandsArray;
The implementation
// set array from plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"Bands" ofType:#"plist"];
NSMutableArray* tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
self.bandsArray = tmpArray;
// sort array after name ascending
NSSortDescriptor *nameSorter = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
[bandsArray sortUsingDescriptors:[NSArray arrayWithObject:nameSorter]];

Do you mean that bandsArray is an array of dictionaries? If so, and assuming each dictionary has a name key, you should be able to change the predicate to something like #"SELF.name beginswith[c] %#".
If, on the other hand, bandsArray is actually a dictionary itself, maybe you want to do [[bandsArray allKeys] filteredArrayUsingPredicate...].

#import <Foundation/Foundation.h>
// clang -framework Foundation Siegfried.m
int
main() {
NSArray *arr = #[
#{#"1" : #"Fafner"},
#{#"1" : #"Fasolt"}
];
NSPredicate *p = [NSPredicate predicateWithFormat: #"SELF['1'] CONTAINS 'e'"];
NSArray *res = [arr filteredArrayUsingPredicate:p];
NSLog(#"Siegfried %#", res);
return 0;
}

Sorry, I didnt notice you had a NSArray of NSDictionaries.
Read this thread: Using NSPredicate to filter an NSArray based on NSDictionary keys
When you use NSDictionary, you should check by its Key... I'm not sure you're using the right approach!
I would do something like:
#implementation Word : NSObect {
NSString *title;
}
then I would create a NSArray of Words and filter on them with:
#"title beginswith[c] %#"

Related

Extracting Unique Objects from a Data Array

I want to add names in a data array only if the name does not previously exist in the data array. When I attempt to print these names, I do get repetitions. Is there a way to solve this?
-(NSMutableArray *)autoComplete
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
NSString *url = [NSString stringWithFormat:#"%#45.25,-95.25&limit=100&client_id=Von0J4Bu6INiez5bGby2R&client_secret=50sUjSbg7dba8cQgtpdfr5Ns7wyYTqtmKpUU3khQ",kWSURL];
NSDictionary * returnDict = (NSDictionary *) [self callWebService:url];
if([returnDict objectForKey:#"success"])
{
NSArray *responceArray = [returnDict objectForKey:#"response"];
for (NSDictionary *dict in responceArray) {
placeDC *place = [[placeDC alloc]init];
NSDictionary *placeDict = (NSDictionary *)[dict objectForKey:#"place" ];
NSDictionary *loctionDict =(NSDictionary *)[dict objectForKey:#"loc"];
NSString * name =[placeDict objectForKey:#"name"];
NSString * stateFull =[placeDict objectForKey:#"stateFull"];
NSString * countryFull =[placeDict objectForKey:#"countryFull"];
NSString *latitude =[loctionDict objectForKey:#"lat"];
NSString *longitude = [loctionDict objectForKey:#"long"];
place.placeNmae=name;
place.countryFullName=countryFull;
place.stateFullName=stateFull;
NSLog(#"%# ",stateFull);
place.latitude=[latitude doubleValue];
place.longitude=[longitude doubleValue];
[dataArray addObject:place];
}
}
return dataArray;
}
First Check that is there any response from the Server side or not, to check response use NSLog() or Break Points.
if response is ok then put a the following check your code
if (![dataArray containsObject:#"Some Name"])
{
// add Object
}
You could add the name NSString to an NSSet and check in every cycle whether it contains it or not.
Inside your if you could write something like:
NSArray *responceArray = [returnDict objectForKey:#"response"];
NSSet *names = [[NSSet alloc] init];
for (NSDictionary *dict in responceArray) {
NSDictionary *placeDict = (NSDictionary *)[dict objectForKey:#"place" ];
NSString * name =[placeDict objectForKey:#"name"];
if (![names containsObject:name]) {
[names addObject:name];
placeDC *place = [[placeDC alloc]init];
NSDictionary *loctionDict =(NSDictionary *)[dict objectForKey:#"loc"];
NSString * stateFull =[placeDict objectForKey:#"stateFull"];
NSString * countryFull =[placeDict objectForKey:#"countryFull"];
NSString *latitude =[loctionDict objectForKey:#"lat"];
NSString *longitude = [loctionDict objectForKey:#"long"];
place.placeNmae=name;
place.countryFullName=countryFull;
place.stateFullName=stateFull;
NSLog(#"%# ",stateFull);
place.latitude=[latitude doubleValue];
place.longitude=[longitude doubleValue];
[dataArray addObject:place];
}
}
Hope this helps!
Do one thing, add your dict in another array and search in this array that data already exist or not,
[tempAry addObject: dict];
and before insertion
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", name];
NSArray *filteredArray = [tempAry filteredArrayUsingPredicate:predicate];
if ([filteredArray count] == 0)
{
[dataArray addObject:place];
}
else{
//Already exist
}
Why don't you create a separate dictionary, as an ivar or property of you class, for storing our required value, say it as :
NSMutableDictionary *uniqueValueDict=[NSMutableDictionary new];
And keep storing your required value and key as:
[uniqueValueDict setObject:stateFull forKey:uniqueValueDict];
Your work will be done.
This is the easiest solution that i have applied and this should get you going in picking up unique elements out of array.
NSArray * claimedOfferArray = [[NSArray alloc]initWithObjects:#"A",#"B",#"A",#"C",#"B" nil];
NSArray * distinctArray = [[NSArray alloc]init];
distinctArray =[[NSSet setWithArray:claimedOfferArray] allObjects];
This code will also work with NSMutableArray
Let me know if it works for you..:).

What's the easiest way to remove empty NSStrings from an NSArray?

In PHP it's one line of code:
$array_without_empty_strs = array_filter($array_with_empty_strs);
What's the objective C equivalent?
UPDATE - Added the following test code to illustrate the use of Nikolai Ruhe's solution:
// SOLUTION Test Code
NSMutableArray *myArray = [[NSMutableArray alloc] init ];
[myArray addObject:[NSNumber numberWithInt:5]];
[myArray addObject:#""];
[myArray addObject:#"test"];
NSLog(#"%#", myArray);
[myArray removeObject:#""];
NSLog(#"%#", myArray);
// SOLUTION Test Code Output
2012-07-12 08:18:16.271 Calculator[1527:f803] (
5,
"",
test
)
2012-07-12 08:18:16.273 Calculator[1527:f803] (
5,
test
)
It's even more simple:
[mutableArrayOfStrings removeObject:#""];
If your array is not mutable you have to create a mutableCopy before.
removeObject: removes all objects from an array that return YES from isEqual:.
NSArray *noEmptyStrings = [maybeEmptyStrings filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"length > 0"]];
we can use NSPredicate
here my code
NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(id str, NSDictionary *unused) {
return ![str isEqualToString:#""];
}];
NSArray *filtered = [yourArray filteredArrayUsingPredicate:pred];
Look at this question, which is almost exactly the same as yours.
I use this method for non-string (object) cases that don't work with NSPredicate
Where array is NSMutableArray
- (void) clearArrayOut
{
for (int j=[array count]-1; j>=0; j--)
if ([[array objectAtIndex:j] length] == 0)
[array removeObjectAtIndex:j];
}
And I totally just stole this from rob mayoff's beautiful answer on my question NSPredicate instead of loop to filter an array of objects
Here's an alternate method that is more flexible even than using a predicate.
NSArray* filteredArray = [arrayToFilter objectsWithIndexes:
[arrayToFilter indexesOfObjectsPassingTest:
^((id obj, NSUInteger idx, BOOL *stop)
{
return [obj length] > 0;
}]];

Use existing NSArray object properties to create a new NSArray for sectioned tableView

So I have the kind of classic situation where I want to group my tableView by Month/Year. I have a member of my conference object called beginDateSearchString that I use to put different conference into buckets; my problem is in the next part where I try and fail to use a NSSortDescriptor to sort each bucket by beginDate (which is a date).
I am getting an error related to unsorted not being able to receive sort descriptor type selectors.
Here is the disgusting code:
- (NSArray *)arrayOfDateSortedEvents {
NSMutableArray *sortedArray = [[NSMutableArray alloc] init];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//place into buckets
for (WSConference *conference in self.arrayOfEvents) {
if (![dictionary objectForKey:[conference beginDateSearchString]]) {
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:conference,nil];
[dictionary setObject:array forKey:[conference beginDateSearchString]];
}
else {
[[dictionary objectForKey:[conference beginDateSearchString]] addObject:conference];
}
}
//sort each bucket by descriptor beginDate
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"beginDate" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:descriptor];
for (NSMutableArray *unsorted in dictionary) {
[unsorted sortUsingDescriptors:sortDescriptors];
}
// now, unkey and add dictionary in order
while ([dictionary count] > 0) {
NSString *lowest = nil;
for (NSMutableArray *array in dictionary) {
if (!lowest)
lowest = [[dictionary allKeysForObject:array] objectAtIndex:0];
else {
if ([(WSConference *)[array objectAtIndex:0] beginDate] < [[dictionary objectForKey:lowest] beginDate])
lowest = [[dictionary allKeysForObject:array] objectAtIndex:0];
}
}
[sortedArray addObject:[dictionary objectForKey:lowest]];
[dictionary removeObjectForKey:lowest];
}
return sortedArray;
}
You want to probably filter the array in addition to sorting. See NSPredicate and the NSArray method -filteredArrayUsingPredicate: Then create an eventsByDateArray of the eventArrays created by the filter. Then in your table view delegate for creating the cells, if everything is ordered properly, the first section would represent the date of the events in the eventArray that is the first object of the eventsByDateArray and the table rows would consist of the events in the eventArray. And so on for each date.
Added
Your fast enumeration is incorrect. You enumerate through the keys of the dictionary. So in your code unsorted equals each of the keys as it enumerates. This is a GREAT lesson to everyone. It does not matter how you 'type' a variable. When Objective-C compiles it turns them all into id. So NSMutableArray *unsorted is not an NSMutableArray unless it is assigned to an NSMutableArray. If you assign unsorted to an NSString it will be an NSString. The fast enumerator for a dictionary works using the keys. So, in this case, unsorted becomes an NSString.
Instead of:
for (NSMutableArray *unsorted in dictionary) {
[unsorted sortUsingDescriptors:sortDescriptors];
}
you should have this:
for (id key in dictionary) {
NSMutableArray *unsorted = [dictionary objectForKey:key];
[unsorted sortUsingDescriptors:sortDescriptors];
}

NSCompoundPredicate fails to match

I'm building a NSPredicate using the code below for an iPhone app. The logging shows the prediate to be: location CONTAINS "head" AND shape CONTAINS "oval" AND texture CONTAINS "bumpy" AND colour CONTAINS "red"
I get no results. If I limit the predicate to a single item it will work, more than 1 fails.
Can anyone tell me why?
Many thanks
NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
for (Ditem in self.tableDataSource) {
NSString *Title = [Ditem valueForKey:#"Title"];
NSString *Value = [Ditem valueForKey:#"Value"];
if([[Value lowercaseString] isEqualToString: #"all"]){
Value = #"";
}
else{
NSPredicate *p = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:[Title lowercaseString]] rightExpression:[NSExpression expressionForConstantValue:[Value lowercaseString]] modifier:NSDirectPredicateModifier type:NSContainsPredicateOperatorType options:0];
[subPredicates addObject:p];
}
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
NSLog(#"predicate: %#", predicate);[self.fetchedResultsController.fetchRequest setPredicate:predicate];
Your predicate is requiring that all of the values in your filterable objects be strings. Is that correct?
Also, I would simplify your subpredicate creation to:
NSPredicate * p = [NSPredicate predicateWithFormat:#"%K CONTAINS %#", [Title lowercaseString], [Value lowercaseString]];

Sort an array with numeric strings

Hello I have an array of persons, and i am trying to sort them by age using a sort descriptor.
The age field in a patient is a string so when calling:
ageSorter = [[NSSortDescriptor alloc] initWithKey:#"age" ascending:YES];
[personList sortUsingDescriptors:[NSArray arrayWithObject:ageSorter]];
It sorts them but 100 appears first because its is not using numericSearch in the compare options.
Is there a ways i can still sort with descriptor but maybe using a selector to change how to compare the strings?
The finderSortWithLocale method (both these are taken from apple api):
int finderSortWithLocale(Person *person1, Person *person2, void *locale)
{
static NSStringCompareOptions comparisonOptions = NSNumericSearch;
NSRange string1Range = NSMakeRange(0, [string1 length]);
NSString *age1 = person1.age;
NSString *age2 = person2.age;
return [age1 compare:age2
options:comparisonOptions
range:string1Range
locale:(NSLocale *)locale];
}
How to call this method (edited: call the function on array of Persons):
NSArray *sortedArray = [personList sortedArrayUsingFunction:finderSortWithLocale
context:[NSLocale currentLocale]];
I also faced the same issue and found answer here.
Instead of NSString comparison, do with your object property. i.e for age.
Example. : In ascending order :
NSArray *sortedArray = [_arrayCaptureLeadList sortedArrayUsingComparator:^(Person *obj1, Person *obj2) {
return [obj1.age compare:obj2.age options:NSNumericSearch];
}];
NSMutableArray *filterResultArray = [NSMutableArray arrayWithArray:sortedArray];
In descending order :
NSArray *sortedArray = [_arrayCaptureLeadList sortedArrayUsingComparator:^(Person *obj1, Person *obj2) {
return [obj2.age compare:obj1.age options:NSNumericSearch];
}];
NSMutableArray *filterResultArray = [NSMutableArray arrayWithArray:sortedArray];
I know this is very late to reply your question but may this will be helpful for others. ^_^
You could create a category on NSString that adds a method numericCompare: and which calls [self compare:otherString options:NSNumericSearch]. Another option is to convert the age field into a NSNumber instead of a NSString. Yet another option involves a NSComparator block and sortUsingComparator.