I've got a simple object "post" that has two NSMutableArrays as properties. One is for "image" objects and the other is for "video" objects. At some point in the lifecycle of "post", I ask it for a dictionary representation of itself.
NSMutableDictionary *postDict = [post getDictionary];
-(NSMutableDictionary *)getDictionary{
NSMutableArray *imgDictArry = [NSMutableArray arrayWithObjects:nil];
NSMutableArray *movDictArry = [NSMutableArray arrayWithObjects:nil];
for (int i = 0; i<self.images.count; i++) {
NSMutableDictionary *imgDict = [[self.images objectAtIndex:i] getDictionary];
[imgDictArry addObject:imgDict];
}
for (int i = 0; i<self.videos.count; i++) {
NSMutableDictionary *movDict = [[self.videos objectAtIndex:i] getDictionary];
[movDictArry addObject:movDict];
}
NSMutableDictionary *postDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:self.friendsOnly], #"IsFriendsOnly",
self.message, #"Message",
self.shortText, #"ShortText",
self.authorId, #"AuthorId",
self.recipientId, #"RecipientId",
self.language, #"Language",
self.lat, #"Lat",
self.lng, #"Lng",
imgDictArry, #"Images",
movDictArry, #"Videos",
nil];
return postDict;
}
As you can see, the "image" and "video" objects have their own methods for describing themselves as NSMutableDictionary objects.
-(NSMutableDictionary *)getDictionary{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
self.nativeURL, #"NativeURL",
self.previewURL, #"PreviewURL",
self.smallURL, #"SmallURL",
self.thumbURL, #"ThumbURL",
self.imageId, #"ImageId",
self.width, #"Width",
self.height, #"Height",
nil];
}
I'm not getting any errors but my imgDictArry and movDictArry objects are turning out to be NULL after I've set them on the postDict object. If I log them to the console just before this moment, I can see the dictionary data. But the other classes requesting this object is getting null for those properties.
Perhaps one of your functions such as self.shortText (or self.lat...) is returning nil, in which case dictionaryWithObjectsAndKeys isn't what you expect it to be: it's truncated to the first function that returns nil...
I changed the postDict creation to this...
NSMutableDictionary *postDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:self.friendsOnly], #"IsFriendsOnly",
self.message, #"Message",
self.shortText, #"ShortText",
self.authorId, #"AuthorId",
self.recipientId, #"RecipientId",
self.language, #"Language",
self.lat, #"Lat",
self.lng, #"Lng",
nil];
[postDict setObject:imgDictArry forKey:#"Images"];
[postDict setObject:movDictArry forKey:#"Videos"];
and it's fixed. But I don't know why.
Related
As I have a requirement to add similar objects into the array, I have created new dictionary in such a way.
NSMutableDictionary* existingStepDict = [[[arrayForSteps objectAtIndex:0] mutableCopy] autorelease];
[arrayForSteps addObject:existingStepDict];
[existingStepDict release];
Now, what happens here is that later when I change something in any one of the dictionary, the other one also gets updated. I require both these dictionaries to behave independently.
For that I went through Deep-copy of dictionaries whose code is like this.
NSMutableDictionary* existingStepDict = [[[arrayForSteps objectAtIndex:0] mutableCopy] autorelease];
NSMutableDictionary* destination = [NSMutableDictionary dictionaryWithCapacity:0];
NSDictionary *deepCopy = [[NSDictionary alloc] initWithDictionary:existingStepDict copyItems: YES];
if (deepCopy) {
[destination addEntriesFromDictionary: deepCopy];
[deepCopy release];
}
//add Properties array to Steps Dictionary
[arrayForSteps addObject:destination];
But this too didn't reflect the difference. I know I am making some minor mistake here.
But could some one help me getting my result?
Thanks a lot!
There's an easy way to get a full deepcopy of an NSDictionary o NSArray using the NSCoding (serialization) protocol.
- (id) deepCopy:(id)mutableObject
{
NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject:mutableObject];
return [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
}
In this way you can duplicate any object plus all the obects it contains in a single step.
when I need a mutable deep copy of a NSDictionary I create a Category with this method:
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys) {
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if ([oneValue respondsToSelector:#selector(mutableCopy)]) {
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) {
oneCopy = [oneValue copy];
}
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
EDIT
and searching the web I found this, I haven't tested
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
for(Attribute* attribute in appDelegate.attributeArray) {
attribute = [appDelegate.attributeArray objectAtIndex:z];
attri = attribute.zName;
int y = 0;
for(Row* r in appDelegate.elementsArray) {
r = [appDelegate.elementsArray objectAtIndex:y];
NSString *ele = r.language;
if([attri isEqualToString:ele]) {
NSLog(#"=================+++++++++++++++++%# %#",attri, r.user);
[aaa insertObject:r atIndex:y]; //here i am adding the value to array
[dict setObject:aaa forKey:attri]; //here i am adding the array to dictionary
}
y++;
}
z++;
NSLog(#"============$$$$$$$$$$$$$$$$$$$$$$$++++++++++ %#",dict);
}
key in one array and the value in the another array and the value array is in object format.
I need to store the multi object for the single key. The attributeArray has the key value and the elementsArray has the object. For example the attributeArray might have the values
" English, French, German..."
and the elementsArray might have the object value
"<Row: 0x4b29d40>, <Row: 0x4b497a0>, <Row: 0x4e38940>, <Row: 0x4b2a070>, <Row: 0x4b29ab0>, <Row: 0x4b178a0> "
In the first value I need to store the two object and for second key I need to store 3 objects and for the third key in need to store last two objects in the dictionary.
For super-simplification you can use the following code:
NSArray *keys = ...;
NSArray *values = ...;
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects: values forKeys: keys];
Hope, this helps.
UPDATE:
to store multiple values for single key in the dictionary, just use NSArray / NSMutableArray as your object:
NSArray *keys = ...;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for( id theKey in keys)
{
NSMutableArray *item = [NSMutableArray array];
[item addObject: ...];
[item addObject: ...];
...
[dict setObject: item forKey: theKey];
}
If you don't know all the values for the key from the beginning and need to add them one by one, you can use the following approach:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for( /*some cycling condition here */)
{
id keyToUse = ...;
id valueToAdd = ...;
id foundArray = [dict objectForKey: keyToUse];
if ( nil == foundArray )
{
foundArray = [NSMutableArray array];
[dict setObject: foundArray forKey: keyToUse];
}
[foundArray addObject: valueToAdd];
}
To me it looks you are settings an array (aaa) with string (attri) as a key.
To set an array as a key for another array as an object. You can do this with the following appraoch.
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
NSArray *valueArray = [NSArray arrayWithObjects:#"v1", #"v2", nil];
NSArray *keyArray = [NSArray arrayWithObjects:#"k1",#"k2", nil];
[myDictionary setObject:valueArray forKey:keyArray];
NSLog(#"myDictionary: %#", myDictionary);
But you should review your code and provide a more brief explanation that what do you want to achieve and how are you taking an approach for this.
regards,
Arslan
Edit: better example...
So I have an NSMutableSet of "existing" objects and I'd like to download JSON data, parse it, and merge these new objects with my existing ones, updating any duplicates with the newly downloaded ones. Here's what the existing set of objects looks like:
NSArray *savedObjects = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"id", #"hello there", #"body", #"200", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"2", #"id", #"hey now", #"body", #"10", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"3", #"id", #"welcome!", #"body", #"123", #"score", nil],
nil
];
self.objects = [NSMutableSet setWithArray:savedObjects];
// after downloading and parsing JSON... I have an example array of objects like this:
NSArray *newObjects = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"id", #"hello there", #"body", #"9999", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"4", #"id", #"what's new", #"body", #"22", #"score", nil],
nil
];
So for example, after merging this new object, the object with id 1 would now have a score of 9999 and a new object with id 4 would be added to the set.
I really want to avoid looping through the NSMutableSet for every new object just to check that the #"id" property exists... I was thinking I could use addObjectsFromArray to merge these new objects but it looks like since the properties differ (eg: score: 9999) it's not seeing the new objects as an existing object in the set.
I'm using the numbers as strings to simplify this example. I'd also like to avoid using iOS SDK 4.0-only features since the app will be 3.0-compatible.
Thanks a ton! I appreciate it!
Not entirely sure from your question (since its an iphone question, objective c notation should have been used), but it sounds like an NSDictionary could be your best friend.
Your best bet is to convert the first set into a dictionary using the #"id" parameter. Here's some code that should do what you want, and it will even handle the case of having dictionaries that have no #"id" parameter (at which point they won't be merged, just included in the result):
// here is the NSSet you already have
self.objects = [NSSet setWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"id", #"hello there", #"body", #"200", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"2", #"id", #"hey now", #"body", #"10", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"3", #"id", #"welcome!", #"body", #"123", #"score", nil],
nil];
// after downloading and parsing JSON... I have an example array of objects like this:
NSArray *newObjects = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"id", #"hello there", #"body", #"9999", #"score", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"4", #"id", #"what's new", #"body", #"22", #"score", nil],
nil];
// Convert self.objects into a dictionary for merging purposes
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[self.objects count]];
NSMutableSet *remainder = [NSMutableSet set]; // for objects with no #"id"
for (NSDictionary *obj in self.objects) {
NSString *objId = [obj objectForKey:#"id"];
if (objId) {
[dict setObject:obj forKey:objId];
} else {
[remainder addObject:obj];
}
}
// merge the new objects in
for (NSDictionary *obj in newObjects) {
NSString *objId = [obj objectForKey:#"id"];
if (objId) {
// merge the new dict with the old
// if you don't want to merge the dict and just replace,
// simply comment out the next few lines
NSDictionary *oldObj = [dict objectForKey:objId];
if (oldObj) {
NSMutableDictionary *newObj = [NSMutableDictionary dictionaryWithDictionary:oldObj];
[newObj addEntriesFromDictionary:obj];
obj = newObj;
}
// stop commenting here if you don't want the merging
[dict setObject:newObj forKey:objId];
} else {
[remainder addObject:obj];
}
}
// add the merged dicts back to the set
[remainder addObjectsFromArray:[dict allValues]];
self.objects = remainder;
NSArray *array = ... // contains the new dictionary objects.
NSMutableSet *set = ... // contains the existing set of dictionary objects.
for (id arrayItem in array)
{
id setItem = [[set filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"id.intValue = %d", [[arrayItem valueForKey:#"id"] intValue]]] anyObject];
if (setItem != nil)
{
[set removeObject:setItem];
[set addObject:arrayItem];
}
}
What is the datatype you use to fetch items whose type is dictionary in plist i.e. nsmutabledictionary or nsdictionary? Because I'm using following code to retrieve dictionary objects from an array of dictionaries in plist.
NSMutableDictionary *_myDict = [contentArray objectAtIndex:0]; //APP CRASHES HERE
NSLog(#"MYDICT : %#",_myDict);
NSString *myKey = (NSString *)[_myDict valueForKey:#"Contents"] ;
[[cell lblFeed] setText:[NSString stringWithFormat:#"%#",myKey]];
Here, on first line it's showing me objc_msgsend. ContentArray is an nsarray and it's contents are showing 2 objects that are there in plist. In plist they are dictionary objects. Then why this error?
Edit
Basically, the contents of my contentArray in console are as shown below :
CONTENT ARRAY :
(
{
favourites = 0;
id = 0;
story = "This is my first record";
timestamp = 324567;
},
{
favourites = 0;
id = 1;
story = "This is my second record";
timestamp = 321456;
}
)
I want to retrieve these dictionary objects from content array.
NSDictionary. You can't simply say
NSMutableDictionary *_myDict = [contentArray objectAtIndex:0];
and hope, that it's a mutable dictionary now. It's still a normal immutable distionary. So, you should write something like:
NSMutableDictionary *_myDict = [NSMutableDictionary dictionaryWithDictionary:[contentArray objectAtIndex:0]];
That'll create mutable dictionary from one that is in the plist.
You can read about it in the "Property List Programming Guide", http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/PropertyLists/index.html
Update:
Also you have a strange plist contents. Available xml-plist types are mentioned here:
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html#//apple_ref/doc/uid/10000048i-CH3-SW1
And overall xml-plist structure is described here:
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/PropertyLists/UnderstandXMLPlist/UnderstandXMLPlist.html#//apple_ref/doc/uid/10000048i-CH6-SW1
Working piece of code
void test() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *arrayIWillWrite = [NSMutableArray array];
NSMutableDictionary *dictionary;
dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:0] forKey:#"favourites"];
[dictionary setObject:[NSNumber numberWithInt:0] forKey:#"id"];
[dictionary setObject:#"This is my first record" forKey:#"story"];
[dictionary setObject:[NSNumber numberWithInt:324567] forKey:#"timestamp"];
[arrayIWillWrite addObject:dictionary];
dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:0] forKey:#"favourites"];
[dictionary setObject:[NSNumber numberWithInt:1] forKey:#"id"];
[dictionary setObject:#"This is my second record" forKey:#"story"];
[dictionary setObject:[NSNumber numberWithInt:321456] forKey:#"timestamp"];
[arrayIWillWrite addObject:dictionary];
[arrayIWillWrite writeToFile:#"/Users/alex/test.plist" atomically:NO];
NSArray *arrayThatWasRead = [NSArray arrayWithContentsOfFile:#"/Users/alex/test.plist"];
NSLog(#"%#", arrayThatWasRead);
NSDictionary *dictionaryFromArrayThatWasRead = [arrayThatWasRead objectAtIndex:0];
NSLog(#"%#", dictionaryFromArrayThatWasRead);
[pool release];
}
Being new to Cocoa, and probably not knowing all of the potential classes available that already have this functionality neatly wrapped in an OO class, here's an algorithm inquiry. What's the best bet to count how many times a particular key occurs in an array of multiple NSDictionary instances?
Essentially my data structure (in this case an NSArray) might contain several NSDictionary instances at any given time, each one having the same keys, potentially different values. Some values repeat. I'd like to be able to know how many times a particular key/value appears. Example:
{
foo => 1,
bar => 2
}
{
foo => 1,
bar => 3
}
{
foo => 2,
bar => 1
}
In this case I'm interested that foo=>1 occured 2 times and foo=>2 occured 1 time. Is building an instance of NSCountedSet the best way to go about this? Perhaps a C linked-list?
You may want to rethink how you are structuring your data. I'd track something like this while adding to the NSArray instead of trying to discover it at a later time. You might create a new class to handle adding and removing the data so that you can keep your own counts of the data.
NSDictionary * dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:2], #"bar", nil];
NSDictionary * dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:3], #"bar", nil];
NSDictionary * dict3 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:2], #"foo",
[NSNumber numberWithInt:1], #"bar", nil];
NSArray * arrayOfDictionaries = [[NSArray alloc] initWithObjects:
dict1, dict2, dict3, nil];
// count all keys in an array of dictionaries (arrayOfDictionaries):
NSMutableDictionary * countKeys = [[NSMutableDictionary alloc] initWithCapacity:0];
NSCountedSet * counts = [[NSCountedSet alloc] initWithCapacity:0];
NSArray * keys;
NSString * pairString;
NSString * countKey;
for (NSDictionary * dictionary in arrayOfDictionaries)
{
keys = [dictionary allKeys];
for (NSString * key in keys)
{
pairString = [NSString stringWithFormat:#"%#->%#", key, [dictionary valueForKey:key]];
if ([countKeys valueForKey:pairString] == nil)
{
[countKeys setValue:[NSString stringWithString:pairString] forKey:pairString];
}
countKey = [countKeys valueForKey:pairString];
{ [counts addObject:countKey]; }
}
}
NSLog(#"%#", counts);
[counts release];
[countKeys release];
[arrayOfDictionaries release];
[dict1 release];
[dict2 release];
[dict3 release];
NSCountedSet *keyCounts = [NSCountedSet set];
for (NSDictionary *dict in myDictionaries)
[keyCounts unionSet:[NSSet setWithArray:[dict allKeys]]];