Abhinav
Abhinav

Reputation: 38142

Grouping NSArray of NSDictionary based on a key in NSDictionay

I am trying to filter out a NSArray of NSDictionaries. With my below example, I want dict1, dict2 & dict4 grouped in one array, dict3 & dict5 grouped in second array and dict6 in third array.

I am getting this data in NSArray, so essentially the "orig" array below is my input and I know that I need to do grouping based on "Name" key.

Instead of looping through the NSArray I though of using valueForKeyPath to return me array based on the key path but this does not work (crashes with logs -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray').

Any suggestion.

NSDictionary *dict1 = @{@"Name" : @"T1", @"Age" : @"25"};
NSDictionary *dict2 = @{@"Name" : @"T1", @"Age" : @"25"};
NSDictionary *dict3 = @{@"Name" : @"T2", @"Age" : @"27"};
NSDictionary *dict4 = @{@"Name" : @"T1", @"Age" : @"25"};
NSDictionary *dict5 = @{@"Name" : @"T2", @"Age" : @"27"};
NSDictionary *dict6 = @{@"Name" : @"T3", @"Age" : @"28"};

NSArray *orig = @[dict1, dict2, dict3, dict4, dict5, dict6];

NSMutableArray *final = [NSMutableArray array];

final = [orig valueForKeyPath:@"@unionOfArrays.Name"];

NSLog(@"Final = %@", final);

Upvotes: 7

Views: 2959

Answers (2)

danh
danh

Reputation: 62686

If the desired output is an array of arrays, you can get there by building a dictionary keyed by the name attribute in the orig dictionaries:

- (NSArray *)collateByName:(NSArray *)original {

    NSMutableDictionary *collate  = [NSMutableDictionary dictionary];
    for (NSDictionary *d in original) {
        NSString *newKey = d[@"Name"];
        NSMutableArray *newValue = collate[newKey];
        if (!newValue) {
            newValue = [NSMutableArray array];
            collate[newKey] = newValue;
        }
        [newValue addObject:d];
    }
    return [collate allValues];
}

It's a little verbose, but clear, I think. If you want to decide the attribute to distinguish with programmatically, pass in another param called attribute and replace the literal @"Name" with it.

Upvotes: 3

Tom Harrington
Tom Harrington

Reputation: 70936

It's a little hard to tell if what you want is three different arrays where each one only contains entries with a specific Name value (as your first paragraph suggests) or if you want a single array where the entries are sorted by Name (as your second paragraph suggests). Regardless,

To sort orig by the value of the Name field:

NSArray *sortedByName = [orig sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"Name" ascending:YES]]];

To get a new array by selecting only entries with a specific value for Name:

NSArray *t1Only = [orig filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"Name = %@", @"T1"]];

Upvotes: 5

Related Questions