NSDictionaryキー値に基づいてNSArrayをサブ配列に分割します

Split NSArray into sub-arrays based on NSDictionary key values


質問 written by Carl Veazey @2013-01-15 10:26:47Z

: 4 : 3 : 4

SOAP Webサービスを呼び出し、XMLの長いリストを取得するアプリがあります。アプリはその後、 NSDictionaryオブジェクトのNSArrayに解析します。 NSArrayは、賃貸アパート情報のリストが含まれており、各情報はNSDictionary保存されていNSDictionary

リスト全体には10種類のアパートメント(つまり、2ルーム、3ルーム)が含まれる可能性があり、 NSArrayを、 NSDictionaryオブジェクトにキー「roomType」を持つRoom-Typeに基づいて小さいNSArrayに分割する必要があります。

現在、私たちのアルゴリズムは

  1. [NSArray valueForKeyPath:@"@distinctUnionofObjects.room-type"]を使用して、一意の部屋タイプ値のリストを取得します。
  2. 一意の部屋タイプ値のリストをループします
  3. 一意の部屋タイプ値ごとに、 NSPredicateを使用して、元のリストから一致するアイテムを取得します

コードを以下に示します(わかりやすくするために名前を変更しました)

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];
}

ただし、元のリストに大量(> 100,000)のアイテムが含まれている可能性があるため、ステップ3には時間がかかります。 NSPredicateは各キー値のリスト全体をNSPredicateているようです。 NSDictionaryキーに基づいて、大きなNSArrayを小さなNSArrayに分割するより効率的な方法はありますか?

回答 1 written by Jonathan Cichon @2013-01-15 10:38:24Z
3

分割された配列の順序が重要でない場合は、解決策があります:

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];
コメント 1

ジョナサンの素早い返信をありがとう!これを試して、これを使用してパフォーマンスが向上するかどうかをテストします。少なくとも、元の配列全体を一度ループする必要があるようです

written by ...-AndyV @2013-01-15 10:38:19Z

回答 2 written by hfossli @2016-01-28 15:14:12Z
1

これは非常に高性能です

- (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 written by umakanta @2016-07-20 04:43:41Z
0

@Jonathan回答の改善

  1. 配列を辞書に変換する
  2. 元の配列と同じ順序を維持する

     //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); 

    // 辞書から配列に再度変換します...

     self.finalArray = [[NSMutableArray alloc]init]; for (NSString *uniqueKey in aMutableArray) { NSDictionary *aUniqueKeyDict = @{@"groupKey":uniqueKey,@"featureValues":[dictFromArray objectForKey:uniqueKey]}; [self.finalArray addObject:aUniqueKeyDict]; } 

クライアントが入力配列と同じ順序で最終配列を必要とする場合に役立ちます。