NSDictionary com chaves ordenadas

Estou curioso para saber se esta é uma situação em que qualquer outra pessoa se encontrou. Eu tenho um NSDictionary (armazenado em um plist) que estou usando basicamente como uma matriz associativa (strings como chaves e valores). Eu quero usar a matriz de chaves como parte do meu aplicativo, mas eu gostaria que eles estivessem em uma ordem específica (não realmente uma ordem que eu possa escrever um algoritmo para classificá-los). Eu sempre poderia armazenar uma matriz separada das chaves, mas isso parece meio chato, porque eu sempre teria que atualizar as chaves do dictionary, assim como os valores da matriz, e ter certeza de que elas sempre correspondam. Atualmente eu uso apenas [myDictionary allKeys], mas obviamente isso os retorna em uma ordem arbitrária e não garantida. Existe uma estrutura de dados no Objective-C que estou perdendo? Alguém tem alguma sugestão sobre como fazer isso com mais elegância?

A solução de ter um NSMutableArray de chaves associado não é tão ruim. Evita subclassing NSDictionary, e se você for cuidadoso ao escrever accessres, não deve ser muito difícil manter-se sincronizado.

Estou atrasado para o jogo com uma resposta real, mas você pode estar interessado em investigar o CHOrderedDictionary . É uma subclass de NSMutableDictionary que encapsula outra estrutura para manter a ordenação de chaves. (É parte do CHDataStructures.framework .) Eu acho que é mais conveniente do que gerenciar um dictionary e matriz separadamente.

Divulgação: Este é o código-fonte aberto que eu escrevi. Apenas esperando que possa ser útil para os outros que enfrentam este problema.

Não existe esse método inerente do qual você pode adquirir isso. Mas uma lógica simples funciona para você. Você pode simplesmente adicionar um texto numérico na frente de cada tecla enquanto prepara o dictionary. Gostar

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys: @"01.Created",@"cre", @"02.Being Assigned",@"bea", @"03.Rejected",@"rej", @"04.Assigned",@"ass", @"05.Scheduled",@"sch", @"06.En Route",@"inr", @"07.On Job Site",@"ojs", @"08.In Progress",@"inp", @"09.On Hold",@"onh", @"10.Completed",@"com", @"11.Closed",@"clo", @"12.Cancelled", @"can", nil]; 

Agora, se você pode usar sortingArrayUsingSelector ao obter todas as chaves na mesma ordem em que você colocou.

 NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)]; 

No local onde você deseja exibir as chaves no UIView, basta cortar o caractere da frente 3.

Se você está indo para subclass NSDictionary você precisa implementar esses methods como um mínimo:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying / NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (para o Leopard)
    • -countByEnumeratingWithState:objects:count:

A maneira mais fácil de fazer o que você deseja é criar uma subclass de NSMutableDictionary que contenha seu próprio NSMutableDictionary que manipula e um NSMutableArray para armazenar um conjunto ordenado de chaves.

Se você nunca vai codificar seus objects, você poderia pular implementando -encodeWithCoder: e -initWithCoder:

Todas as suas implementações de método nos 10 methods acima iriam então diretamente para o seu dictionary hospedado ou para o array de chaves ordenado.

Minha pequena adição: sorting por chave numérica (usando notações abreviadas para código menor)

 // the resorted result array NSMutableArray *result = [NSMutableArray new]; // the source dictionary - keys may be Ux timestamps (as integer, wrapped in NSNumber) NSDictionary *dict = @{ @0: @"a", @3: @"d", @1: @"b", @2: @"c" }; {// do the sorting to result NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)]; for (NSNumber *n in arr) [result addObject:dict[n]]; } 

Rápido e sujo

Quando você precisar encomendar seu dictionary (aqui chamado de “myDict”), faça o seguinte:

  NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil]; 

Então, quando você precisar pedir seu dictionary, crie um índice:

  NSEnumerator *sectEnum = [ordering objectEnumerator]; NSMutableArray *index = [[NSMutableArray alloc] init]; id sKey; while((sKey = [sectEnum nextObject])) { if ([myDict objectForKey:sKey] != nil ) { [index addObject:sKey]; } } 

Agora, o object * index conterá as chaves apropriadas na ordem correta. Note que esta solução não requer que todas as chaves existam necessariamente, que é a situação usual com a qual estamos lidando …

