snksnk
snksnk

Reputation: 1595

Multiple dictionaries within an array and Checking for duplicate keys - Objective C

I have an array which contains multiple Dictionaries each one with 3 keys (@"date", @"username", @"text").

What I want to check for, is whether the same user (@"username") exists in more than one dictionary in that Array. And, if she does, combine the text for those "duplicates" into one dictionary.

I have considered this answer to check for duplicates and this one but I cannot figure out how to combine these two.

Upvotes: 0

Views: 6217

Answers (3)

Lyndsey Scott
Lyndsey Scott

Reputation: 37300

Jumping in here because although I think you should work on the code yourself first, I think Miro's answer is more complicated than the issue requires and though I like the idea of using predicates in Greg's answer, here's a 3rd solution that (1) wouldn't require you to change your data structure and (2) references the necessary loops...

The way I'd do it: Create an NSMutableArray then start adding the usernames in order. If the NSMutableArray already contains the username though, don't add another instance of the username, but instead merge the dictionary info.

ex.

// Note: I'm calling your array of user dictionaries userArray.

// Create a username array to store the usernames and check for duplicates
NSMutableArray *usernames = [[NSMutableArray alloc] init];

// Create a new userArray to store the updated dictionary info, merged
// entries et. al.
NSMutableArray *newUserArray = [[NSMutableArray alloc] init];

// Go through the array of user dictionaries
for (NSDictionary *userDict in userArray) {

    // If the usernames array doesn't already contain the username,
    // add it to both the usernames array and the newUserArray as is
    if (![usernames containsObject:[userDict objectForKey:@"username"]]) {
        [usernames addObject:[userDict objectForKey:@"username"]];
        [newUserArray addObject:userDict];
    }

    // Otherwise, merge the userArray entries
    else {

        // Get a mutable copy of the dictionary entry at the first instance
        // with this username 
        int indexOfFirstInstance = [usernames indexOfObject:[userDict objectForKey:@"username"]];
        NSMutableDictionary *entry = [[newUserArray objectAtIndex:indexOfFirstInstance] mutableCopy];

        // Then combine the "text" or whatever other values you wanted to combine
        // by replacing the "text" value with the combined text.
        // (I've done so with a comma, but you could also store the value in an array)
        [entry setValue:[[entry objectForKey:@"text"] stringByAppendingString:[NSString stringWithFormat:@", %@", [userDict objectForKey:@"text"]]] forKey:@"text"];

        // Then replace this newly merged dictionary with the one at the 
        // first instance
        [newUserArray replaceObjectAtIndex:indexOfFirstInstance withObject:entry];
    }
}

Upvotes: 4

Greg
Greg

Reputation: 25459

If you use NSMutableDictionary, NSMutableArray and NSMutableString you can do it with predicate like that:

    NSMutableDictionary *d1 = [@{@"username": @"Greg", @"text" : [@"text 1" mutableCopy]} mutableCopy];
    NSMutableDictionary *d2 = [@{@"username": @"Greg", @"text" : [@"text 2" mutableCopy]} mutableCopy];
    NSMutableDictionary *d3 = [@{@"username": @"John", @"text" : [@"text 3" mutableCopy]} mutableCopy];
    NSMutableArray *array = [@[d1, d2, d3] mutableCopy];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"username = %@", @"Greg"];
    NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];

    NSMutableDictionary * firstDict = filterArray[0];
    for (NSDictionary *d in filterArray)
    {
        if (firstDict != d)
        {
            [firstDict[@"text"] appendString:d[@"text"]];
            [array removeObject:d];
        }
    }

Upvotes: 1

Miro
Miro

Reputation: 5337

Maybe something like this [untested] example? Loop through, maintain a hash of existing items, and if a duplicate is found then combine with existing and remove.

NSMutableArray main;  // this should exist, with content
NSMutableDictionary *hash = [[NSMutableDictionary alloc] init];

// loop through, backwards, as we're attempting to modify array in place (risky)
for(int i = [main count] - 1; i >= 0; i--){ 
    // check for existing
    if(hash[main[i][@"username"]] != nil){
        int existingIdx = [hash[main[i][@"username"]] integerValue];  // get existing location
        main[existingIdx][@"text"] = [main[existingIdx][@"text"] stringByAppendingString:main[i][@"text"]];   // "combine text" .. or however you'd like to 
        [main removeObjectAtIndex:i];  // remove duplicate
    } else {
        [hash setValue:[[NSNumber alloc] initWithInt:i] forKey:main[i][@"username"]];  // mark existance, with location
    }       
}

Upvotes: 1

Related Questions