Filling class variables from an NSDictionary in a loop - iphone

I would like to fill in the class variables in a loop from an dictionary. What I want to do is having the dictionary key as a class variable and assign the class variable (the dictionary key) the value from dictionary... something like this:
+(void) initWithDictionary:(NSDictionary *)dic {
MyClass *classInstance = [[[self alloc] init] autorelease];
NSArray *allKeys = [dic allKeys];
for(NSUInteger i = 0; i < [allKeys count]; i++)
{
id classVariable = [allKeys objectAtIndex:i];
classInstance.classVariable = [dic objectForKey:[allKeys objectAtIndex:i]];
}
return classInstance;
}
It does not work, because I do not know how to assign the class variable from the string.
Thanks for answer, I am returning a JSON string that gives me an NSDictionary with keys and values. I am trying to fill this values to my class, let's say DetailObject. I want to use later in the project the DetailObject.id, DetailObject.description, etc. I would like to do it in a loop, becouse now I have to write this:
+ (id) initWithDiccionary :(NSDictionary *)dic//;
{
//Instantiating an object of this class... that's okay.
DetailObject *classInstance = [[[self alloc] init] autorelease];
classInstance.id = [dic objectForKey#"id"];
classInstance.desc = [dic objectForKey#"desc"];
etc... etc...
return classInstance;
}
What I want is to parse the dictionary from JSON to my object and respective variables and values that comes from dictionary in a loop, because if the JSON dictionary changes, I just add the new class variable with the same name of the returned dictionary key...
I do not know if I have explained it well...

Your question is very very unclear and I have no idea what you're trying to do or why. But just looking at your code I can tell you already that it's definitely not doing what you want.
//There should be no semicolon after "dic" below.
//Also, you should be returning a MyClass or an id.
- (id) initWithDiccionary :(NSDictionary *)dic//;
{
//Instantiating an object of this class... that's okay.
MyClass *classInstance = [[[self alloc] init] autorelease];
//Getting all the keys from the dictionary, seems fine...
NSArray *allKeys = [dic allKeys];
//Looping through all the keys in the dictionary, seems okay...
for(NSUInteger i = 0; i < [allKeys count]; i++)
{
//Storing the current key.
id classVariable = [allKeys objectAtIndex:i];
//Assigning the class's property "classVariable" to match the current key's value.
//No reason to say "[allKeys objectAtIndex:i]" again, though.
classInstance.classVariable = [dic objectForKey:classVariable];
}
//Returning something when you have no return type above (void) is wrong.
return classInstance;
}
Your code will just assign classInstance.classVariable to be equal to [allKeys objectAtIndex:[allKeys count]-1]. Your loop is pointless.
After I actually annotated your code though I think I have some idea of what you want. Basically you want to assign the variables with names matching the keys in the dictionary the values in the dictionary. i.e. if there is a key called "superKey" then you want to find the variable within classInstance (classInstance.superKey) and assign it the value in the dictionary that matches superKey. That's what you want, right?
Well, the only way I know of to do that is to use a big switch statement or a bunch of if statements. Make some function within MyClass like this:
- (void) assignProperty:(id)property toValue:(id)value
{
if (property == #"superKey")
{
self.superKey = value;
}
else if (property == #"lameKey")
{
self.lameKey = value;
}
//etc.
}
Then you just call [classInstance assignProperty:classVariable toValue:[doc objectForKey:classVariable]] and the job will be done.
But having told you all that...
Why would you ever want to do what you're doing? Want to know a much better way of doing this? Give MyClass its own NSDictionary. Basically all you are doing is defeating the entire purpose of the dictionary. Why? They are incredibly fast to access and can store whatever you want. There is no reason not to use one. So just do this:
- (id) initWithDiccionary :(NSDictionary *)dic
{
MyClass *classInstance = [[[self alloc] init] autorelease];
classInstance.dictionary = dic;
return classInstance;
}
Voila.

Enter Key-Value Coding. The following is an example of how you could achieve your desired outcome:
#interface MyClass : NSObject
#property (nonatomic, retain) NSString *aString;
#property (nonatomic, retain) NSNumber *aNumber;
#property (nonatomic, retain) NSString *yetAnother;
- (id)initWithDictionary:(NSDictionary *)dictionary;
#end
#implementation MyClass
#synthesize aString;
#synthesize aNumber;
#synthesize yetAnother;
- (id)initWithDictionary:(NSDictionary *)dictionary {
if ((self = [super init])) {
[self setValuesForKeysWithDictionary:dictionary];
}
return self;
}
// dealloc is left as an exercise for the reader
#end
You could use this class as follows:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"my string", #"aString",
[NSNumber numberWithInt:42], #"aNumber",
#"strings!", #"yetAnother", nil];
MyClass *myClass = [[MyClass alloc] initWithDictionary:dictionary] autorelease];
// yay!
You can thank Objective-C's dynamism for that. :)

Related

Populating NSDictionary and NSArrays for Model data

I'm trying to create an NSDictionary full of arrays in the implementation file of my model but my code hasn't worked yet. I want to create arrays that are lists of types of dogs and cats and then add those arrays to a dictionary with keys called DOG and CAT. Here is my code:
#implementation wordDictionary
#synthesize catList = _catList;
#synthesize dogList = _dogList;
#synthesize standardDictionary =_standardDictionary;
- (void)setCatList:(NSMutableArray *)catList
{
self.catList = [NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil];
}
- (void)setDogList:(NSMutableArray *)dogList
{
self.dogList = [NSMutableArray arrayWithObjects:#"pit bull", #"pug", #"chihuahua", nil];
}
-(void)setStandardDictionary:(NSMutableDictionary *)standardDictionary
{
[self.standardDictionary setObject: _catList forKey:#"CAT"];
[self.standardDictionary setObject: _dogList forKey:#"DOG"];
}
- (NSString*)selectKey
{
NSInteger keyCount = [[self.standardDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[self.standardDictionary allKeys] objectAtIndex:randomKeyIndex];
return randomKey;
}
#end
This code is the model. The model is hooked up to my view controller such that when a user taps a button, the NSString returned from randomKey is displayed in a label on the screen. So the text will read either CAT or DOG. Here's the code for that:
- (IBAction)changeGreeting:(UIButton*)sender {
NSString *chosenKey = [self.dictionary selectKey];
NSString *labelText = [[NSString alloc] initWithFormat:#"%#", chosenKey];
self.label.text = labelText;
}
Unfortunately when I tap the button on the simulator I get an error message saying: Thread 1:EXC_ARITHMETIC (code=EXC_1386_DIV, subcode=0x0) at NSInteger randomKeyIndex = arc4random() % keyCount; and it appears that I'm getting it because neither my NSArray nor my NSDictionary have any objects inside of them.
Does anyone have any idea why my NSArray and NSDictionary haven't been populated?
Thanks very much.
The simple answer is that there isn't any code here that calls the methods to set the arrays or dictionary.
But the real underlying issue is that there are a couple of bad 'patterns' going on here that you should fix:
In your setter methods (setCatList:, setDogList:, setStandardDictionary:) you're not setting the properties in question to the values that are passed in. For example, you should be setting catList to the passed in "catList" variable.
- (void)setCatList:(NSMutableArray *)catList
{
if (_catList != catList) {
[_catList release];
_catList = [catList retain];
}
}
Then you should have some kind of "setup" happening, usually in a method in the view controller like viewDidLoad:
[wordDictionary setCatList:[NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil]];
// and more for the other two setters
Alternately, you can set these default values in the init for the wordDictionary class:
- (id)init {
self = [super init];
if (self) {
[self setCatList:[NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil]];
}
return self;
}
The former is better in most cases, but you may have a good reason to pre-populate your model for all instances of the class.
Assuming you called setCatList:, setDogList: and setStandardDictionary: before. Probably that causing is this :
NSString *chosenKey = [self.dictionary selectKey];
change into this :
NSString *chosenKey = [self selectKey];
UPDATE
I'm trying to make your life easier. no need to create your object if you don't need the most.
- (NSMutableArray*)getCatList
{
return [NSMutableArray arrayWithObjects:#"lion", #"puma", #"snow leopard", nil];
}
- (NSMutableArray*)getDogList
{
return [NSMutableArray arrayWithObjects:#"pit bull", #"pug", #"chihuahua", nil];
}
-(NSMutableDictionary*)getStandardDictionary
{
NSMutableDictionary *standardDictionary = [NSMutableDictionary new];
[standardDictionary setObject:[self getCatList] forKey:#"CAT"];
[standardDictionary setObject:[self getDogList] forKey:#"DOG"];
return [standardDictionary autorelease];
}
- (NSString*)selectKey
{
NSMutableDictionary *standardDictionary = [self getStandardDictionary];
NSInteger keyCount = [[standardDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[standardDictionary allKeys] objectAtIndex:randomKeyIndex];
return randomKey;
}
- (IBAction)changeGreeting:(UIButton*)sender {
// NSString *chosenKey = [self selectKey];
//NSString *labelText = [[NSString alloc] initWithFormat:#"%#", chosenKey];
self.label.text = [self selectKey]; //no need to convert it to NSString again
}
Two things to consider:
I don't see you calling these:
setCatList:(NSMutableArray*)catList;
setDogList:(NSMutableArray*)dogList;
You use self.catList and self.dogList, but neither of those are synthesized, instead you have beatList and meList synthesized
Change the synthesizes to the catList and dogList, and make sure you call the set list methods, and then you should make some progress.

objective c how to create dictionaries dynamically and refer to them

i need to create and destroy dynamically dictionaries, or arrays,
and have them as instance variables,
so for example, [pseudocode]
*.h
nsmutableDictionary myDictn???
nsstring arrayn ???
how to create an instance dictionarie, and property, that dinamically get created and destroyed?, and how to refer to it?
*.m
n = 0
create container {
myDictn alloc init
n+1
}
other {
myDictn addobject#"data" forKey"myKey"
}
destroy container {
myDictn release
n-1
}
So what intend to show is that i would like to have myDict1, myDict2...
if created,
or destroy them if needed
thanks a lot!
To create dictionaries dynamically & add entries to them you could do this -
NSMutableDictionary *dictResult = [[[NSMutableDictionary alloc] init] retain];
[dictResult setValue:result forKey:#"key"];
Here result can be anything. NSString or NSArray etc. Also using retain retains this object & causes a memory leak if not explicitly released. Instead try to do autorelease that way ios takes care of releasing the object when its no longer referred to. You do that like so -
NSMutableDictionary *dictResult = [[[NSMutableDictionary alloc] init] autorelease];
This is all you need to create dictionaries dynamically.
I think what you're asking for is how to have multiple mutable dictionaries dynamically created. You haven't said where the numbering scheme is coming from, so you may need to modify this solution for your purposes.
What you want is an array or dictionary of dictionaries.
Make one NSMutableDictionary called something like dictionaryContainer. Then, when you want to create dictionary number 7, do
NSMutableDictionary *aDictionary = [NSMutableDictionary new];
[dictionaryContainer setObject:aDictionary forKey:[NSNumber numberWithInt:7]];
To recall that dictionary, do
NSMutableDictionary *theSameDictionary = [dictionaryContainer objectForKey:[NSNumber numberWithInt:7]];
You don't have to hard code the 7, you can get it from anywhere and pass it in as an integer variable.
If I got your question correctly, this is pretty easy
#interface MyClass {
NSMutableDictionary *dict;
NSMutableArray *arr;
}
#property (nonatomic, retain) NSMutableDictionary *dict;
#property (nonatomic, retain) NSMutableArray *arr;
#end
Implementation file
#import "MyClass.h"
#implementation MyClass
#synthesize dict;
#synthesize arr;
- (id) init {
self = [super init];
if (self) {
dict = [[NSMutableDictionary alloc] init];
arr = [[NSMutableArray alloc] init];
}
return self;
}
- (void) dealloc {
[dict release];
[arr release];
[super dealloc];
}
- (void) otherStuff {
[dict setObject: #"value" forKey: #"key"];
[arr addObject: #"item"];
}
#end
usage from another class:
...
MyClass *instance = [MyClass new];
[instance.dict setObject: #"value" forKey: #"key"];
NSLog(#"Array items: %#", instance.arr);
[instance release];
...

how do I build up a global like integer array?

How do I build up a global like integer array?
I tried variations of the following:
#interface
int *iArray; //this space will vary depending upon need in the implementation
#implementation
...
int iArrayInit[4] = {1,2,3,4};
iArray = iArrayInit;
-bottom line: I need to keep index values in array that I can access easily, and use of [NSArray intValue] maybe to slow.
thanks
If it needs to be static you can declare an NSMutableArray as static in the implementation file and expose static methods to access it. When using an NSArray the values need to be of type id which NSNumber can do. Here is an example which currently is not thread safe.
//.h file
#interface Foo : NSObject
{
}
+(NSArray*)iArray;
+(void)addiArrayValue:(NSNumber*)value;
#end
//.m file
#implementation Foo
static NSMutableArray *_iArray;
+(void)initialize
{
if([Foo class] == self)
{
_iArray = [[NSMutableArray alloc] init];
}
}
+(NSArray*)iArray
{
return [[_iArray copy] autorelease];
}
+(void)addiArrayValue:(NSNumber*)value
{
[_iArray addObject:value];
}
#end
//Use
[Foo addiArrayValue:[NSNumber numberWithInt:10]];
[Foo addiArrayValue:[NSNumber numberWithInt:12]];
NSLog(#"%#", [Foo iArray]);

Deep copy of NSMutableDictionary

How can one deep-copy the contents of an NSMutableDictionary to another NSMutableDictionary?
I do not want to use initWithDictionary:copyItems: method as my dictionaries are already initialized.
This might work:
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary
This copies the Keys, But not the values, of an NSDictionary. Of course, If you need a deep copy, take a look at this:
#interface NSDictionary(rross)
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL) copyItems;
#end
#implementation NSDictionary(rross)
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL) copyItems
{
if (copyItems)
{
// get the keys
NSArray *oldKeys = [otherDictionary allKeys];
for (int i = 0; i < oldKeys.count; i++)
{
// add all the new key / value pairs
id key = [oldKeys objectAtIndex:i];
[self setObject:[[[otherDictionary objectForKey:key] copy] autorelease] forKey:[[key copy] autorelease]];
}
}
else
{
[self addEntriesFromOtherDictionary:otherDictionary];
}
}
#end

Sorting NSDictionary with multiple constraints

I have a NSDictionary collection whose key is a unique id and value is an array with two different objects (FruitClass, ProductClass) and I would like to group the collection such that it's sorted first by ProductClass.productName and then by FruitClass.itemName.
So the final list would look something like:
{apple, butter}
{apple, pie}
{banana, daiquiri}
{banana, smoothie}
{melon, zinger}
where the first item is a FruitClass instance item and second is a ProductClass instance item.
What's the best way to go about doing this? Most of the examples I've come across are done on one key. How do you do it with an NSDictionary that has 2 different object types?
Looking at NSDictionary's's keysSortedByValueUsingSelector,
- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator
I get the impression that you would create the 'compare' method on the class type of the value object. So for multiple field sort, would I have to resort to creating a new object type, 'CombinedClass' which contains FruitClass & ProductClass and implement a 'compare' to make this happen?
FruitClass:
{
NSString *itemName;
}
#end
#interface ProductClass
{
NSString *productName;
}
#end
If there is a data structure that consists of only one fruit and only one product then an array is not really a good option. You can use another class and provide a compare: comparator:
#interface ComboClass : NSObject
{
FruitClass *fruit;
ProductClass *product;
}
#property(nonatomic,retain) FruitClass *fruit;
#property(nonatomic,retain) ProductClass *product;
- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p;
#end
#implementation ComboClass
#synthesize fruit;
#synthesize product;
- (void) dealloc
{
[fruit release];
[product release];
[super dealloc];
}
- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p
{
self = [super init];
if (!self) return nil;
self.fruit = f; // some recommend against accessor usage in -init methods
self.product = p;
return self;
}
- (NSComparisonResult) compare:(id) another
{
NSComparisonResult result = [self.fruit.itemName compare:another.fruit.itemName];
if (result == NSOrderedSame)
return [self.product.productName compare:another.product.productName];
else
return result;
}
#end
Alternatively, you might be able to use an NSDictionary with product and fruit key-value pairs, (so you'll end up with dictionaries inside a dictionary). The NSSortDescriptor class can be used to sort arrays using values of key-paths, so it might be another option to explore.
You could add a category to NSArray that would do the comparison and you wouldn't have to create another class.
#interface NSArray ( MySortCategory )
- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
#end
The implementation should be pretty straightforward based on your description.
Edit I got a little miffed that this was marked down with no comment, so I did a full implementation to make sure it would work. This is a little different than your sample, but the same idea.
FruitsAndProducts.h
#interface Fruit : NSObject
{
NSString *itemName;
}
#property(nonatomic, copy)NSString *itemName;
#end
#interface Product : NSObject
{
NSString *productName;
}
#property(nonatomic, copy)NSString *productName;
#end
FruitsAndProducts.m
#import "FruitsAndProducts.h"
#implementation Fruit
#synthesize itemName;
#end
#implementation Product
#synthesize productName;
#end
NSArray+MyCustomSort.h
#interface NSArray (MyCustomSort)
- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
#end
NSArray+MyCustomSort.m
#import "NSArray+MyCustomSort.h"
#import "FruitsAndProducts.h"
#implementation NSArray (MyCustomSort)
- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare
{
// This sorts by product first, then fruit.
Product *myProduct = [self objectAtIndex:0];
Product *productToCompare = [arrayToCompare objectAtIndex:0];
NSComparisonResult result = [myProduct.productName caseInsensitiveCompare:productToCompare.productName];
if (result != NSOrderedSame) {
return result;
}
Fruit *myFruit = [self objectAtIndex:1];
Fruit *fruitToCompare = [arrayToCompare objectAtIndex:1];
return [myFruit.itemName caseInsensitiveCompare:fruitToCompare.itemName];
}
#end
Here it is in action
// Create some fruit.
Fruit *apple = [[[Fruit alloc] init] autorelease];
apple.itemName = #"apple";
Fruit *banana = [[[Fruit alloc] init] autorelease];
banana.itemName = #"banana";
Fruit *melon = [[[Fruit alloc] init] autorelease];
melon.itemName = #"melon";
// Create some products
Product *butter = [[[Product alloc] init] autorelease];
butter.productName = #"butter";
Product *pie = [[[Product alloc] init] autorelease];
pie.productName = #"pie";
Product *zinger = [[[Product alloc] init] autorelease];
zinger.productName = #"zinger";
// create the dictionary. The array has the product first, then the fruit.
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObjects:zinger, banana, nil], #"zinger banana", [NSArray arrayWithObjects:butter, apple, nil], #"butter apple", [NSArray arrayWithObjects:pie, melon, nil], #"pie melon", nil];
NSArray *sortedKeys = [myDict keysSortedByValueUsingSelector:#selector(customCompareToArray:)];
for (id key in sortedKeys) {
NSLog(#"key: %#", key);
}
Your comparator can work on whatever you throw at it... you can make it treat its two arguments as NSArray objects, if this is what you need. When you put arrays as values into your dictionary, then just use those - no need for another class.
If you want to build a new class anyway (maybe for design reasons) - go for it, but it is not a "must do" here.
Edit: strike out to make clear that only one argument is given - as the other one is the object the selector is called on. Using NSArray will need a class extension, a custom class is much cleaner.