Algorithm: array of arrays in Cocoa Touch (perhaps using NSCountedSet) - iphone

This one is a bit tedious in as far as explaining, so here goes. I'm essentially populating a tableView on the iPhone with multiple sections, and potentially multiple rows per section. To my understanding, it's best to have an array of arrays so that you can simply determine how many sections one has by sending a message to the top level array of count, then for rows per section, doing the same for the inner array(s). My data is in the form of a dictionary. One of the key/value pairs in the dictionary determines where it will be displayed on the tableView. An example is the following:
{
name: "bob",
location: 3
}
{
name: "jane",
location: 50
}
{
name: "chris",
location: 3
}
In this case I'd have an array with two subarrays. The first subarray would have two dictionaries containing bob and chris since they're both part of location 3. The second subarray would contain jane, since she is in location 50. What's my best bet in Cocoa populate this data structure? A hash table in C would probably do the trick, but I'd rather use the classes available in Cocoa.
Thanks and please let me know if I need to further clarify.

The following code works: (edit: added my initialization code)
NSArray * arrayOfRecords = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"bob", #"name",
[NSNumber numberWithInt:3], #"location", nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"jane", #"name",
[NSNumber numberWithInt:50], #"location", nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"chris", #"name",
[NSNumber numberWithInt:3], #"location", nil],
nil];
NSMutableDictionary * sections = [NSMutableDictionary dictionary];
for (NSDictionary * record in arrayOfRecords)
{
id key = [record valueForKey:#"location"];
NSMutableArray * rows = [sections objectForKey:key];
if (rows == nil)
{
[sections setObject:[NSMutableArray arrayWithObject:record] forKey:key];
}
else
{
[rows addObject:record];
}
}
NSArray * sortedKeys = [[sections allKeys] sortedArrayUsingSelector:#selector(compare:)];
NSArray * sortedSections = [sections objectsForKeys:sortedKeys notFoundMarker:#""];
NSLog(#"%#", sortedSections);

And NSDictionary is a hash table.

In Cocoa, it's best to use model objects rather than primitive collections, especially when using Bindings. The dictionaries should certainly be model object, and you may want to turn your inner arrays into model objects as well. (The outer array should stay an array.)
Cocoa Touch doesn't have Bindings, but I find (in Cocoa, as I don't program my iPhone) that model objects make things easier to think about and work with. I recommend you make model objects anyway.
(“Model” refers to Cocoa's “Model-View-Controller” pattern, in which Cocoa provides view and controller objects and you provide the model.)

Related

Setting flags for elements in NSMutableArray

I have an NSMutableArray of elements and I want to be able to conditionally set custom flags for some of the elements. For example an error count for certain elements if they return an error. If the count is more than 3, I would like to delete this element from an array.
What would be the best way to implement such behaviour?
A few options:
Have a separate array holding your counter for each object. When deleting one from your original array, remember to delete it's corresponding counter object.
Create a small class that contains an int value and whatever other object you are storing in the array, and populate your NSMutableArray with that object. You will then have your object and the error counter on the same place
Edit: The second option is the most scalable one, if you ever want to add more flags or whatever to it.
You would be better off creating a mutable array filled with mutable dictionaries. This would allow you have two keys corresponding to each index in the array:
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"some text, or what ever you want to store",#"body",
[NSNumber numberWithUnsignedInteger:0],#"errorCount",
nil];
[myMutableArray addObject:mutableDictionary];
And then here is a basic example of how to increment the error count for a specific item in the array:
- (void)errorInArray:(NSUInteger)idx
{
if ([[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue] == 2) {
[myMutableArray removeObjectAtIndex:idx];
}else{
NSUInteger temp = [[[myMutableArray objectAtIndex:idx] objectForKey:#"errorCount"] unsignedIntegerValue];
temp ++;
[[myMutableArray objectAtIndex:idx] setObject:[NSNumber numberWithUnsignedInteger:temp] forKey:#"errorCount"];
}
}
As alluded above, no need for custom object creation necessarily:
Creating a mutable array, creating a dictionary with objects/keys and adding said dictionary to the array:
NSMutableArray *myArray = [[NSMutableArray alloc] init] autorelease];
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"John Doe", #"elementName",
[NSNumber numberWithInt:0], #"errorCount",
nil];
[myArray addObject:myDictionary];

Sort an NSMutableArray / Dictionary with several objects

I am having a problem that I think I am overcomplicating.
I need to make either an NSMutableArray or NSMutableDictionary. I am going to be adding at least two objects like below:
NSMutableArray *results = [[NSMutableArray alloc] init];
[results addObject: [[NSMutableArray alloc] initWithObjects: [NSNumber numberWithInteger:myValue01], #"valueLabel01", nil]];
This gives me the array I need but after all the objects are added I need to be able to sort the array by the first column (the integers - myValues). I know how to sort when there is a key, but I am not sure how to add a key or if there is another way to sort the array.
I may be adding more objects to the array later on.
Quick reference to another great answer for this question:
How to sort NSMutableArray using sortedArrayUsingDescriptors?
NSSortDescriptors can be your best friend in these situations :)
What you have done here is create a list with two elements: [NSNumber numberWithInteger:myValue01] and #"valueLabel01". It seems to me that you wanted to keep records, each with a number and a string? You should first make a class that will contain the number and the string, and then think about sorting.
Doesn't the sortedArrayUsingComparator: method work for you? Something like:
- (NSArray *)sortedArray {
return [results sortedArrayUsingComparator:(NSComparator)^(id obj1, id obj2)
{
NSNumber *number1 = [obj1 objectAtIndex:0];
NSNumber *number2 = [obj2 objectAtIndex:0];
return [number1 compare:number2]; }];
}

iOS - deleting all entries containing a key on all NSDictionaries stored inside a main NSDictionary

I have a main NSMutableDictionary that contains a collection of others NSMutableDictionary.
The thing goes like this:
NSMutableDictionary *subDict1 = [NSMutableDictionary dictionaryWithObjectsAndKeys:
obj1, #"name",
obj2, #"color",
nil];
NSMutableDictionary *subDict2 = [NSMutableDictionary dictionaryWithObjectsAndKeys:
obj3, #"name",
obj4, #"color",
obj5, #"address",
obj6, #"phone",
obj7, #"color",
obj8, #"parent",
nil];
NSMutableDictionary *subDict3 = [NSMutableDictionary dictionaryWithObjectsAndKeys:
obj0, #"name",
obj9, #"parent",
objA, #"site",
objB, #"surname",
objC, #"label",
nil];
These sub dictionaries may have different number of entries and the keys may vary. Some may have keys with the same name.
They are stored in a main dictionary like this:
NSMutableDictionary *mainDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
subDict1, #"1",
subDict3, #"3",
subDict2, #"2",
nil];
I want at one shot, remove all entries in all sub dictionaries that have a specific key.
I know I can iterate thru the dictionaries and sub dictionaries, but I also know that Dictionaries have smart ways to do that using predicates and other stuff, but I am not seeing how. I am trying to find that because the method this will run is a little bit tricky and have to do it as fast as possible and I am not sure if normal iteration with loops or whatever will be fast enough...
Any clues? thanks.
Here's a recursive method that doesn't care how many levels deep the target key is. (haven't tried it) ...
- (void)removeKey:(NSString *)keyToRemove fromDictionary:(NSMutableDictionary *)dictionary {
NSArray *keys = [dictionary allKeys];
if ([keys containsObject:keyToRemove]) {
[dictionary removeObjectForKey:keyToRemove];
} else {
for (NSString *key in keys) {
id value = [dictionary valueForKey:key];
if ([value isKindOfClass:[NSMutableDictionary self]]) {
[self removeKey:keyToRemove fromDictionary:(NSMutableDictionary *)value];
}
}
}
}
You'll need to just iterate through all the subdirectories and remove the appropriate key-value pairs manually. You shouldn't worry if it's fast enough at this point. Rather, create a working implementation and test/measure it. If it's too slow, then you can profile it and come up with ways to improve performance. Premature optimization is a bad thing.
NSArray* keys = [NSArray arrayWithObjects:#"name", #"address", nil];
[dics removeObjectsForKeys:keys]; //Or sub dics

How to read all NSDictionary objects for a key to an array

I have a dictionary "playerDict" that reads data from a plist where there is names (myKey) with nine associated objects to each key.
I am trying to read all objects for a specific key (myKeys) into an NSMutableArray (theObjects). I have read the class reference and search internet but cannot figure our this, probably very simple, problem.
Among all other test i have done I have tried the following but that returns the key into theObjects and not the objects.
theObjects = [playerDict objectForKey:myKeys];
Anyone that could give a hint?
Here is the code that created the dict, i stripped it:
NSArray *objs = [NSArray arrayWithObjects:[NSNumber numberWithBool:playerObject.diffHard],[NSNumber numberWithBool:playerObject.diffMedium],
[NSNumber numberWithBool:playerObject.diffEasy],[NSNumber numberWithBool:playerObject.currentGame],
[NSNumber numberWithInt:playerObject.currentGameQuestion],[NSNumber numberWithInt:playerObject.currentGameRightAnswer],
[NSNumber numberWithInt:playerObject.currentGameType],[NSNumber numberWithInt:playerObject.nrOfType0Games],
[NSNumber numberWithInt:playerObject.type0Result], nil];
NSDictionary *newPlayerDict = [NSDictionary dictionaryWithObjectsAndKeys:objs, keyString, nil];
Try valueForKey:
You can only store one item per key in an NSDictionary. If you need story multiple items for the same key, you need to first add each of the items to an NSArray (or NSSet) that you instead then set as an object in your dictionary.
If might be useful if you posted the code that creates the dictionary.
Update: It looks like you are already doing this. So:
NSArray *myObjs=[playerDict objectForKey:keyString];
will get you your array. And this:
BOOL diffHard=[[myObjs objectAtIndex:0] boolValue];
BOOL diffMedium=[[myObjs objectAtIndex:1] boolValue];
Will get you the value you stored in the first and second objects of the array. Repeat it for the rest.

Is this possible with NSDictionary? Or should I use something else?

What I am trying to do is create a an array of text strings, and I want to select a random text string out of this array, which I could normally do easily with an array. However, I would also like to be able to put them into categories, and when I select a random one, I would like to know what category it is in and do something different with it which is decided by the category it was in. I was thinking I could use keys in NSDictionary to decide the categories as in setting all the entries in a category to have the same key. But then I don't know how I could retrieve a random one from that dictionary and then know what the key was. I have never used NSDictionary so I don't know much about it so maybe what I just said doesn't make any sense.
It is also possible that I am approaching this in completely the wrong way, so if you have any other suggestions as to how to do what I described I would be open to that, and a pretty detailed code answer would be best if it is possible.
I would just fill the array with dictionaries, i.e.
{
{ category = Animals,
name = Cat },
{ category = Vehicles,
name = Helicopter },
{ category = Foods
name = Pie },
{ category = Animals,
name = Zebra }
}
and then select randomly from that array.
For a programmatic example:
theArray = [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:#"Animals", #"category", #"Cat", #"name", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"Vehicles", #"category", #"Helicopter", #"name", nil],
// ...
[NSDictionary dictionaryWithObjectsAndKeys:#"Animals", #"category", #"Zebra", #"name", nil],
nil];
// ...
randomDict = [theArray objectAtIndex:(rand() % [theArray count])];
NSString *name = [randomDict objectForKey:#"name"];
NSString *category = [randomDict objectForKey:#"category"];