简体   繁体   中英

How to read a plist and create different arrays from its content in Xcode

I've got a .plist like this:

plist的屏幕截图
Click image for full resolution

How can I create different arrays for each subpart of items. like NSArray foodName with contents of item 0's item 1's to item n's foodName

There are lots of items.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                    NSUserDomainMask, YES); 
self.pFile = [[paths objectAtIndex:0]
                    stringByAppendingPathComponent:@"recipes.plist"];

self.plist = [[NSMutableDictionary alloc] initWithContentsOfFile:pFile];
if (!plist) {
    self.plist = [NSMutableDictionary new];
    [plist writeToFile:pFile atomically:YES];
}

An NSDictionary is probably what you want. Then do a 'for' on it for each recipe / recipeDetail / wherever you're at in the structure?

You don't need to create "different arrays".

A plist is a textual (or binary) representation of a collection of objects. The valid object kinds come from a small set which includes NSArray , NSDictionary , NSNumber & NSString . The collection is rooted either in a dictionary or an array, each element of which can be any of the valid object kinds, including further dictionaries and arrays.

When you read the plist in your application the collection of objects is re-created. So if there are nested arrays or dictionaries they are recreated. To access them you just need to use the appropriate sequence of indices (arrays) and/or keys (dictionaries) which specify the element you need. You can store the returned object reference into a variable.

So for example if your plist is a dictionary keyed by vegetable names, each element of which is an array, and the third element of that array is another array of observed weights of that vegetable then you can access that array as follows (code just typed into answer, expect errors):

NSDictionary *vegetableInfo = [NSDictionary dictionaryWithContentsofURL:urlOfVegtableInfoPlist"];
NSArray *carrrotObservedWeights = vegetableInfo[@"Carrot"][3];

You now have a reference to the required array stored in carrrotObservedWeights .

If you are concerned over memory management, first use ARC. Second if you just want the extracted arrays and not the whole plist to be kept you just need to drop the reference to the plist after you've stored strong references to the contained arrays and ARC will clean up for you.

HTH

Addendum - After question clarified

First your plist is a dictionary of dictionaries where the containing dictionary has the keys Item 1 , Item 2 , etc. These keys carry no information and you would be better off making your plist an array of dictionaries.

Next we assume you have read in your plist using one of the standard methods and have a reference to it in sample - which is either an NSDictionary * if your plist is as shown, or an NSArray * if you modify the plist as suggested.

How many ways to do this? Many, here are three.

Method 1 - Simple Iteration

The straightforward way to obtain your arrays is simple iteration - iterate over each item and build your arrays. Here is a code fragment for two of the fields assuming the original dictionary of dictionaries:

NSMutableArray *foodNames = [NSMutableArray new];
NSMutableArray *hardnesses = [NSMutableArray new];

for (NSString *itemKey in sample)             // for-in on a dictionary returns the keys
{
   NSDictionary *item = sample[itemKey];      // use the key to obtain the contained dictionary
   [foodNames addObject:item[@"foodName"]];   // extract each item
   [hardnesses addObject:item[@"hardness"]];
}

The above fragment if sample is an array is similar.

Method 2 - Keypaths

If you do switch your plist to an array you can use a keypath to obtain all the values in one go. The method valueforkeypath: creates an array from an array by extracting the keypath - a list of keys separated by dots which allows for dictionaries within dictionaries. In your case the keypath has just one item:

NSMutableArray *foodNames = [sample valueForKeyPath:@"foodName"];
NSMutableArray *hardnesses = [sample valueForKeyPath:@"hardness"];

This will not work for your plist as shown with a top-level dictionary as they keypath is different each time - Item 1.foodName , Item 2.foodName , etc. - and wildcards (eg *.foodName ) are not supported. A good reason to change your plist to have a top-level array!

Method 3 - Encapsulated Iteration

This is just a variation of method 1, but shows how to use the supplied block enumeration methods on NSDictionary . Rather than write a for loop yourself you can pass the body of the loop as a block to enumerateKeysAndObjectsUsingBlock: and it will perform the iteration:

NSMutableArray *foodNames = [NSMutableArray new];
NSMutableArray *hardnesses = [NSMutableArray new];

[sample enumerateKeysAndObjectsUsingBlock:^(id key, id item, BOOL *stop)
 {
   [foodNames addObject:item[@"foodName"]];
   [hardnesses addObject:item[@"hardness"]];
 }];

I'd make the root of the plist an array, but still have each item as a dictionary, so i could do something like this:

//ASSUMING YOUR PLIST IS IN RESOURCES FOLDER
NSString *filepath = [[NSBundle mainBundle] pathForResource:@"YOUR_PLIST_FILE_NAME" ofType:@"plist"];
NSArray *foodArray = [NSArray arrayWithContentsOfFile:filepath];

NSMutableArray *foodNames = [[NSMutable Array] init];
NSInteger i = 0;

for (Item *item in foodArray) {
    NSDictionary *itemDict = [foodArray objectAtIndex:i];
    NSString *foodName = [itemDict objectForKey:@"foodName"];
    [foodNames addObject:foodName];

    i++;
} 

Hope this helps you figure something out!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM