views:

85

answers:

3

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!

+4  A: 

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.

Jesse Naugher
Indeed. This is an attempt to use a set as a dictionary, which is why it seems awkward.
Chuck
sorry about that it was just quicker to type out and i just put it up before grabbing dinner. i've updated the code and it should make more sense now. :) thanks!
taber
A: 
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];
    }
}
jojaba
hi will this work with iOS 3.0? i forgot to mention that i want to avoid iOS 4.0 only API's. thanks!
taber
Sorry but predicateWithBlock requires 4.0.
jojaba
oh i thought so, thanks anyway, appreciate it
taber
Just made an edit that will allow for it work with iOS 3.0.
jojaba
A: 

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;
Kevin Ballard