Pulling data out of NSMutableDictionary and adding to a different NSMutableDictionary - ios

SETUP
I have a NSMutableDictionary with over 800 NSMutableDictionaries in it representing employees. I am trying to implement a search bar and i am having serious issues working with my dictionaries.
In the first For Loop i cread a dictionary for searching, and in the send i am attempting to search each employee in that dictionary.
PROBLEM
How can i add an individual dictionary to a new dictionary to hold all the dictionaries with the search term in it?
- (void) searchTableView:(UISearchBar *)theSearchBar {
NSString *searchText = theSearchBar.text;
NSMutableDictionary *searchDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *employee in employeeData)
{
[searchDict setValue:employee forKey:[employee objectForKey:kFULLNAME_TAG]];
}
for (NSDictionary *emp in searchDict)
{
NSString *empName = [emp objectForKey:kFULLNAME_TAG];
NSRange titleResultsRange = [empName rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0){
NSLog(#"search result ---> %#" ,emp);
[copyListOfItems setValue:empName forKey:emp];
}
}
}
In the second For Loop, I am having trouble with the copyListOfItems setValue:empName forKey:emp.

I think you have your parameters backwards when inserting into your copyListOfItems (which I'm assuming is an NSMutableDictionary ivar in your class?). The employee object should be the value and the employee name should be the key.
[copyListOfItems setValue:emp forKey:empName];
You shouldn't need to use two loops to accomplish what you need though. This would be simpler:
for (NSDictionary *emp in employeeData)
{
NSString *empName = [emp objectForKey:kFULLNAME_TAG];
NSRange titleResultsRange = [empName rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.location != NSNotFound){
[copyListOfItems setValue:emp forKey:empName];
}
}

Related

How to detect if a dictionary is empty or null

I am receiving a JSON string that I need to iterate to retrieve some objects values.
This is the structure
-meta
-objects
|_cabdriver
|_employee
|client
There are objects under the objects tree and there are also child nodes, like cabdriver and client. The child node cabdriver has also another child node called employee.
This is the way I am iterating it:
NSArray *messageArray = [json objectForKey:#"objects"];
historialServicios = [[NSMutableArray alloc]init];
// Parse and loop through the JSON
for (dictionary in messageArray) {
//datos de nivel objects
NSString * date = [dictionary objectForKey:#"date"];
NSString * origin = [dictionary objectForKey:#"origin"];
NSString * destiny = [dictionary objectForKey:#"destiny"];
NSString * rate = [dictionary objectForKey:#"service_rate"];
NSString * state = [dictionary objectForKey:#"state"];
NSString * time_service = [dictionary objectForKey:#"time_service"];
NSString * id_service = [dictionary objectForKey:#"id"];
//datos de nivel cliente
NSDictionary *level2Dict = [dictionary objectForKey:#"client"];
NSString *client_id = [level2Dict objectForKey:#"id"];
//datos de nivel cabdriver
NSDictionary *cabdriverLevelDict=[dictionary objectForKey:#"cabdriver"];
//datos de nivel employee
NSDictionary *employeeLevelDict = [cabdriverLevelDict objectForKey:#"employee"];
//datos del employee
NSString *driverName = [employeeLevelDict objectForKey:#"name"];
NSString *driverLastname = [employeeLevelDict objectForKey:#"lastname"];
NSString *driverPhone = [employeeLevelDict objectForKey:#"phone"];
NSString *driverId = [employeeLevelDict objectForKey:#"id"];
[historialServicios addObject:#{
#"time_service": time_service,
#"id_service": id_service,
#"rate": rate,
#"destiny": destiny,
#"state": state,
#"origin": origin,
#"client_id":client_id,
#"date": date,
#"driverName":driverName,
#"driverLastname": driverLastname,
#"driverPhone": driverPhone,
#"driverId": driverId
}];
NSLog(#"DESPUES DE ANADIR OBJETOS");
NSLog(#"OBJETO ANADIDO==>TIME SERVICE = %#, ID SERVICE=%#, SERVICE RATE=%#,SERVICE DATE=%#,DESTINY=%#, STATE =%#,CLIENT ID=%#, ORIGIN=%#,DRIVER NAME=%#, DRIVER LASTNAME=%#,DRIVER PHONE=%#, DRIVER ID=%#",time_service,id_service,rate,date,destiny,state,client_id,origin,driverName,driverLastname,driverPhone,driverId);
//insertamos objetos en diccionario historialServicios
}
Everything works fine if the object has all nodes but some times, the node cabdriver is empty and doesn't have the employee child node. If it is the case I get an exception is thrown and the app crashes.
How can I determined if the node employee doesn't exist and avoid to get the exception?
Thank you.
You could declare a category to deal with the [NSNull null] values that are injected into your json.
#interface NSDictionary (NilNull)
- (id)optionalObjectForKey:(id)key;
- (id)optionalObjectForKey:(id)key defaultValue:(id)defaultValue;
#end
#implementation NSDictionary (NilNull)
- (id)optionalObjectForKey:(id)key {
return [self optionalObjectForKey:key defaultValue:nil];
]
- (id)optionalObjectForKey:(id)key defaultValue:(id)defaultValue {
id obj = [self objectForKey:key];
return (obj == [NSNull null] || !obj) ? defaultValue : obj;
}
#end
Then use that instead:
NSDictionary *cabdriverLevelDict = [dictionary optionalObjectForKey:#"cabdriver"];
NSDictionary *employeeLevelDict = [cabdriverLevelDict optionalObjectForKey:#"employee"];
You haven't posted the contents of your exception, but from the looks of it, it's probably related to trying to add nil values to your new dictionary.
Then use a default value of [NSNull null] for all your data lookups that produce objects with which you will construct your final dictionary. The full lookup source will now be like this:
NSString * date = [dictionary optionalObjectForKey:#"date" defaultValue:[NSNull null]];
NSString * origin = [dictionary optionalObjectForKey:#"origin" defaultValue:[NSNull null]];
NSString * destiny = [dictionary optionalObjectForKey:#"destiny" defaultValue:[NSNull null]];
NSString * rate = [dictionary optionalObjectForKey:#"service_rate" defaultValue:[NSNull null]];
NSString * state = [dictionary optionalObjectForKey:#"state" defaultValue:[NSNull null]];
NSString * time_service = [dictionary optionalObjectForKey:#"time_service" defaultValue:[NSNull null]];
NSString * id_service = [dictionary optionalObjectForKey:#"id" defaultValue:[NSNull null]];
//datos de nivel cliente
NSDictionary *level2Dict = [dictionary optionalObjectForKey:#"client" defaultValue:[NSDictionary dictionary]];
NSString *client_id = [level2Dict optionalObjectForKey:#"id" defaultValue:[NSNull null]];
//datos de nivel cabdriver
NSDictionary *cabdriverLevelDict=[dictionary optionalObjectForKey:#"cabdriver" defaultValue:[NSDictionary dictionary]];
//datos de nivel employee
NSDictionary *employeeLevelDict = [cabdriverLevelDict optionalObjectForKey:#"employee" defaultValue:[NSDictionary dictionary]];
//datos del employee
NSString *driverName = [employeeLevelDict optionalObjectForKey:#"name" defaultValue:[NSNull null]];
NSString *driverLastname = [employeeLevelDict optionalObjectForKey:#"lastname" defaultValue:[NSNull null]];
NSString *driverPhone = [employeeLevelDict optionalObjectForKey:#"phone" defaultValue:[NSNull null]];
NSString *driverId = [employeeLevelDict optionalObjectForKey:#"id" defaultValue:[NSNull null]];
Try this here:
if( cabdriverLevelDict.allkeys.count ){
// Do something with the dict
} else {
// dict is empty
}
Basically, you need to check every single result that you get. If you don't do that, your app is open to attacks, and one attack might allow a hacker into the user's device and cause unlimited damage. Where you expect a dictionary, you might get nil, you might get a null, you might get a number, or a string, just anything. It's quite simple.
NSDictionary* dict = ...;
if (! [dict isKindOfClass:[NSDictionary class]]) dict = nil;
In Objective-C, nil objects are quite safe. You can use objectForKey [#"employee"], for example, and all that will happen is that you get nil as the result. And you could have received nil anyway.
There is no point checking for [NSNull null] only, because any other result that the server gave you will crash your app just the same. Just check for what you actually expect. Throwing away incorrect data is fine, after all the JSON deserialiser will throw away everything if just a single byte of data is wrong.
Sometimes you need to do a bit more care because servers misbehave and you have to cope with it. For example, a server supposed to return an array of dictionaries might give you just a dictionary if there is only one, so you would check for example
NSArray* arrayOfDicts = ...;
if ([arrayOfDicts isKindOfClass:[NSDictionary class]] arrayOfDicts = #[arrayOfDicts];
else if (! [arrayOfDicts isKindOfClass:[NSArray class]] arrayOfDicts = nil;
As others have pointed out, if any of the objects passed into the dictionary are nil, that will throw an exception that crashes your app. By doing the following:
[historialServicios addObject:#{
#"time_service": time_service,
#"id_service": id_service,
#"rate": rate,
#"destiny": destiny,
#"state": state,
#"origin": origin,
#"client_id":client_id,
#"date": date,
#"driverName":driverName,
#"driverLastname": driverLastname,
#"driverPhone": driverPhone,
#"driverId": driverId
}];
You're depending that all these objects (eg time_service, id_service, etc) are not nil. As you've pointed out, they can be nil, so you need to have a means of checking for each object you do. I would recommend using an NSMutableDictionary, making a category method that only adds the key/value pair if they are both not nil:
#implementation NSMutableDictionary (Util)
-(void)setObjectOrRemoveIfNil:(id)anObject forKey:(id<NSCopying>)aKey
{
if (anObject == nil)
{
[self removeObjectForKey:aKey];
}
else
{
[self setObject:anObject forKey:aKey];
}
}
#end
And then put together your dictionary like so:
NSMutableDictionary* values = [NSMutableDictionary dictionary];
[values setObjectOrRemoveIfNil:time_service forKey:#"time_service"];
[values setObjectOrRemoveIfNil:id_service forKey:#"id_service"];
//Keep going with the rest of your values.
Finally we use that dictionary like you did already:
[historialServicios addObject:values];
check the count for the dictionary
if ([cabdriverLevelDict count] == 0) {
NSLog("empty");
}
else{
// Do your stuff !!
}
if (![cabdriverLevelDict isKindOfClass:[NSNull class]] ){
//do something
}
try this
You can try
NSDictionary *employeeLevelDict = [cabdriverLevelDict objectForKey:#"employee"];
if (employeeLevelDict.count != 0)
{
// do something if dict is not empty
}
else
{
}];

How do you find characters in an NSArray and return YES in Objective-C?

I am trying to find "Worf" inside the array of strings and then returning the BOOL of "YES" if I do find it, but if not, then return a BOOL of "NO".
So far this is what I have but it isn't working.
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
NSString *myWorfString = #"Worf";
BOOL yesOrNo = NO;
for (NSString *task in myArray) {
if ([task isEqualToString: myWorfString]) {
yesOrNo = YES;
return yesOrNo;
} else {
return yesOrNo;
}
How would I input "return [myArray containsObject:myWolfString];" into my equation?
First of all you shouldn't compare objects with == unless you know what you are doing, since it compares memory addresses (where they are allocated) and not the contents. So two NSString* which points to two different instances which contain the same value are not equal according to ==.
You should use isEqualToString: or isEqualTo:.
In addition NSArray already contains this functionality, you don't need to reinvent the wheel:
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
BOOL yesOrNo = [myArray containsObject:#"Worf"];
You have a logic error - you return immediately if the first item in the array is not #"Worf". While others have proposed solutions that will work, this one is most like your original code:
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
NSString *myWorfString = #"Worf";
for (NSString *task in myArray)
if ([task isEqualToString: myWolfString])
return YES;
return NO;
Rather than go through the items one at a time, you can search the array concurrently, with lots of searches happening in parallel.
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
NSString *myWorfString = #"Worf";
__block BOOL searchStringFound = NO;
[myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *string, NSUInteger idx, BOOL *stop) {
if ([string isEqualToString:myWorfString]) {
searchStringFound = YES;
*stop = YES;
}
}];
What's the advantage of this? It's faster, rather than search the array serially, you are doing it concurrently. Okay, so in this instance with such a small array it's unlikely to make much difference, but for larger arrays it could.
It's also a useful technique to use when looking for objects in collections. Because once you have found the object, you just set the stop parameter to YES to signal that no more enumerations need to be performed.
If you are looking for a substring:
- (BOOL)array:(NSArray *)array containsSubstring:(NSString *)substringToSearchFor
{
for (NSString *string in array) {
if (! [string isKindOfClass:[NSString class]])
continue; // skip objects that are not an NSString
if ([string rangeOfString:substringToSearchFor].location != NSNotFound)
return YES;
}
return NO;
}
Never compare strings with == use isEqualToString: instead.
You can just write:
return [myArray containsObject:myWolfString];
If you need to also check substring, you can use NSPredicate :
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
NSString *myWolfString = #"Worf";
NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"SELF CONTAINS [c] '%#'",myWolfString]];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:pred];
if ([filteredArray count])
{
NSLog(#"Index Of Object in main Array : %lu",(unsigned long)[myArray indexOfObject:filteredArray[0]]);
}
There are various ways you can find characters in NSArray and return YES in objective-c:-
1) By using NSPredicate
NSPredicate *pd=[NSPredicate predicateWithFormat:#"self MATCHES[CD] %#",myWorfString];
BOOL yesOrNo=[[NSNumber numberWithUnsignedLong:[[myArray filteredArrayUsingPredicate:pd]count]]boolValue];
2) Second using containsObjectapi
BOOL yesOrNo = [myArray containsObject:myWorfString];
3) Refer #EricS answer
NSArray *myArray = #[#"Worf", #"son of Mogh", #"slayer of Gowron"];
NSString *myWorfString = #"Worf";
BOOL yesOrNo = NO;
for (NSString *task in myArray)
{
if (([task isEqualToString: myWorfString]) || ([task rangeOfString:myWorfString].location != NSNotFound)) {
yesOrNo = YES;
break;
}
}
return yesOrNo;
Above code will return either array will contain as string or substring in array.

Parsing JSON data and handling an Array

I am using Mantle to parse some JSON data from Yelp.
For each business returned I get an NSArray of categories. This would be an example:
yelpCategories = (
(
"Wine Bars",
"wine_bars"
),
(
"Ice Cream & Frozen Yogurt",
icecream
)
);
yelpCategories is the name of the array that I save. Later on I am trying to parse the array into a string:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (NSObject * obj in business.yelpCategories)
{
[yelpCats appendString:[NSString stringWithFormat:#"%#,",[obj description]]];
}
The issue is with the above. I am being returned a string just as "(" so I must be accessing the array incorrectly. How can I correctly access each object, ideally I would be looking for the end string o be #"Wine Bars, Ice Cream & Frozen Yogurt".
EDIT
The categories array: (
(
Pubs,
pubs
)
)
FINAL EDIT - Proposed Solution
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;
Using the description of the object gives you what you see in the debugger, which includes extra carriage returns.
What you want to do is something like:
yelpCats = [yelpCategories componentsJoinedByString:#", "];
#jeffamaphone 's answer is the correct and best way of doing things however what your doing will almost work, I think your just confused on the contents of the array.
The yelpCategories array is an array of strings so you don't need to call stringWithFormat or call the description method. In fact [obj description] will return a string so you didn't even need stringWithFormat in your example and you would have gotten the same output. To make your original method work change to:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (id obj in business.yelpCategories)
{
//obj is a string so we can just append it.
[yelpCats appendString:obj]];
}
Also noticed I changed NSObject *obj to just id obj, this is the idiomatic way and shorthand way of declaring NSObjects in objective-c. In this example however I would actually use (NSString *category in business.yelpCategories) instead for better readability. In this case you are declaring to everyone that you expect each object in the array to be a string and then if you wanted to use NSString methods on it inside the loop then you don't have to cast it.
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;

select indexOfObject from NSArray in Xcode by comparing string

Hi I have NSarray values in Xcode. I need to get array indexOfObject by comparing string values.
my array values are
(
{
firstName = lord;
lastname = krishna;
},
{
firstName = priya;
lastname = amirtha;
}
)
If I type first name in textfield and click button means last name want to display in another textfield.
thank you.
To answer the title of your question:
NSString *compareString = #"something";
NSMutableArray *indexesOfMatches = [[NSMutableArray alloc]init];
for (NSString *string in theArray) {
if ([string isEqualToString:compareString]) {
NSNumber *index = [[NSNumber numberWithInterger:[theArray indexOfObject:string]];
[indexOfMatches addObject:index];
}
}
//indexOfMatches will now contain NSNumber objects that represent the indexes of each of the matching string objects in the array
I think that using an NSDictionary would be better for you though. Then you can simply keep the first and last names as Key Value pairs.
NSDictionary *names = #{#"lord" : #"krishna", #"priya" : #"amirtha" };
Then you can just do value for key when you get the first name:
NSString *firstName = #"lord";
NSString *lastName = [names valueForKey:firstName];
Store firstNameArray and lastNameArray a mutable array NSMutableArray.
Using Fast Enumeration. Suppose array is the array you are provided with
for (NSDictionary *item in array) {
[firstNameArray addObject:[item objectForKey:#"firstName"]];
[lastNameArray addObject:[item objectForKey:#"lastName"]];
}
After entering the data in firstNameTextField click the button
Button action method implementation
-(IBAction)btnClicked:(id)sender {
NSInteger index = [firstName indexOfObject:[firstNameTextField text]];
[lastNameTextField setText:[lastName objectAtIndex:index]];
}

How to separate contents from NSArray into two new one in Objective C?

any one here have an idea how I could separate this array in to 2??
2013-08-25 02:47:47.052 yahoo[11357:c07] (
"",
"1377253260000.33300.0",
"1377253440000.33280.0",
"1377254100000.33280.0",
"1377255600000.33220.0",
"1377257400000.33220.0",
"1377261660000.33200.0",
"1377264000000.33200.0",
"1377264060000.33200.0",
"1377267780000.33200.0",
"1377271260000.33200.0",
"1377273120000.33200.0",
"1377273180000.33200.0",
"1377273240000.33240.0",
""
)
The first NSArray would be with the long numbers and the second with the smaller one including the ".".
So something like: array1 with 1377253260000 and array2 with 33300.0 and so on.
There are tons of different ways of doing this. For example, you could do something as simple as find the first period, and add the string up to that period in the first array and everything after in the next array:
NSMutableArray *smallerNumbers = [NSMutableArray array];
NSMutableArray *longNumbers = [NSMutableArray array];
for (NSString *string in array) {
NSRange range = [string rangeOfString:#"."];
if (range.location != NSNotFound) {
[longNumbers addObject:[string substringToIndex:range.location - 1]];
[smallerNumbers addObject:[string substringFromIndex:range.location + 1]];
} else {
[longNumbers addObject:#""]; // or you could insert [NSNull null] or whatever
[smallerNumbers addObject:#""];
}
}
Another way..
NSArray *objects = #[
#"",
#"1377253260000.33300.0",
#"1377253440000.33280.0",
#"1377254100000.33280.0",
#"1377255600000.33220.0",
#"1377257400000.33220.0",
#"1377261660000.33200.0",
#"1377264000000.33200.0",
#"1377264060000.33200.0",
#"1377267780000.33200.0",
#"1377271260000.33200.0",
#"1377273120000.33200.0",
#"1377273180000.33200.0",
#"1377273240000.33240.0",
#""
];
NSMutableArray *firstParts = [[NSMutableArray alloc] initWithCapacity:objects.count];
NSMutableArray *secondParts = [[NSMutableArray alloc] initWithCapacity:objects.count];
for (NSString *object in objects)
{
NSArray *components = [object componentsSeparatedByString:#"."];
if (components.count > 0) {
[firstParts addObject:components[0]];
}
if (components.count > 1) {
[secondParts addObject:components[1]];
}
}
NSLog(#"firstParts = %#", firstParts);
NSLog(#"secondParts = %#", secondParts);

Resources