Create custom NSString from JSON Array using Mantle - ios

I am using Mantle to parse some business JSON. At present we go through an array of JSON objects for the business categories with the following:
NSMutableString *stringCats = [[NSMutableString alloc] init];
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
if ([category hasSuffix:#"s"]) {
category = [category substringToIndex:[category length] - 1];
}
}
if (cats == business.yelpCategories.lastObject) {
[stringCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[stringCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
searchResultCell.stringCategories.text = stringCats;
This loops through the array of categories removes the last letter if an 's' then appends the string into one string.
This is currently completed in the cellForRowAt.. and I feel like this is not the correct place to do this sort of work.
What I would like to do is parse this data into a string on the business model created with Mantle originally rather than complete this for each cell.
Question
How do I create a custom NSValueTransformer based on our current work above to transform the JSON array into a string on the model instead?

Related

Parsing id to NSString

When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}

How to filter search within a set of letters in search bar so that each letter typed will reduce the results in objective -c

i have implemented a search bar that searching trough an array of countries(presented in a picker view), the problem is that the user need to type the full country name that it will find it and i want him to be able to type even one letter and it will show the first country that starts with that letter and if types another than it sorts even further etc etc.
Anyone have any ideas??
for(int x = 0; x < countryTable.count; x++){
NSString *countryName = [[countryTable objectAtIndex:x]objectForKey:#"name"];
if([searchedStr isEqualToString:countryName.lowercaseString]){
[self.picker selectRow:i inComponent:0 animated:YES];
flag.image = [UIImage imageNamed:[[countryTable objectAtIndex:i]objectForKey:#"flag"]];
}
}
There's a method on NSArray called filteredArrayUsingPredicate: and a method on NSString called hasPrefix:. Together they do what you need...
NSString *userInput = //... user input as lowercase string. don't call this countryName, its confusing
NSPredicate *p = [NSPredicate predicateWithBlock:^BOOL(id element, NSDictionary *bind) {
NSString countryName = [[element objectForKey:#"name"] lowercaseString];
return [countryName hasPrefix:userInput];
}];
NSArray *filteredCountries = [countryTable filteredArrayUsingPredicate:p];
If you're on iOS 8 or OS X Yosemite, you can do:
NSString *country = countryName.lowercaseString; //"england"
NSString *needle = #"engl";
if (![country containsString:needle]) {
NSLog(#"Country string does not contain part (or whole) of searched country");
} else {
NSLog(#"Found the country!");
}
Else, if on versions below iOS 8:
NSString *country = countryName.lowercaseString; //"england"
NSString *needle = #"engl";
if ([country rangeOfString:needle].location == NSNotFound) {
NSLog(#"Country string does not contain part (or whole) of searched country");
} else {
NSLog(#"Found the country!");
}
Lastly, just iterate through all possible countries and apply this to them all. There might exist more robust solutions out there (like danh's solution with some smaller modifications), but this is by far the easiest to start with.

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;

JSON with Dictionary - nested objects to convert to strings and display

I came across few posts here related to what I am doing but I am working with some nested objects that I want to extract.
This is a sample of my returned data - https://gist.github.com/ryancoughlin/8043604
I have this in my header so far :
#import "TideModel.h"
#protocol TideModel
#end
#implementation TideModel
-(id)initWithDict:(NSDictionary *)json {
self = [super init];
if(self) {
self.maxheight = [dictionary valueForKeyPath:#"tide.tideSummaryStats.minheight"];
self.minheight = [dictionary valueForKeyPath:#"tide.tideSummaryStats.maxheight"];
self.tideSite = [dictionary valueForKeyPath:#"tide.tideInfo.tideSite"];
}
return self;
}
#end
I have declared a property for each string and i am accessing it accordingly.
But what I have above doesn't work, maybe because it wont know what to drill in to correct?... Or will it?
tide.tideSummaryStats returns an array.
tide.tideInfo returns an array.
So you can't do -valueForKeyPath: all the way.
Also, this is incorrect: [dictionary valueForKeyPath:...];
it should be : [json valueForKeyPath:...];
because json is the name of the NSDictionary variable passed (not dictionary)
Try this (not sure):
-(id)initWithDict:(NSDictionary *)json {
self = [super init];
if(self) {
NSArray *arrOfTideSummaryStats = [json valueForKeyPath:#"tide.tideSummaryStats"];
NSDictionary *dctOfTideSummaryStats = [arrOfTideSummaryStats objectAtIndex:0];
//since self.maxheight and self.minheight are NSString objects and
//the original keys "minheight" & "maxheight" return float values, do:
self.maxheight = [NSString stringWithFormat:#"%f", [dctOfTideSummaryStats valueForKey: #"maxheight"]];
self.minheight = [NSString stringWithFormat:#"%f", [dctOfTideSummaryStats valueForKey: #"minheight"]];
/*============================================================*/
NSArray *arrOfTideInfo = [json valueForKeyPath:#"tide.tideInfo"];
NSDictionary *dctOfTideInfo = [arrOfTideInfo objectAtIndex:0];
self.tideSite = [dctOfTideInfo valueForKey:#"tideSite"];
}
return self;
}
Similar Questions:
How to parsing JSON object in iPhone SDK (XCode) using JSON-Framework
Getting array elements with valueForKeyPath
Keypath for first element in embedded NSArray
Recently had to create a app that worked with a remote RESTful server that returned JSON data and was then deserialised into an object for graphing.
I used unirest for the requests and responses and then deserialised the returned JSON into an object. Below is an extract of the code where "hourlySalesFigures" within dictionary "jsonResponseAsDictionary" was a JSON collection of 24 figures which I put into an array. Please note the function is a lot larger but I removed anything which I thought was distracting.
- (PBSSales*) deserializeJsonPacket2:(NSDictionary*)jsonResponseAsDictionary withCalenderType:(NSString *)calendarViewType
{
PBSSales *pbsData = [[PBSSales alloc] init];
if(jsonResponseAsDictionary != nil)
{
// Process the hourly sales figures if the day request and returned is related to Daily figures
if([calendarViewType isEqualToString:#"Day"]){
NSArray *hourlyFiguresFromJson = [jsonResponseAsDictionary objectForKey:#"hourlySalesFigures"];
PBSDataDaySales *tmpDataDay = [[PBSDataDaySales alloc] init];
NSMutableArray *hSalesFigures = [tmpDataDay hourlySalesFigures];
for(NSInteger i = 0; i < [hourlyFiguresFromJson count]; i++){
hSalesFigures[i] = hourlyFiguresFromJson[i];
}
[[pbsData dataDay] setHourlySalesFigures:hSalesFigures];
[pbsData setCalViewType:#"Day"];
}
}
return pbsData;
}

Pulling data out of NSMutableDictionary and adding to a different NSMutableDictionary

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

Resources