views:

805

answers:

2

I have an NSMutableArray that is populated with objects of strings. For simplicity sake we'll say that the objects are a person and each person object contains information about that person.

Thus I would have an NSMutableArray that is populated with person objects:

person.firstName
person.lastName
person.age
person.height

And so on.

The initial source of data comes from a web server and is populated when my application loads and completes it's initialization with the server. Periodically my application polls the server for the latest list of names.

Currently I am creating an NSArray of the result set, emptying the NSMutableArray and then re-populating the NSMutableArray with NSArray results before destroying the NSArray object.

This seems inefficient to me on a few levels and also presents me with a problem losing table row references which I can work around, but might be creating more work for myself in doing so.

The inefficiency seems to be that I should be able to compare the two arrays and end up with a filtered NSArray. I could then add the filtered set to the NSMutableArray. This would mean that I can simply append new data to the NSMutableArray instead of throwing everything out and re-populating.

Conversely I would need to do the same filter in reverse to see if there are records that need removing from the NSMutableArray.

Is there any method to do this in a more efficient manner? Have I overlooked something in the docs some place that refers to a simpler technique?

I have a problem when I empty the NSMutableArray and re-populate in that any referencing tables lose their selected row state. I can track it and re-select it, but my theory is that using some form of compare and adding objects and removing objects instead of dealing with the whole array in one block might mean I keep my row reference (assuming the item isn't deleted of course).

Any suggestions or help much appreciated.

Update

Would it be just as fast to do a fast enumeration over each comparing each line item as I go? It seems like an expensive operation, but with the last fast enumeration code it might be pretty efficient...

Solution

I ended up going with Abizem's suggestion. Creating the mutable copy of the array and a copy of the object appears to be the slightly faster approach than using sbooth's solution when dealing with large sets of data. Both worked great, I just got more of an edge by using the mutable copy approach. That being said, it did open my eyes up to NSSet where I hadn't looked before.

Thanks for the feedback.

+3  A: 

You can use NSSet to do this type of thing easily (assuming your person objects are unique):

NSSet *existingItems = [NSSet setWithArray:existingItemArray];
NSSet *newItems = /* Get the new items from the server */

// Determine which items were removed
NSMutableSet *removedItems = [NSMutableSet setWithSet:existingItems];
[removedItems minusSet:newItems];

// Determine which items were added
NSMutableSet *addedItems = [NSMutableSet setWithSet:newItems];
[addedItems minusSet:existingItems];

// Modify the original array
[existingItemArray removeObjectsInArray:[removedItems allObjects]];
[existingItemArray addObjectsFromArray:[addedItems allObjects]];

I'd be surprised if the performance isn't decent, as I'm sure the implementation is optimized.

sbooth
Thanks for the suggestion sbooth. I hadn't considered using NSSet, but this approach makes sense from a more efficient approach. I could potentially have a large set of data so all the efficiency gains I get will help.
Hooligancat
+2  A: 

Two points.

  1. The new NSArray contains all the data you need to show. Which is why you are adding and deleting from the NSMutableArray to match the new one.
  2. You don't want to lose the selected state of the rows in your table.

Here’re my suggestions

  1. Rather than emptying the NSMutableArray and repopulating it with the new Array; why not create a mutableCopy of the NSArray and set that as your new NSMutableArray?
  2. Rather than worry about the order of the items (and hence the selected row number); how about creating a copy of the object that is selected, and after creating your new NSMutableArray as in step 1, find the matching object in the new array and set that as the selected row in the table using its new index.
Abizern
Abizem... Like the approach. At least this way I don't need to compare every item in each array with each other. Even if I took the `NSSet` direction sbooth suggested it is still doing a compare. This approach means I only need to set the selected index once and boom I'm done. I'll implement each and see if I get a visual/performance difference using one over the other.
Hooligancat
Hooligancat…also, if you are doing any sort of comparing of equality of objects, be sure to write an `isEqualTo:` method for your object class. For example `NSString` has the `isEqualToString` method which makes sure that the string values are the same rather than seeing of the `NSString *` pointers are the same.
Abizern
Good point Abizem. Thanks
Hooligancat