objectAtIndex for Nested Array iOS - ios

Beginners iOS dev question here.
I have a plist in the following format:
I've converted this plist into an array in my app using:
NSMutableArray *array2 = [[NSMutableArray alloc] initWithContentsOfFile:path];
and have verified the content using NSLog.
However the issue I have is in understanding objectAtIndex. How do i obtain Item2 in Item1 i.e. the value 6? I'm used to Javas "array[1][2]" style :p

You can actually still use array[1][2] in Objective-C (with Xcode 4). Otherwise the "Objective-C way" to do it is:
[[array objectAtIndex:1] objectAtIndex:2];
But it is lots more code and for someone from most other backgrounds, not as readable either.

It was quite tedious, but Apple have provided a simple way to do it now. Simply put, it would be:
array2[1][2];
The first number inside the square brackets sends -[array2 objectAtIndex: 1] to the top level array. The second number sends the inner array the same message with 2 as an argument.

Related

iOS read data from a bit more 'complex' .plist

I know this question had been asked for several times, but I have trouble reading data in Xcode iOS. The problem was to achieve an implementation without having the keys for NSDictionaries hardcoded in the code.
Let's say, we have this .plist:
This is my plist
In this case I want to access the name and address from the restaurants dynamically without accessing the data by a hardcoded string.
Background of this is that I try to access the data on every cell on a grouped UITableView for iOS.
Anyone has some good information about this?
Generally (normally I code in c#) I know how to access dictionaries. But in this case, there are nested dictionaries with arrays which get me really confused and also frustrating :(
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyListName" ofType:#"plist"];
//Plist should exist in main bundle
NSDictionary *root = [[NSDictionary alloc] initWithContentsOfFile:path];
Then in your case your first value is a array in dictionary read that object in an array like,
NSArray * restaurantsArray = [names objectForKey:#"restaurants"];
Then so forth You know what type of object exist in the hierarchy just call respective methods of each,
like objectForKey for dictionary.
objectAtIndex for array.

How can i retrieve "extract" field from wikipedia api?

This is the link which I have to parse it in Objective C:
http://en.wikipedia.org/w/api.php?action=query&prop=extracts&format=json&exsentences=2&exintro=&titles=USA
If you clearly look at the link the word "USA" is actually going to change as per user requests.In the JSON information after pages key you can actually see a number before pageid. How can I access it? If that field is static I can access with that particular key. But it seems like that number is dynamic according to the user search. My aim is to access "extract" key. If I want to do that I have to go inside to that number which is a dynamic one. I appreciate any kind of help. Thanks in advance.
Under "pages" you are getting a dictionary with keys that are the same as the ids (just strings). It seems your question circles around the fact that you do not know these keys beforehand.
Once you extracted the pages dictionary, you can iterate through the keys like this:
for (NSString *idString in pages.allKeys) {
NSDictionary *content = pages[idString];
NSString *extract = content["extract"];
// do something with extract
}
If you just want the any key or you are sure there is only one, you can instead create the content dictionary with
NSDictionary *content = pages[pages.allKeys.firstObject];
One expression doing the same as Mundi's answer:
NSString *extract =
[[[array valueForKeyPath:#"query.pages"] allObjects].firstObject
valueForKey:#"extract"];

self referencing NSMutableDictionary

Say I have this:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[#1] = #2;
dict[#3] = dict;
I archive dict by calling:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
then I unarchive later:
NSMutableDictionary *dict2 = [NSKeyedUnarchiver unarchiveObjectWithData:data]
The problem is that dict2[#3] is not dict2, but rather an uninitialized NSDictionary, and I was not able to recover what I had put in. Does anyone know how I would work around this? Thanks in advance!
It is explained by an Apple engineer on the Apple Mailing List: http://lists.apple.com/archives/cocoa-dev/2007/May/msg00747.html,
in reply to a similar question about archiving an NSMutableArray
containing itself as an element.
Summary:
This (very probably -- I haven't looked into it) is not a problem with
the recursion per-se, but rather with objects which replace themselves
during unarchiving by returning a new object from initWithCoder:.
...
So the summary answer is: you can't reliably have recursive
references. In practice, though, these do occur (think of the NSView
subview/superview relationship) and things squeak by. Sometimes they
don't and it's impossible to know beforehand if something will work or
not.
Chris Kane
Cocoa Frameworks, Apple
I would be really surprised if this worked. When you set dict[#3]=dict, you are basically giving it an infinite loop to create an infinitely deep dictionary. When you create data from the dictionary, it seems to protect you from that by replacing the infinite dictionary with an uninitialized one so that the user's ram is not completely drained before the application crashes.
If you were to try to print out dict[#3] in the console, the application would infinitely loop trying to print it out and would get stuck until it finally crashes sometime later.
Hence, f you want to store your dictionary in a dictionary, create another one.

Objective C converting NSDictionary valueForKeyPath into a string

I have a NSDictionary that I am trying to get some strings out of. The name of the dictionary is called tweets. I can get the user name and tweet by doing the following.
NSDictionary *tweet = self.detailItem;
NSString *username = [[tweet objectForKey:#"user"] objectForKey:#"name"];
NSString *tweetText = [tweet objectForKey:#"text"];
If I NSLog the results I get; username is "person's username" and tweetText is "person's tweet." These are both NSString that I can manipulate.
However, I also need to get the actual URL from the tweet, not the shortened one. So I have found it in the dictionary with valueForKeyPath. I need this as a string and have tried the following.
NSString *url = [tweet valueForKeyPath:#"entities.urls.display_url"];
I can NSLog this as well and I get the proper information. But I think it comes out as an Array? The NSLog looks like this.. url is ( "url goes here" ). With the brackets and quotes. This is not a string like the others. I am assuming it is because of the valueForKeyPath. When I tried to use url hasPrefix, it doesn't like that and mentions you can't have hasPrefix with and array. But I also tried to use multiple objectForKey with entities, urls, and display_url. But this does not work. Is there any way to get my entry into a string format. I have looked over some SO questions and on the web, but can't find anything that is useful for me. Any help will by much appreciated. Thanks for your time
The keyPath entities.urls.display_url traverse an array of arrays and flattens that to return an array of display_urls. If you were confident that there would be only one url within that hierarchy you could use
NSString *url = [[tweet valueForKeyPath:#"entities.urls.display_url"] lastObject];
If there are multiple entities with URLs you may have to traverse the entities and urls to determine the URL in which you are interested. Or you may be able to use firstObject if what you are really looking for is entities.urls.[0].display_url
Per Twitter's documentation on entities, entities is a dictionary and urls is an array of dictionaries.
When you perform ... valueForKeyPath:#"entities.urls.display_url"] you do the same thing as ... valueForKey:#"entities"] valueForKey:#"urls] valueForKey:#"display_url"].
Per NSDictionary's documentation, its valueForKey: returns the same thing as objectForKey: if — as in this case — the key in question doesn't start with an #.
So the entities part of valueForKeyPath: returns an array of objects.
Per NSArray's documentation, its valueForKey: returns an array comprised of the result of calling valueForKey: on each object in the array individually.
Given that each thing in the entities array is a dictionary, what you therefore get back is an array of the display_url key for every entity in the tweet.
So I think your problem is that you expect to get "the actual URL from the tweet" (emphasis added). Tweets may contain arbitrary many links outward, not merely one — entities represent ranges of characters with special meanings like links, hashtags and similar. So you end up with an array rather than a single value.

Sorting NSArray on two properties - int and date

note from N00B land again. I have read lots about sorting arrays - wanted to try the block method, but haven't wrapped my head around it. Instead, I opted for the descriptors method. I read this Sort NSArray of custom objects by their NSDate properties and this How to sort an NSMutableArray with custom objects in it? amongst oodles and oodles of others. In my code I did this:
NSString *lastHighScore = #"_highScore";
NSString *dateScoreCreated = #"_dateCreated";
NSSortDescriptor *highScoreDescriptor = [[[NSSortDescriptor alloc]
initWithKey:lastHighScore
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSSortDescriptor *dateScoreCreatedDescriptor = [[[NSSortDescriptor alloc]
initWithKey:dateScoreCreated
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray *descriptors = [NSArray arrayWithObjects:highScoreDescriptor,
dateScoreCreatedDescriptor, nil];
NSArray *sortedArray = [[[FlipHighScoreStore sharedStore] allHighScores] sortedArrayUsingDescriptors:descriptors];
Sadly, I am getting an error to begin with - initializer element is not a compile-time element. I looked this up and tried setting NSSortDescriptor *highScoreDescriptor = nil but then I get a warning saying that highScoreDescriptor "Type Specifier Missing, default to int" which in this case is ok, but is not so ok for the Date object in the next descriptor. (Turns out I am also getting an error saying that I am redefining highSoreDescriptor with a different type.)
Also, is there a list somewhere of what selectors are available? I doubt that localizedCaseInsensitiveCompare: is what I want to use since the first property "_highScore" is an int and the second "_dateCreated" is a date. I read somewhere that the default is "compare" so can I just put "compare:"? (Found one answer, I think - I can use (intValue) for the first descriptor:
selector:#selector(intValue)] autorelease];
More reading makes me think that I can do away with the selector line entirely for the date sort. Is that correct?
Lastly, if I say ascending:NO is that the same as descending? I would guess that it is, but one never knows with programming, does one?
Do I wrap all of this code in its own method? Or can I (until later) just plunk it in the code where I am laying out the table?
This project is not ARC.
To answer my own question, with a little help from a friend, I was basically doing two things wrong. First, I was writing the code outside of a method - which is why I was getting all of the errors about initializer elements. I guess I was very tired when I was adding this.
As for the actual sorting, I deleted the selector option from the descriptor description and the sorting actually happened!
Lastly, yes, ascending:NO is equal to descending.
The last bit, will have to wait until I a ready to tackle more refactoring of the application.

Resources