collections reference in obj-c - objective-c

I'm new to Obj-C, and I have a little problem here.
I'm trying insert the values from a temp Dictionary into an array
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myDict setObject:[NSNumber numberWithInt:100] forKey:#"Record"];
[myDict setObject:#"www" forKey:#"Name"];
[myArray addObject:myDict];
[myDict setObject:[NSNumber numberWithInt:80] forKey:#"Record"];
[myDict setObject:#"eee" forKey:#"Name"];
[myArray addObject:myDict];
[myDict setObject:[NSNumber numberWithInt:70] forKey:#"Record"];
[myDict setObject:#"rrr" forKey:#"Name"];
[myArray addObject:myDict];
[myDict setObject:[NSNumber numberWithInt:0] forKey:#"Record"];
[myDict setObject:#"ttt" forKey:#"Name"];
[myArray addObject:myDict];
NSLog(#"myArray %#",myArray);
Regardless of the bad implementation code here (and my bad English as well :p), what I'm trying to figure out is how do I clean myDict after each insertion in myArray.
Using [myDict removeAllObjects]; after each addObject is giving me an empty array at the end.
Thanks!
Thanks for all your answers, with [myArray addObject:[[myDict copy] autorelease]]; the code works just fine.

The problem here is you're adding the exact same mutable dictionary to the array over and over. So your array is simply a collection of references to the exact same object. Therefore it shouldn't be surprising that every object in your array is identical to the last one.
In order to do what you want, you have two options. The first is to copy your mutable dictionary each time and add that copy to the array. You can do this by using
[myArray addObject:[[myDict copy] autorelease]];
The other solution is to use separate dictionaries to begin with. Instead of mutating a mutable dictionary each time, you could construct a brand new NSDictionary:
[myArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:100], #"Record",
#"www", #"Name",
nil]];
[myArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:80], #"Record",
#"eee", #"Name",
nil]];

I think you need to copy the dictionary before you add it to the array...
[myArray addObject:[[myDict copy] autorelease]];
Then you can use [myDict removeAllObjects] as normal.

NSMutableDictionary * as * suggests is a pointer to the object of dictionary.
So when you add it to an array it is still the same object - pointed by myDict as well as by the element in the array.
Therefore, I suggest you create new dictionary after each add:
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myDict setObject:[NSNumber numberWithInt:100] forKey:#"Record"];
[myDict setObject:#"www" forKey:#"Name"];
[myArray addObject:myDict];
myDict = [[NSMutableDictionary alloc] init];
[myDict setObject:[NSNumber numberWithInt:80] forKey:#"Record"];
[myDict setObject:#"eee" forKey:#"Name"];
[myArray addObject:myDict];
I assume you are using ARC in the code above.
Although, the more Objective-C'ish way would be to do:
[myArray addObject: [NSDictionary dictionaryForObjectsAndKeys: [NSNumber numberWithInt:80], #"Record", #"www", #"Name"]];

Related

How to sort an NSArray in alphabetical order with numbers first

I have a small doubt that is I have an NSArray which contains the following 4 objects:
Genesis, 1 Kings, leviticus, 2 Kings
I want to sort this array in dictionary order like i want an expected output like this
1 Kings, 2 Kings, Genesis, leviticus
How can this be achieved?
Go to this link:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Collections/Articles/Arrays.html#//apple_ref/doc/uid/20000132-SW5
This is Apple documentation and your problem is solved over there.
Check out the example.
//First create the array of dictionaries
NSString *last = #"lastName";
NSString *first = #"firstName";
NSMutableArray *array = [NSMutableArray array];
NSArray *sortedArray;
NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Jo", first, #"Smith", last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Joe", first, #"Smith", last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Joe", first, #"Smythe", last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Joanne", first, #"Smith", last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Robert", first, #"Jones", last, nil];
[array addObject:dict];
//Next we sort the contents of the array by last name then first name
// The results are likely to be shown to a user
// Note the use of the localizedCaseInsensitiveCompare: selector
NSSortDescriptor *lastDescriptor =[[NSSortDescriptor alloc] initWithKey:last
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =
[[NSSortDescriptor alloc] initWithKey:first
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil];
sortedArray = [array sortedArrayUsingDescriptors:descriptors];
This code is an example from Apple documentation.
You can sort an array of NSString alphabetically like this:
NSArray *sortedArray = [myArray sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];

Is there a more elegant way of finding unique NSDictionary keys within an NSArray?

Aim: To obtain an NSArray containing unique keys for given NSDictionary(s) using elegant code
Example Code with Current Working Solution:
NSArray *data = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], #"a", [NSNumber numberWithInt:2], #"b", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:3], #"b", [NSNumber numberWithInt:4], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:5], #"a", [NSNumber numberWithInt:6], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:7], #"b", [NSNumber numberWithInt:8], #"a", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:8], #"c", [NSNumber numberWithInt:9], #"b", nil],
nil];
// create an NSArray of all the dictionary keys within the NSArray *data
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for (int i=0; i<[data count]; i++) {
[setKeys addObjectsFromArray:[[data objectAtIndex:i] allKeys]];
}
NSArray *arrayKeys = [setKeys allObjects];
NSLog(#"arrayKeys: %#", arrayKeys);
Which returns the desired array of keys:
2012-06-11 16:52:57.351 test.kvc[6497:403] arrayKeys: (
a,
b,
c
)
Question: Is there a more elegant way of approaching this? Surely there must be some KVC approach that can get all the keys without having to iterate through the array? I've been looking at Apple Developer Documentation and can't see a solution. Any ideas? (looking at purely elegance of code rather than performance).
Normally you could use KVC by doing something like this:
NSArray *uniqueKeys = [data valueForKeyPath:#"#distinctUnionOfArrays.allKeys";
However NSDictionary overrides the valueForKey: selector which is used by the KVC internals, so this will not work correctly.
The documentation for NSDictionary's valueForKey: method tells us that:
If key does not start with “#”, invokes objectForKey:. If key does start with “#”, strips the “#” and invokes [super valueForKey:] with the rest of the key.
So we just insert an # before allKeys:
NSArray *uniqueKeys = [data valueForKeyPath:#"#distinctUnionOfArrays.#allKeys"];
And we get what we want:
(lldb) po [data valueForKeyPath:#"#distinctUnionOfArrays.#allKeys"]
(id) $14 = 0x07bb2fc0 <__NSArrayI 0x7bb2fc0>(
c,
a,
b
)
This is less ugly, and possibly marginally faster, I suppose:
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for (NSDictionary* dict in data) {
for (id key in [dict keyEnumerator]) {
[setKeys addObject:key];
}
}
But you're not doing a particularly common operation, so I wouldn't expect to find some incredibly elegant method. If that's what you want, go learn Haskell.
You could try this:
NSMutableSet *setKeys = [[NSMutableSet alloc] init];
for(NSDictionary *dict in data) {
[setKeys addObjectsFromArray:[dict allKeys]];
}
NSArray *arrayKeys = [setKeys allObjects];
If you prefer blocks you could use this:
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[setKeys addObjectsFromArray:[obj allKeys]];
}];

Missing sentinel in function call?

Not sure why I get "Missing sentinel in function call?"
NSMutableArray *kkk = [NSMutableArray arrayWithObjects: #"a", #"b", #"cat", #"dog", nil];
ppp = [NSMutableArray arrayWithCapacity:3];
[ppp addObject:[[NSMutableArray alloc] initWithObjects: kkk]]; // <<--- Missing sentinel in function call
[ppp addObject:[[NSMutableArray alloc] initWithObjects: kkk, nil]]; //<<--- change, but it falls out
NSLog(#"Working: %# %#", [[ppp objectAtIndex:0] objectAtIndex:3], [[ppp objectAtIndex:0] objectAtIndex:2] );
initWithObjects: must be terminated with a trailing nil. Since it is a single object, you should be a able to use initWithObject:. That said, you will be leaking the array like this. Do
[ppp addObject:[NSMutableArray arrayWithObject:kkk]];
There is one more problem with the piece of code here,
NSMutableArray *kkk = [NSMutableArray arrayWithObjects: #"a", #"b", #"cat", #"dog", nil];
ppp = [NSMutableArray arrayWithCapacity:3];
[ppp addObject:[[NSMutableArray alloc] initWithObjects: kkk, nil]];
You are creating a three dimensional array. So
NSLog(#"Working: %# %#", [[ppp objectAtIndex:0] objectAtIndex:3], [[ppp objectAtIndex:0] objectAtIndex:2] );
is wrong.
NSLog(#"Working: %# %#", [[[ppp objectAtIndex:0] objectAtIndex:0] objectAtIndex:3], [[[ppp objectAtIndex:0] objectAtIndex:0] objectAtIndex:2] );
should log proper values.
However if you need a two dimensional array based on your log statement, I would say you need to do this instead,
[ppp addObject:kkk];
You need to add nil as the last object in the list.
[ppp addObject:[[NSMutableArray alloc] initWithObjects: kkk, nil]];
Basically it tells the method to stop looking for more objects. Without that, it can look at a bad pointer and crash.

Copying NSDictionary to NSArray

I have a variable declared as NSDictionary. How can I copy it to an extern const NSArray variable? For example:
NSMutableDictionary *selectedItem = [self.dataArray objectAtIndex:indexPath.row];
[selectedItem setObject:[NSNumber numberWithBool:targetCustomCell.checked]
forKey:#"checked"];
allKeys returns an NSArray containing all the keys. allValues returns an NSArray containing all the values.
NSArray *keys = [myDict allKeys];
NSArray *values = [myDict allValues];

Can NSArray hold NSDictionary in Objective-C/

Can each index of array hold the NSDictionary?
Thank You.
Yes, the value of an NSArray can resolve to an object identifier for an NSDictionary. However, the array doesn't "hold" the NSDictionary, nor can the index of an NSArray be an NSDictionary. An index from an Array is always an integer value.
An NSArray can hold any type of object, so yes, putting an NSDictionary in an NSArray works just fine.
You sure can, here is an example:
NSDictionary *dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:
#"value1", #"key1", #"value2", #"key2", nil];
NSDictionary *dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
#"Billy", #"Goat", #"Rover", #"Dog", nil];
NSArray *arrayWithDictionaries = [[NSArray alloc] initWithObjects:
dict1, dict2, nil];
[dict1 release];
[dict2 release];