Czy istnieje sposób na iterację słownika?

200

Wiem NSDictionariesjako coś, czego potrzebujesz key, aby uzyskać value. Ale w jaki sposób można iteracyjne nad wszystkim keysi valuesw sposób NSDictionary, tak, że wiem, co klucze istnieją, i jakie wartości są? Wiem, że jest coś o nazwie dla w pętli w JavaScript. Czy jest coś podobnego Objective-C?

Alex Cio
źródło
Dziękuję za ten post. Jeśli iterujesz w Swiftskładni, zapoznaj się z tym postem: stackoverflow.com/a/24111700/419348
AechoLiu

Odpowiedzi:

323

Tak, NSDictionaryobsługuje szybkie wyliczanie. Z Objective-C 2.0 możesz to zrobić:

// To print out all key-value pairs in the NSDictionary myDict
for(id key in myDict)
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);

Alternatywną metodą (której musisz użyć, jeśli celujesz w Mac OS X w wersji wcześniejszej niż 10.5, ale nadal możesz korzystać z wersji 10.5 i iPhone'a), to NSEnumerator:

NSEnumerator *enumerator = [myDict keyEnumerator];
id key;
// extra parens to suppress warning about using = instead of ==
while((key = [enumerator nextObject]))
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);
Adam Rosenfield
źródło
2
Nowoczesna składnia ObjC: NSLog (@ "klucz =% @ wartość =% @", klucz, myDict [klucz]);
geowar
@Darthenius ze względu na ostatnie optymalizacje, szybkie wyliczanie jest znowu szybsze niż oparte na blokach, przynajmniej w niektórych przypadkach. Ale jeśli rozwiązywany problem pozwala na użycie opcji równoczesnej, podejście oparte na blokach może być szybsze.
Zev Eisenberg,
@ZevEisenberg Zobacz koniec mojego postu.
Rok Strniša
Ups, kliknąłem powyższy link, aby otworzyć w nowej karcie, i nawet nie zauważyłem, kto go napisał lub czy jest na tej samej stronie. Jeśli nadal możesz edytować powyższy komentarz, możesz chcieć, aby leniwi czytelnicy nie wpadli na zły pomysł.
Zev Eisenberg,
153

Podejście blokowe pozwala uniknąć uruchamiania algorytmu wyszukiwania dla każdego klucza :

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
  NSLog(@"%@ => %@", key, value);
}];

Mimo że NSDictionaryjest implementowany jako tablica mieszająca (co oznacza, że ​​koszt wyszukiwania elementu wynosi O(1)), wyszukiwania wciąż spowalniają twoją iterację o stały czynnik .

Moje pomiary pokazują, że dla słownika dliczb ...

NSMutableDictionary* dict = [NSMutableDictionary dictionary];
for (int i = 0; i < 5000000; ++i) {
  NSNumber* value = @(i);
  dict[value.stringValue] = value;
}

... sumując liczby z podejściem blokowym ...

__block int sum = 0;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSNumber* value, BOOL* stop) {
  sum += value.intValue;
}];

... zamiast podejścia w pętli ...

int sum = 0;
for (NSString* key in dict)
  sum += [dict[key] intValue];

... jest około 40% szybszy .

EDYCJA : Nowy zestaw SDK (6.1+) wydaje się optymalizować iterację pętli, więc podejście do pętli jest teraz około 20% szybsze niż podejście do bloku , przynajmniej w powyższym prostym przypadku.

Rok Strniša
źródło
Co z iOS 10/11, który jest szybszy?
Supertecnoboff
elegancki, uwielbiam to!
YvesLeBorg
10

Oto iteracja przy użyciu podejścia blokowego:

    NSDictionary *dict = @{@"key1":@1, @"key2":@2, @"key3":@3};

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSLog(@"%@->%@",key,obj);
        // Set stop to YES when you wanted to break the iteration.
    }];

Dzięki autouzupełnianiu jest bardzo szybki do ustawienia i nie musisz się martwić pisaniem koperty iteracyjnej.

Javier Calatrava Llavería
źródło
Dzięki .. Dobre rozwiązanie, jeśli potrzebujesz zmutować NSMutableDictionaryproces
jose920405