iPhone SDK Nested For Loop performance - iphone

I have a NSArray of string id and a NSDictionary of NSDictionary objects. I am currently looping through the string id array to match the id value in the NSDictionary.
There are around 200 NSDictionary objects and only 5 or so string ID.
My current code is such:
for (NSString *Str in aArr) {
for (NSDictionary *a in apArr)
{
if ([a objectForKey:#"id"] == Str)
{
NSLog(#"Found!");
}
}
}
The performance of the above code is really slow and I was wondering if there is a better way to do this?

I'd implement your code in the following way:
for (NSDictionary *a in apArr)
{
if ([aArr containsObject:[a objectForKey:#"id"]])
{
NSLog(#"Found!");
}
}
I'm still not sure about containsObject performance, but, I guess there should be SDK optimizations to find objects faster than O(n).
Added:
Another suggestion. I suppose, that "id" field is unique for all NSDictionary objects. If it is so, you can remap your NSArray of NSDictionaries to NSDictionary:
from:
index -> NSDictionary
to:
id -> NSDictionary
And you will find elements for O(1) instead of O(n).
Re remapping. Either you should create NSDictionary with appropriate format (id -> object) or you may remap your array in the following way:
NSMutableDictionary *md = [[NSMutableDictionary alloc] init];
for ( NSDicationary *a in apArr ) {
[md setObject:a forKey:[a objectForKey:#"id"]];
}

Not sure about the iPhone's cache architecture, but swapping the two loops so that you make as many lookups on the same dictionary before switching to another sounds like it should improve locality. Better locality often leads to (sometimes dramatically) better performance.

Related

Issues converting nested Plist to NSDictionaries & Arrays

Given this P-List Dictionary:
How do I get at the 3rd. Key - "Dinner" - which in itself is also a Dictionary, and parse its values correctly?
Or, should I structure this P-List differently to begin with, so I can get at everything more easily?
Here's what I got, starting by grabbing all the Keys from my 'MenuDictionary' and storing them in an Array:
// Load the Property-List file into the Dictionary:
MenuDictionary = [[NSDictionary alloc] initWithContentsOfFile:menuPath];
// Get all the Keys from the Dictionary, put 'em into a 'mealTimesArray':
mealTimesArray = [[MenuDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
// For each meal-type KEY, grab all the Values (dishes) in it and store them in a 'mealDishesArray':
for (NSString *mealTime in mealTimesArray) {
NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];
// Now I can iterate through the 'mealDishesArray' to access each dish at
// a time, so I can print them out or do whatever else:
for (NSString *dish in mealDishesArray) {
NSLog(#"Iterating through 'mealDishesArray' now...");
NSLog(#"Current 'dish' is: %#", dish);
The problem occurs when I get to the "Dinner" key: its a Dictionary, containing 2 Keys with 2 array Values. So how do I load its contents into a Dictionary object? More specifically, what 'init' method should I be using to load the "Dinner" contents into my new Dictionary object?
I tried this - doesn't work:
// I put this inside the first fast-enum loop:
if ([mealTime isEqualToString: #"Dinner"]) {
// init new Dictionary object (declared previously):
dinnerDictionary = [[NSDictionary alloc] initWith ???];
I'd like to init it with the contents of the "Dinner" Key, but its not a P-List file obviously, so I can't use
initWithContentsOfFile: pathName
I don't understand which of the other init methods will give me access to both the Keys and Values of "Dinner". Because even though "Dinner" is structured as a Dictionary, its currently sitting inside an Array, which doesn't regard it as a Dictionary (I think...)
I'm a little unclear about this obviously.
Or, should I be structuring my P-List differently to begin with so I can get at this nested Dinner dictionary?
Any ideas?
I think plist structure makes sense, and dealing with the contents conditionally based on class is perfectly okay, too. I would react to what's in the plist within a reasonable range of expectations, so...
// for each meal...
for (NSString *mealTime in mealTimesArray) {
// we're not sure what kind of meal we have
id mealInfo = [MenuDictionary valueForKey:mealTime];
if ([id isKindOfClass:[NSArray self]]) {
// it's an array? cast as an array and deal with the array
NSArray *mealDishesArray = (NSArray *)mealInfo;
[self handleMealArray:mealDishesArray];
} else if ([id isKindOfClass:[NSDictionary self]]) {
// it's a dictionary? that's cool, too. cast as a dictionary and deal with it
NSDictionary *mealDictionary = (NSDictionary *)mealInfo;
[self handleMealDictionary:mealDictionary];
}
}
// you've worked out to handle the array
- (void)handleMealArray:(NSArray *)mealDishesArray {
for (NSString *dish in mealDishesArray) {
NSLog(#"Iterating through 'mealDishesArray' now...");
NSLog(#"Current 'dish' is: %#", dish);
}
}
// handle the dictionary like a dictionary, realizing that it contains
// arrays, which you've already worked out how to handle
- (void)handleMealDictionary:(NSDictionary *)mealDictionary {
for (NSString *dishType in [mealDictionary allKeys]) {
NSArray *mealDishesArray = [mealDictionary valueForKey:dishType];
[self handleMealArray:mealDishesArray];
}
}
The 'problem' is with this line:
NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];
when mealTime is 'Dinner' you are assigning mealDishesArray a value that is an NSDictionary. Thinking you have an array you then use:
for (NSString *dish in mealDishesArray)
to iterate over the elements in the array which is not going to give you what you expect for 'Dinner'. You might consider adding something like:
NSAssert ([mailDishesArray isKindOfClass: [NSArray class]], #"Expecting an array");
after your assignment to mealDishesArray.
What is the solution? Your PLIST has a totally different structure between 'Breakfast,' 'Lunch,' and 'Dinner.' Why is 'Dinner' a NSDictionary and the others are NSArray? Make them all the same type. If they can't be, then you must conditionalize your code based on:
if ([mealTime isEqualToString: #"Dinner"]) {
NSDictionary *dinnerDictionary = (NSDictionary *) [MenuDictionary valueForKey:mealTime];
/* ... */ }
You don't need to alloc anything or read anything from file; you already have a dictionary for the 'Dinner' data.

Sorting NSMutableDictionary's data

I am having an NSMutableDictionary with, being filled dynamically. It looks like this:
{
SomeKey = 6;
AnotherKey = 2;
JustAnotherKey = 28;
}
I need to sort this, so it will be like this one:
{
JustAnotherKey = 28;
SomeKey = 6;
AnotherKey = 2;
}
Is there any way to achieve this? Thanks in advance.
No, sorry!
An NSDictionary doesn't support sorting it's keys - you would have to do that yourself.
Get the keys array from your dictionary, sort that and then go through and get the values from your dictionary. Something like :
NSArray *keys = [myDictionary allKeys];
NSArray *sortedKeys = [keys sortedArrayUsingSelector:#selector(compare:)];
NSArray *values = [NSMutableArray array];
for (id key in sortedKeys)
[values addObject:[myDictionary objectforKey:key]];
Now, values are in the correct order.
However, that's quite a lot of work; if you want them sorted, I would look at storing them in an array to start with?
A Dictionary is an unordered set, which means it doesn't have any order of its elements.So even if you insert the first object as say "one":"first value", and then "two":"second value", when you iterate over the keys, you might get it in any random order(eg: "two' and then "one").
However, if all you want is the values in sorted order, you can iterate over all the keys, fetch the values and store it in an array, and then sort them.
NSArray *values=[myDict allValues];
NSMutableArray *sortedKeys=[[NSMutableArray alloc] init];
NSArray *sortedValues = [values sortedArrayUsingSelector:#selector(yourSelector)];
for (val in sortedArray){
NSString *key=(NSString*)[[myDict allKeysForObject:val] objectAtIndex:0];
[sortedKeys addObject:key]
}
This would be starter for getting first the values in sorted order, and then the corresponding keys. (It is not doing any error checks. So beware of OutOfIndex exceptions).
I am not sure how good would be the efficiency of this code be, coz allKeysForObject would be iterating over all the keys.
Instead of using a NSMutableDictionary, you might consider to use a NSMutableArray and populate it with your own model class which contains a property with the key string and a property with the value.
NSMutableArray provides methods to sort these objects.
Is this helpful enough?

Performance Problem while retrieving custom objects from array

I create a custom object that has some properties like ID and Title,description etc...
And I add it to an array. (That array may contains more than 500 values).
And I use the following code to retrieve custom objects,
-(CustomObjects *)getObjectWithId:(int)id {
CustomObjects *objCustomObjects = nil;
for (CustomObjects *customObjects in arrayCustomObjects) {
if (customObjects.id == id) {
objCustomObjects = customObjects;
break;
}
}
return objCustomObjects;
}
But It has some performance problem, because I use the function to call on UIScrollview pinch.
How can I improve performance in fetching the objects?
thanks in advance,
A dictionary is better for this. The only catch is that you can’t have a NSDictionary with primitive int keys, so that you have to wrap the id in an NSNumber.
- (void) addCustomObject: (CustomObject*) obj {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
[dictionary setObject:obj forKey:wrappedID];
}
- (CustomObject*) findObjectByID: (int) idNumber {
NSNumber *wrappedID = [NSNumber numberWithInt:[obj idNumber]];
return [dictionary objectForKey:wrappedID];
}
A dictionary (also called hash table) does not have to go through all the values to find the right one, it has all the values arranged cleverly according to the keys so that it can jump to the right one or close to it. What you are doing with the array is called linear search and it’s not very efficient.
Better you can use NSDictionary with id as the key. You can easily fetch the object from the dictionary.
Is it Ok for your requirement?
You could use an NSPredicate that checks whether id equals the one you're looking for, and simply filter the custom objects using this predicate by calling filteredArrayUsingPredicate:.
To improve performance, I would try to postpone whatever you're trying to calculate by not directly calling the function that does the heavy work in your scroll view, but rather call [self performSelector:... withObject:nil afterDelay:0]; which postpones the calculation to the next runloop cycle. If you check if there's already a calculation scheduled before you call performSelector you should actually be able to reduce the frequency of the calculation while maintaining a crisp interface.
You must ditch the array in favor for a dictionary if you want to have fast lookups.
If you want to access objects both by key and index then you need to the objects around in two collections, and make sure they are in sync.
I have already done a helper class for this named CWOrderedDictionary. It's a subclass of NSMutableDictionary that allows for access to objects by both keys (as any dictionary do), and by index using methods identical to NSMutableArray.
My class is available to use for inspiration or as is from here: https://github.com/jayway/CWFoundation/
Use NSPredicate:-
You will receive the filtered array with the object that has the id you passed;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
NSArray *filtered = [arrayCustomObjects filteredArrayUsingPredicate:predicate];
Instead of intjust use [NSNumber numberWithInt:] , i did some changes in your given code.
-(CustomObjects *)getObjectWithId:(NSNumber* )id {//changed int to NSNumber
CustomObjects *objCustomObjects = nil;
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF.id==%#",id];
NSArray *result = [array filteredArrayUsingPredicate:bPredicate];
//return filtered array contains the object of your given value
if([result count]>0)
objCustomObjects = [result objectAtIndex:0];
}
return objCustomObjects;
}

Reverse NSMutableDictionary

I'm just learning objective-c and I have an NSMutableDictionary with UITableViewCell sections in it.
The thing is, it's sorted on year and I'd like to reverse that order so the last year comes on top and not bottom.
Any ideas how to reverse sort an NSMutableDictionary?
Thanks in advance
As already stated, an NSDictionary has no order.
What you can do is order the keys in a separate array and then access the keyed objects in the dictionary in the order that the keys appear in your ordered array.
Something like this:
NSArray *orderedKeys = [[dictionary allKeys] sortedArrayUsingDescriptors:<your sort descriptor>];
for (NSString *key in orderedKeys)
{
MyObject *myObject = [dictionary objectForKey:key];
// do what you will with myObject
}
As already pointed out, a dictionary does not guarantee you any order. You might be interested in this related question that shows how to get all dictionary values sorted using a custom function.
An NSDictionary has no implied order.

How can i get Original order of NSDictionary/NSMutableDictionary?

i have created NSMutableDictionary with 10 keys.Now i want to access NSMutableDictionary keys in a same order as it was added to NSMutableDictionary (using SetValue:* forKey:* );
How can i achieve that ?
If you absolutely must use a dictionary container, you have to use a key that is sortable by the order in which you add key-value pairs. Thus, when creating your dictionary, you use a key that is an auto-incrementing integer or similar. You can then sort on the (integer) keys and retrieve the values associated with those keys.
If you do all of that, however, you may as well just use an NSMutableArray and add values to the array directly! It will be much faster and require less code. You just retrieve objects in order:
for (id obj in myArray) { /* do stuff with obj... */ }
NSMutableDictionary can't do that. Take a look at e.g. Matt Gallaghers OrderedDictionary.
I wrote a quick method to take a source array (of objects that are all out of order) and a reference array (that has objects in a desired (and totally arbitrary) order), and returns an array where the items of the source array have been reorganized to match the reference array.
- (NSArray *) reorderArray:(NSArray *)sourceArray toArray:(NSArray *)referenceArray
{
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [referenceArray count]; i++)
{
if ([sourceArray containsObject:[referenceArray objectAtIndex:i]])
{
[returnArray addObject:[arrReference objectAtIndex:i]];
}
}
return [returnArray copy];
}
Note that this is very fragile. It uses NSArray's containsObject: method, which ultimately will call NSObject's isEqual:. Basically, it should work great for arrays of NSStrings, NSNumbers, and maybe NSDates (haven't tried that one yet), but outside of that, YMMV. I imagine if you tried to pass arrays of UITableViewCells or some other really complex object, it would totally sh*t itself, and either crash or return total garbage. Likewise if you were to do something like pass an array of NSDates as the reference array and an array of NSStrings as the source array. Also, if the source array contains items not covered in the reference array, they'll just get discarded. One could address some of these issues by adding a little extra code.
All that said, if you're trying to do something simple, it should work nicely. In your case, you could build up the reference array as you are looping through your setValue:forKey:.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (//whatever you are looping through here)
{
[yourDictionary setValue://whatever forKey:key];
[referenceArray addObject:key];
}
Then, when you want to loop over your items in the order they came in, you just
for (NSString *key in [self reorderArray:[myDict allKeys] toArray:referenceArray])
Actually you have a reference array in order manner then why you have to add to one more array.So i guess this approach is not good.Please consider my opinion.
Although #GenralMike 's answer works a breeze, it could be optimized by leaving off the unnecessary code as follows:
1) Keep an array to hold reference to the dictionary keys in the order they are added.
NSMutableArray *referenceArray = [[NSMutableArray alloc] init];
NSMutableDictionary *yourDictionary = [[ NSMutableDictionary alloc] init];
for (id object in someArray) {
[yourDictionary setObject:object forKey:someKey];
[referenceArray addObject:someKey]; // add key to reference array
}
2) Now the "referenceArray" holds all of the keys in order, So you can retrieve objects from your dictionary in the same order as they were originally added to the dictionary.
for (NSString *key in referenceArray){
//get object from dictionary in order
id object = [yourDictionary objectForKey:key];
}