问题
We have an app that calls a SOAP web service and retrieves a long list of XML, which the app then parses into an NSArray
of NSDictionary
objects. The NSArray
contains a list of Rental Apartment information, each of which is stored into an NSDictionary
.
The entire list may contain 10 different types of Apartments (i.e. 2-room, 3-room), and we need to split the NSArray
into smaller NSArray
s based on Room-Type, which has the key "roomType" in the NSDictionary
objects.
Currently our algorithm is
- Use
[NSArray valueForKeyPath:@"@distinctUnionofObjects.room-type"]
to obtain a list of unique room-type values. - Loop through the list of unique room-type values
- For each unique room-type value, use
NSPredicate
to retrieve matching items from the Original list
Our code is below (renamed for clarity):
NSArray *arrOriginal = ... ...; // Contains the Parsed XML list
NSMutableArray *marrApartmentsByRoomType = [NSMutableArray arrayWithCapacity:10];
NSMutableArray *arrRoomTypes = [arrOriginal valueForKeyPath:@"distinctUnionOfObjects.roomType"];
for(NSString *strRoomType in arrRoomTypes) {
NSPredicate *predicateRoomType = [NSPredicate predicateWithFormat:@"roomType=%@", strRoomType];
NSArray *arrApartmentsThatMatchRoomType = [arrOriginal filteredArrayUsingPredicate:predicateRoomType]; // TAKES A LONG TIME EACH LOOP-ROUND
[marrApartmentsByRoomType addObject:arrApartmentsThatMatchRoomType];
}
However, step 3 is taking a long time as the original list may contain large amount (>100,000) of items. It seems that NSPredicate
goes through the entire list for each key value. Is there a more efficient way of splitting a large NSArray
into smaller NSArray
s, based on NSDictionary
keys?
回答1:
If the order of your splited Arrays is not important, i have a solution for you:
NSArray *arrOriginal;
NSMutableDictionary *grouped = [[NSMutableDictionary alloc] initWithCapacity:arrOriginal.count];
for (NSDictionary *dict in arrOriginal) {
id key = [dict valueForKey:@"roomType"];
NSMutableArray *tmp = [grouped objectForKey:key];
if (tmp == nil) {
tmp = [[NSMutableArray alloc] init];
[grouped setObject:tmp forKey:key];
}
[tmp addObject:dict];
}
NSMutableArray *marrApartmentsByRoomType = [grouped allValues];
回答2:
This is quite performant
- (NSDictionary *)groupObjectsInArray:(NSArray *)array byKey:(id <NSCopying> (^)(id item))keyForItemBlock
{
NSMutableDictionary *groupedItems = [NSMutableDictionary new];
for (id item in array) {
id <NSCopying> key = keyForItemBlock(item);
NSParameterAssert(key);
NSMutableArray *arrayForKey = groupedItems[key];
if (arrayForKey == nil) {
arrayForKey = [NSMutableArray new];
groupedItems[key] = arrayForKey;
}
[arrayForKey addObject:item];
}
return groupedItems;
}
回答3:
Improving @Jonathan answer
- Converting array to dictionary
Maintaining the same order as it was in original array
//only to a take unique keys. (key order should be maintained) NSMutableArray *aMutableArray = [[NSMutableArray alloc]init]; NSMutableDictionary *dictFromArray = [NSMutableDictionary dictionary]; for (NSDictionary *eachDict in arrOriginal) { //Collecting all unique key in order of initial array NSString *eachKey = [eachDict objectForKey:@"roomType"]; if (![aMutableArray containsObject:eachKey]) { [aMutableArray addObject:eachKey]; } NSMutableArray *tmp = [grouped objectForKey:key]; tmp = [dictFromArray objectForKey:eachKey]; if (!tmp) { tmp = [NSMutableArray array]; [dictFromArray setObject:tmp forKey:eachKey]; } [tmp addObject:eachDict]; } //NSLog(@"dictFromArray %@",dictFromArray); //NSLog(@"Unique Keys :: %@",aMutableArray);
//Converting from dictionary to array again...
self.finalArray = [[NSMutableArray alloc]init]; for (NSString *uniqueKey in aMutableArray) { NSDictionary *aUniqueKeyDict = @{@"groupKey":uniqueKey,@"featureValues":[dictFromArray objectForKey:uniqueKey]}; [self.finalArray addObject:aUniqueKeyDict]; }
Hope, It will help when client wants final array in same order as input array.
来源:https://stackoverflow.com/questions/14335466/split-nsarray-into-sub-arrays-based-on-nsdictionary-key-values