Parsing JSON data and handling an Array - ios

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;

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.

Create custom NSString from JSON Array using Mantle

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?

stringByAppendingString not concatenating

I'm trying to take an array of strings and take each item and put it into a string format. I wrote a method to do this as I need to concatenate the listed array values into another string. For some reason I cannot get the array values to list properly, a blank string is returned.
- (NSString*)listParameters:(NSArray*)params
{
NSString *outputList = #"";
if (params) {
for (int i=0; i<[params count]; i++) {
NSLog(#"%#",[params objectAtIndex:i]);
[outputList stringByAppendingString:[params objectAtIndex:i]];
if (i < ([params count] - 1)) {
[outputList stringByAppendingString:#", "];
}
}
}
NSLog(#"outputList: %#", outputList);
return outputList;
}
The first log statement properly returns a string (so there is definitely a string in the array), but the second log statement only returns "outputList: ".
I tried making outputList start as more than just an empty string which didn't work. I also tried assigning [params objectAtIndex:i] to a string then appending it, didn't work either.
I feel like I'm missing something obvious here, but I cannot get it to work.
How can I get this array of strings to print into a single string separated by commas?
You probably want to use a NSMutableString instead with its appendString method. NSString is immutable.
- (NSString*)listParameters:(NSArray*)params
{
NSMutableString *outputList = [[NSMutableString alloc] init];
if (params) {
for (int i=0; i<[params count]; i++) {
NSLog(#"%#",[params objectAtIndex:i]);
[outputList appendString:[params objectAtIndex:i]];
if (i < ([params count] - 1)) {
[outputList appendString:#", "];
}
}
}
NSLog(#"outputList: %#", outputList);
return outputList;
}
You need to assign the result of [outputList stringByAppendingString:[params objectAtIndex:i]] and [outputList stringByAppendingString:#", "] back to outputList.
It would be better still if you were using an instance of NSMutableString for outputList instead, as you're going to create a lot of autoreleased objects in that loop otherwise.
Try:
outputList = [outputList stringByAppendingString:#", "];
as stringByAppendingString works by returning a new String

How can I access one value among some values

{
"id"=12
"genres_en" = "thriller,movies,action";
}
{
"id"=13
"genres_en" = "thriller,horror";
}
Hi everyone ,
I have one json like this..I mean i have one content and all movie has their genre ..And i need to do one categorization ..For example I need to check which genre of movie is horror..Or which one is thriller..I m getting all value like this:
--> "horror,movies"
But how can i do the controllation to check whether the genre of this movie includes horror or not..??what is your suggesstion?
Thank you
You could use componentsSeparatedByString
NSDictionary *dic = PARSE The JSON;
NSString *values = [dic objectForKey:"genres_en"];
NSString *firstValue = [[values componentsSeparatedByString:","] objectAtIndex:0];
I don't have Xcode nearby, so the following code may contain an error(or two :) ).
Try it like that:
NSDictionary *yourDict = //your parsed json
NSString *genres = [yourDict objectForKey:#"genres_en"];
if ([genres rangeOfString:#"horror"].location != NSNotFound) {
return YES;
} else {
return NO;
}
Hope it helps
for(int j=0;j< GenresArray.count; j++)
{
NSString *value=[[genre componentsSeparatedByString: #","]objectAtIndex:j];
if([value isEqualToString:#"movies"])
{
[genreMovies addObject:value];
}
}
Thank you for your help guys ..Now i have my result:)
You can try to check if the string contains the substring you want . Like this:
NSString *typesString = [dic objectForKey:"genres_en"];
NSRange range = [typesString rangeOfString:#"horror"];
if(range.location != NSNotFound)
{
//it contains the substring "horror"
}
Or , the better way would be to get the array of types.
NSArray *types = [[typesString componentsSeparatedByString:","];
Now you can store this array for each entry and whenever you want to check if it belongs to a certain gender , just loop through the types array ( of each entry ) and compare the strings.
Hope this helps.
Cheers!

Resources