NSDiccionario con claves ordenadas

Tengo curiosidad si esta es una situación en la que otras personas se han encontrado. Tengo un NSDictionary (almacenado en un plist) que básicamente estoy usando como una matriz asociativa (cadenas como claves y valores). Quiero utilizar el conjunto de claves como parte de mi aplicación, pero me gustaría que estén en un orden específico (no es realmente un orden en el que pueda escribir un algoritmo para ordenarlas). Siempre pude almacenar una matriz separada de las teclas, pero parece una especie de tonto porque siempre tendría que actualizar las claves del diccionario, así como los valores de la matriz, y asegurarse de que siempre se correspondan. Actualmente solo uso [myDictionary allKeys], pero obviamente esto los devuelve en un orden arbitrario y no garantizado. ¿Hay una estructura de datos en Objective-C que me falta? ¿Alguien tiene alguna sugerencia sobre cómo hacer esto de manera más elegante?

La solución de tener un NSMutableArray asociado de claves no es tan malo. Evita la subclasificación de NSDictionary, y si tiene cuidado al escribir accesos, no debería ser demasiado difícil mantenerla sincronizada.

Llego tarde al juego con una respuesta real, pero podría interesarte investigar CHOrderedDictionary . Es una subclase de NSMutableDictionary que encapsula otra estructura para mantener el orden de las teclas. (Es parte de CHDataStructures.framework ). Encuentro que es más conveniente que administrar un diccionario y una matriz por separado.

Divulgación: este es el código de fuente abierta que escribí. Solo espero que pueda ser útil para otros que enfrentan este problema.

No existe un método incorporado del que pueda adquirir esto. Pero una simple lógica funciona para ti. Simplemente puede agregar algunos textos numéricos al frente de cada tecla mientras prepara el diccionario. Me gusta

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

Ahora, si puede usar sortingArrayUsingSelector mientras obtiene todas las claves en el mismo orden en el que las coloca.

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

En el lugar donde desea mostrar las teclas en UIView, simplemente corte los 3 caracteres frontales.

Si va a subclase NSDictionary, debe implementar estos métodos como mínimo:

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

La forma más fácil de hacer lo que desea es crear una subclase de NSMutableDictionary que contenga su propio NSMutableDictionary que manipule y un NSMutableArray para almacenar un conjunto ordenado de claves.

Si nunca va a codificar sus objetos, podría concebir omitir la implementación de -encodeWithCoder: y -initWithCoder:

Todas las implementaciones de sus métodos en los 10 métodos anteriores pasarían directamente a través de su diccionario alojado o su matriz de claves ordenadas.

Mi pequeña adición: ordenando con la tecla numérica (usando notaciones abreviadas para códigos más pequeños)

 // 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 y sucio

Cuando necesite ordenar su diccionario (en este documento llamado “myDict”), haga esto:

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

Luego, cuando necesite ordenar su diccionario, cree un índice:

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

Ahora, el objeto de índice * contendrá las claves apropiadas en el orden correcto. Tenga en cuenta que esta solución no requiere que todas las claves existan necesariamente, que es la situación habitual con la que nos enfrentamos …

Para, Swift 3 . Pruebe el siguiente enfoque

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

Implementación mínima de una subclase ordenada de NSDictionary (basada en https://github.com/nicklockwood/OrderedDictionary ). Siéntase libre de extender para sus necesidades:

Swift 3 y 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) } 

C objective

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

No me gusta mucho C ++, pero una solución que me veo usando cada vez más es usar Objective-C ++ y std::map de la Biblioteca de plantillas estándar. Es un diccionario cuyas claves se clasifican automáticamente en la inserción. Funciona sorprendentemente bien con los tipos escalares u objetos Objective-C como claves y como valores.

Si necesita incluir una matriz como valor, solo use std::vector lugar de NSArray .

Una advertencia es que es posible que desee proporcionar su propia función insert_or_assign , a menos que pueda usar C ++ 17 (vea esta respuesta ). Además, debe typedef los tipos para evitar ciertos errores de comstackción. Una vez que descubra cómo usar std::map , iterators, etc., es bastante sencillo y rápido.