0

I am trying to follow two tutorials on how to implement a search controller for a UITableView. This is all working ok so far the issue I have is with the search/filter itself:

Tutorial links: http://www.appcoda.com/how-to-add-search-bar-uitableview/ http://code-ninja.org/blog/2012/01/08/ios-quick-tip-filtering-a-uitableview-with-a-search-bar/

One of the link suggests the Predicate method, which I can use as follows:

NSPredicate *resultPredicate = [NSPredicate
                                predicateWithFormat:@"SELF contains[cd] %@",
                                searchText];
self.searchResults = [self.fbFriends filteredArrayUsingPredicate:resultPredicate];

The issue with this, is that the above does not take into account that I do actually want to search the self.fbFriends array but I would like to search each dictionary within that array. The array is setup to have a dictionary for each fb friend, including @"id" and @"name". The table displays the names and in alphabetical order - this all works fine.

I would like to be able to search within the dictionaries within the self.fbFriends array and return an array (self.searchResults) that is the filter array of dictionaries.

The second tutorial ops for another route shown below:

for (NSDictionary *friend in self.fbFriends) {
    NSRange textRange = [[friend objectForKey:@"name"]rangeOfString:searchText options:NSCaseInsensitiveSearch];
    if (textRange.location != NSNotFound) {
        [self.searchResults addObject:friend];
    } else {
        [self.searchResults removeObjectIdenticalTo:friend];
    }
}

The problem with this route is that I am not checking that the object already exists in the filtered self.searchResults array therefore continue to add after each character is typed... I am sure this could be solved, but I do not think this is the cleanest method. If the predicate is best, how can I get that to work with the array/dictionary layout detailed above?

EDIT - from answer

self.searchResults = [self.fbFriends filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *object, NSDictionary *bindings) {
     //get the value from the dictionary (the name value)
     NSString *name = [object objectForKey:@"name"];
                                                                 //check if the name contains the search string (you can change 
                                                                 //the validation to check if the name starts with 
                                                                 //the search string or ends etc.
    if([name rangeOfString:searchText].location != NSNotFound) {
        return YES;
    }
    return NO;
}]];
StuartM
  • 6,743
  • 18
  • 84
  • 160
  • I can add this :: if (![self.searchResults containsObject:friend]) { [self.searchResults addObject:friend]; } :: To check if the object already exists in the array which makes it work as expected. If anyone else can comment on performance of the two, if predicate is better and how to get this working that would be accepted – StuartM May 20 '13 at 21:50

1 Answers1

3

You can use the block form of the predicate something like:

self.searchResult = [self.array filteredArrayUsingPredicate:[NSPredicate 
//since the array contains only dictionaries you can change the 
//type of the object which by default is `id` to NSDictionary
   predicateWithBlock:^BOOL(NSDictionart *object, NSDictionary *bindings) {
        //get the value from the dictionary (the name value)
        NSString *name = [object objectForKey:yourDictionaryNameKey];
        //check if the name contains the search string (you can change 
       //the validation to check if the name starts with 
       //the search string or ends etc.
      if([name rangeOfString:searchString].location != NSNotFound) { 
          return YES
      }
      return NO  
}]];

Also you might have to declare the searchString with the __block identifier.

danypata
  • 9,895
  • 1
  • 31
  • 44
  • I have added an edit to the question showing what I have added per your answer. Unfortunately I have two issues. The first line shows a warning incompatible pointer from NSMutableArray to NSArray. The self.fbFriends is an Array as would not change, the self.searchResults is the MutableArray for search results. Secondly, the search does not work as expected. I can type and the filter begins to work but as soon as i get to say three letters of a name clearly in the table view it does not show in results? – StuartM May 20 '13 at 22:20
  • Ok, first of all `filterArrayUsingPredicate` returns a NSArray not a NSMutableArray since the first warning, so you should change the type of self.searchResults to `NSArray` or use another variable. Second, are you performing other operations to the self.fbFriends and I suppos that you are showing data from self.searchResults in your searchResults table view – danypata May 20 '13 at 22:26
  • Ok. No to your second question no other changes other than shown in the question. Here is my code for the cells if (tableView == self.searchDisplayController.searchResultsTableView) { NSDictionary *friend = (NSDictionary *)[self.searchResults objectAtIndex:indexPath.row]; – StuartM May 20 '13 at 23:00
  • And are you sure that the array contains a dictionary with the searched string ? – danypata May 21 '13 at 07:04
  • Yes. I am not sure your code is correct, what does the bindings dictionary do within the block? Is it not missing the for loop to create the reference to each friend. Surely the object dictionary in the above code is just empty? – StuartM May 21 '13 at 08:08
  • No, the predicateWithBlock iterates trough the array and the `objeect` is always an array element. Regarding the `bindings` param check this great article http://www.wannabegeek.com/?p=149. Also the code is correct, i use offen this format of the NSPredicate and I get the proper results – danypata May 21 '13 at 08:17
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/30305/discussion-between-danypata-and-stuartm) – danypata May 21 '13 at 08:22
  • Clicking answer your question does nothing, so i have flagged it thanks – StuartM May 25 '13 at 13:10
  • @StuartM What is your last comment about ? – danypata May 27 '13 at 08:41
  • Per above, I found the answer with NSRange. However, I cannot press the button 'Answer your question' below and put my own answer in... it does nothing when clicked, so I have flagged for an admin to take a look – StuartM May 27 '13 at 19:14