Searching through an array of dictionaries - iphone

I'm making an iPhone app which displays information about red wines. The wines are stored in a plist containing an array of dictionaries. I'm adding a search view controller to the application, and to start up easy I want to get the wines who the value for the "Name" key contains nameString (from textbox).
It kind of stops here, I need some advices about the most suitable way of doing this. Is there a function in the NSArray class which will do the job, should bring in NSPredicate, NSUserDefaults, or are there other options? I've done some research but I'm going to need some advices and maybe an example to get started.
I will advance the search function to let the user include/exclude countries, get wines that suit this and that food, set minimum price, maximum price, and so on. The dictionaries have strings for all this info. So before I start on something advanced like this I'll need some advice for which functions could do my job the best.
-(IBAction)searchButtonPoke:(id)sender{
NSString *path = [[NSBundle mainBundle] pathForResource:#"Wines" ofType:#"plist"];
allObjectsArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSString *nameString = [NSString stringWithFormat:#"%#", [nameTextField text]];
resultObjectsArray = /*objects where the value for the key "Name" contains
nameString*/;

How about simply iterating through the array and compare the names?
resultObjectsArray = [NSMutableArray array];
for(NSDictionary *wine in allObjectsArray)
{
NSString *wineName = [wine objectForKey:#"Name"];
NSRange range = [wineName rangeOfString:nameString options:NSCaseInsensitiveSearch];
if(range.location != NSNotFound)
[resultObjectsArray addObject:wine];
}
Swift version is even simpler:
let resultObjectsArray = allObjectsArray.filter{
($0["Name"] as! String).range(of: nameString,
options: .caseInsensitive,
range: nil,
locale: nil) != nil
}
Cheers,
anka

This Works !!! tested !!!
for (NSDictionary* dict in Array) {
if ([[dict objectForKey:#"key"] isEqualToString:string]) {
Index = [Array indexOfObject:dict];
}
}

We can use NSPredicate too, like this:
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"publisher == %#", #"Apress" ];
NSArray *filtered = [bookshelf filteredArrayUsingPredicate:predicate];
If the publisher can be found in the bookshelf array, filtered count will be bigger than 0.
I thinks this way is much cleaner way to search. Hope it helps.

remember to break; out your for loop once your object has been found

Related

Creating dynamic NSMutableDictionary query with multiple values

I'm working on a project and I want to be able to handle some template type messages. The template would contain something like:
"{{user1}} has just created an account"
I then have a data map that would give you a location within the NSMutableDictionary where the data is located:
"activity.message.status"
I then want to be able to query the NSMutableDictionary by splitting up that string, so that it becomes something like:
[[[myDictionary objectForKey:#"activity"] objectForKey:#"message"] objectForKey:#"status"]
I could make something as long as it was consistant on being just 3 strings, but some may be more or less.
Any help would be extremely appreciated.
It's actually much easier than splitting strings into keys. Apples Key-Value-Coding allows exactly what you want.
[myDictionary valueForKeyPath:#"activity.message.status"];
A key path is a string of dot separated keys that is used to specify a sequence of object properties to traverse. The property of the first key in the sequence is relative to the receiver, and each subsequent key is evaluated relative to the value of the previous property.
For example, the key path address.street would get the value of the address property from the receiving object, and then determine the street property relative to the address object.
Key-Value Coding Programming Guide
You would do something like,
NSArray *array = [#"activity.message.status" componentsSeperatedByString:#"."];
Which will create an array containing {activity,message,status).
Now you have your array you can use for querying your dictionary.
[[[myDictionary objectForKey:[array objectAtIndex:0]] objectForKey:[array objectAtIndex:1]] objectForKey:[array objectAtIndex:2]];
Which is equivalent to:
[[[myDictionary objectForKey:#"activity"] objectForKey:#"message"] objectForKey:#"status"];
Hope this helps !
It's not clear to me from your question how we should map user1 to activity.message.status. For now I'll assume you mean that the template might contain a string like "{{activity.message.status}}" and you want to be able to parse that.
Here's one iteration that operates on an NSMutableString that can be looped until no match is found:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\{\\{.+?\\}\\}"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSRange matchRange = [regex rangeOfFirstMatchInString:string
options:0 range:NSMakeRange(0, [string length])];
NSRange keyPathRange = NSMakeRange(matchRange.location + 2, matchRange.length - 4);
NSString *keyPath = [string substringWithRange:keyPathRange];
NSString *newSubstring = [myDictionary valueForKeyPath:keyPath];
[string replaceCharactersInRange:matchRange withString:newSubstring];
I haven't tested this code.
How about a (recursive ... cool) category method on NSMutableDictionary like this:
- (void)setObject:(id)object forCompoundKey:(NSString *)compoundKey {
NSArray *keys = [compoundKey componentsSeparatedByString:#"."];
if ([keys count] == 1) {
return [self setObject:object forKey:compoundKey];
}
// get the first component of the key
NSString *key = [keys objectAtIndex:0];
// build the remaining key with the remaining components
NSRange nextKeyRange;
nextKeyRange.location = 1;
nextKeyRange.length = [keys count] - 1;
NSArray nextKeys = [keys subarrayWithRange:nextRange];
NSString *nextKey = [nextKeys componentsJoinedByString:#"."];
NSMutableDictionary *nextDictionary = [NSMutableDictionary dictionary];
[self addObject:nextDictionary forKey:key];
// now the cool part... recursion
[nextDictionary setObject:object forCompoundKey:nextKey];
}
I haven't tested this, but it passes a quick desk check. The objectForCompoundKey: retrieval can be written analogously.

Filter Through Array

How would I filter through an array and return values that contain a certain part of a string? I have a text box where, for the sake of this example, a user puts in 25, and then hits a "Done" button.
Example:
Original Array {25-1002, 25-1005, 12-1003, 1000-0942, 1-1, 234-25}
I want it to return (after sorting through it and pulling the values I want):
New Array {25-1002, 25-1005}
Please note that in the original array, the last value of 234-25 has a 25 in it as well but is not pulled through. It needs to be the number on the first part of the hyphen.
Thanks in advance!
Use the -filteredArrayUsingPredicate: method, like this:
NSString *searchText = [someField.text stringByAppendingString:#"-"];
newArray = [originalArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^(NSString *value, NSDictionary *bindings){
return ([value rangeOfString:searchText].location != NSNotFound)
}]];
Note that blocks (the ^{} thing) aren’t available pre-iOS 4, so you’ll have to use another of NSPredicate’s constructors if you’re targeting 3.x devices as well.
as an easy to understand answer (not using NSPredicate, which can be intimidating (but is really the correct way to do it)):
NSMutableArray *myNewArray = [[NSMutableArray alloc] init];
for (NSString *string in myArray) {
if([[string substringToIndex:3] isEqualToString #"25-"])
{
[myNewArray addObject:string];
}
}

Removing all values (strings) matching a search term from arrays located in a dictionary?

Currently I'm programming an app with a tableView, similar to that one in the iPhone Contacts app.
Everything works (the sections, the bar on the right showing the titles, the cells are configured...), beside the search bar. I'm familiar how to do this (search) if the tableView's data is loaded from an array, but my situation is that its loaded from arrays located in a NSDictionary.
The dict looks like
Key = "A" >> Value = "array = apple, animal, alphabet, abc ..."
Key = "B" >> Value = "array = bat, ball, banana ..."
How can I remove all strings (from all of the dictionary's arrays) matching the search term?
Thanks a lot in advance :)
Well you can do it like this
NSMutableDictionary *newItems = [NSMutableDictionary dictionary];
for (NSString *key in oldItems) {
NSMutableArray *newArray = [NSMutableArray array];
for (NSString *item in [oldItems objectForKey:key]) {
if ([item rangeOfString:searchTerm].location != NSNotFound) {
[newArray addObject:item];
}
}
if ([newArray count]) {
[newItems setObject:newArray forKey:key];
}
}
[oldItems release];
oldItems = [newItems retain];
I don't know if this is the best way to do it or even if it's faster enough but let me know if this works for you.
Did you want to update the existing Dictionary with the new Array that excludes that string?
NSMutableDictionary* excludedDictionary = [NSMutableDictionary dictionaryWithDictionary:existingDictionary];
for(id key in [existingDictionary allKeys])
{
NSArray* existingArray = [existingDictionary objectForKey:key];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self != %#", excludedString];
NSArray* excludedArray = [existingArray filteredArrayUsingPredicate:predicate];
[excludedDictionary setObject:excludedArray forKey:key];
}
existingDictionary = [NSDictionary dictionaryWithDictionary:excludedDictionary];
This will replace your existing dictionary with one that doesn't have the string in it...
From you comments, I understand that you want to filter the table contents on the basis of what the user enters in the text field. For this, you do not need to modify your dictionary at every character change. The UISearchDisplayController is provided for exactly this scenario. Have a look at the reference for details: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UISearchDisplayController_Class/Reference/Reference.html.
HTH,
Akshay

how to search nsmutable array in objective C

can any body tell me how to search NSMutable Arry in which objects are stored from xml feed
i have the following code
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
//blog entries is the nsmutable array in which objects are stored from RSS feed
for (NSMutableDictionary *dictionary in blogEntries)
{
NSArray *images=[dictionary objectForKey:#"image"];
NSArray *titlearray = [dictionary objectForKey:#"title"];
NSDictionary *imagesDic=[NSDictionary dictionaryWithObject:images forKey:#"image"];
NSDictionary *titlearrayDic=[NSDictionary dictionaryWithObject:titlearray forKey:#"title"];
[searchArray addObject:imagesDic];
[searchArray addObject:titlearrayDic];
}
//know the problem comes in below code i just want to compare title not image string as there any way to search only of title array not for both image in title some what like this For(nsstring *stemp in searcArray.titleArray etc)
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0){
[copyListOfItems addObject:sTemp];
}}
the problem is that this code just saving title not image and if i save image then it also search in image string which i dont want to do. i want the user will search only by title then when he type something in textbox if search is true against some values then only thos e are displayed in table cell with title and image.
as this is RSS APPLiction and feeds are comming from xml feed
which
click here
bescially i am extracting this xml feed and em displaying image and title tage in table cell know i want to implement searchbar in it
Thanks....i am waiting for your response...
#mipadi is right - try using containsObject: first.
If that doesn't work, just a simple loop will do it - you can put in whatever matching criteria you want in there. e.g. This code searches by comparing the name properties :
- (id)searchArray:(NSArray *)haystack for:(id)needle {
for(id temp in haystack)
if ([temp name] isEqual:[needle name]])
return needle;
return nil;
}
Hope that helps.
NB If you're using your own objects in the array, you can use containsObject: if you have overridden isEqual: (and hash)
Depends on how you want to search. If you're just looking for a particular object, you can use containsObject:.
Without knowing more about what you want, it's tough to answer your question. But here are some starting points:
NSArray - Check out the methods starting like indexOfObject-; I think one of these probably does what you want. There's also filteredArrayUsingPredicate.
NSMutableArray - The only notable method here is filterUsingPredicate, I think.
Hope one of these helps you.

How can I get the first element in an NSDictionary?

I have an array of NSDictionaries. How can I pull out the first element in the dictionary?
NSArray *messages = [[results objectForKey:#"messages"] valueForKey:#"message"];
for (NSDictionary *message in messages)
{
STObject *mySTObject = [[STObject alloc] init];
mySTObject.stID = [message valueForKey:#"id"];
stID = mySTObject.stID;
}
There is no "first" element in an NSDictionary; its members have no guaranteed order. If you just want one object from a dictionary, but don't care which key it's associated with, you can do:
id val = nil;
NSArray *values = [yourDict allValues];
if ([values count] != 0)
val = [values objectAtIndex:0];
NSDictionaries are unordered, meaning that there are not first or last element. In fact, the order of the keys are never guaranteed to be the same, even in the lifetime of a specific dictionary.
If you want any object, you can get one of the keys:
id key = [[message allKeys] objectAtIndex:0]; // Assumes 'message' is not empty
id object = [message objectForKey:key];
NSArray has a selector named firstObject that simplifies the code and makes it more readable:
id val = [[yourDict allValues] firstObject];
If yourDict is empty val will be nil, so is not necessary to check the dictionary/array size.
Simplest:
[[dict objectEnumerator] nextObject];
According to Apple, calls to allKeys or allValues incur the cost of creating new arrays:
A new array containing the dictionary’s values, or an empty array if
the dictionary has no entries (read-only)
So, an alternative option that does not incur such cost could look like this:
NSString* key = nil;
for(key in yourDict)
{ // this loop will not execute if the dictionary is empty
break; // exit loop as soon as we enter it (key will be set to some key)
}
id object = yourDict[key]; // get object associated with key. nil if key doesn't exist.
Note: If the dictionary is empty, the key will remain nil, and the object returned will also be nil, we therefore don't need special handling of the case where the dictionary is actually empty.
If someone is still looking for answer for this type of situation then can refer this:
// dict is NSDictionary
// [dict allKeys] will give all the keys in dict present
// [[dict allKeys]objectAtIndex:0] will give from all the keys object at index 0 because [dict allKeys] returns an array.
[[dict allKeys]objectAtIndex:0];
If you have NSDictionary named message,
It's pretty simple:
message[[[message allKeys] objectAtIndex:0]];
But you have to be sure (or better check) that your dictionary has at least one element.
Here is how you can check it:
if ([message allKeys] > 0) NSLog(#"%#", message[[[message allKeys] objectAtIndex:0]]);
But NSDictionary has no guaranteed order, so you probably should use this code only if your dictionary has only one element.
[UPDATE]
It's also good idea to use this if you need to get ANY element of dictionary
Try this:
NSDictionary *firstValue = [responseObject objectAtIndex:0];