Para, Swift 3 . Por favor, tente a seguinte abordagem

  //Sample Dictionary let dict: [String: String] = ["01.One": "One", "02.Two": "Two", "03.Three": "Three", "04.Four": "Four", "05.Five": "Five", "06.Six": "Six", "07.Seven": "Seven", "08.Eight": "Eight", "09.Nine": "Nine", "10.Ten": "Ten" ] //Print the all keys of dictionary print(dict.keys) //Sort the dictionary keys array in ascending order let sortedKeys = dict.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending } //Print the ordered dictionary keys print(sortedKeys) //Get the first ordered key var firstSortedKeyOfDictionary = sortedKeys[0] // Get range of all characters past the first 3. let c = firstSortedKeyOfDictionary.characters let range = c.index(c.startIndex, offsetBy: 3).. 

Implementação mínima de uma subclass ordenada de NSDictionary (baseada em https://github.com/nicklockwood/OrderedDictionary ). Sinta-se à vontade para estender suas necessidades:

Swift 3 e 4

 class MutableOrderedDictionary: NSDictionary { let _values: NSMutableArray = [] let _keys: NSMutableOrderedSet = [] override var count: Int { return _keys.count } override func keyEnumerator() -> NSEnumerator { return _keys.objectEnumerator() } override func object(forKey aKey: Any) -> Any? { let index = _keys.index(of: aKey) if index != NSNotFound { return _values[index] } return nil } func setObject(_ anObject: Any, forKey aKey: String) { let index = _keys.index(of: aKey) if index != NSNotFound { _values[index] = anObject } else { _keys.add(aKey) _values.add(anObject) } } } 

uso

 let normalDic = ["hello": "world", "foo": "bar"] // initializing empty ordered dictionary let orderedDic = MutableOrderedDictionary() // copying normalDic in orderedDic after a sort normalDic.sorted { $0.0.compare($1.0) == .orderedAscending } .forEach { orderedDic.setObject($0.value, forKey: $0.key) } // from now, looping on orderedDic will be done in the alphabetical order of the keys orderedDic.forEach { print($0) } 

Objetivo-C

 @interface MutableOrderedDictionary<__covariant KeyType, __covariant ObjectType> : NSDictionary @end @implementation MutableOrderedDictionary { @protected NSMutableArray *_values; NSMutableOrderedSet *_keys; } - (instancetype)init { if ((self = [super init])) { _values = NSMutableArray.new; _keys = NSMutableOrderedSet.new; } return self; } - (NSUInteger)count { return _keys.count; } - (NSEnumerator *)keyEnumerator { return _keys.objectEnumerator; } - (id)objectForKey:(id)key { NSUInteger index = [_keys indexOfObject:key]; if (index != NSNotFound) { return _values[index]; } return nil; } - (void)setObject:(id)object forKey:(id)key { NSUInteger index = [_keys indexOfObject:key]; if (index != NSNotFound) { _values[index] = object; } else { [_keys addObject:key]; [_values addObject:object]; } } @end 

uso

 NSDictionary *normalDic = @{@"hello": @"world", @"foo": @"bar"}; // initializing empty ordered dictionary MutableOrderedDictionary *orderedDic = MutableOrderedDictionary.new; // copying normalDic in orderedDic after a sort for (id key in [normalDic.allKeys sortedArrayUsingSelector:@selector(compare:)]) { [orderedDic setObject:normalDic[key] forKey:key]; } // from now, looping on orderedDic will be done in the alphabetical order of the keys for (id key in orderedDic) { NSLog(@"%@:%@", key, orderedDic[key]); } 

Eu não gosto muito de C ++, mas uma solução que eu me vejo usando mais e mais é usar Objective-C ++ e std::map da Standard Template Library. É um dictionary cujas chaves são classificadas automaticamente na inserção. Ele funciona surpreendentemente bem com tipos escalares ou objects Objective-C, tanto como chaves quanto como valores.

Se você precisar include uma matriz como um valor, apenas use std::vector vez de NSArray .

Uma ressalva é que você pode querer fornecer sua própria function insert_or_assign , a menos que você possa usar o C ++ 17 (veja esta resposta ). Além disso, você precisa digitar seus tipos para evitar certos erros de compilation. Depois de descobrir como usar std::map , iteradores etc., é bastante simples e rápido.